diff --git a/backend/src/api/add.rs b/backend/src/api/add.rs
deleted file mode 100644
index ec0f868..0000000
--- a/backend/src/api/add.rs
+++ /dev/null
@@ -1,95 +0,0 @@
-use crate::aur::aur::get_info_by_name;
-use crate::builder::types::Action;
-use crate::db::prelude::{Packages, Versions};
-use crate::db::{packages, versions};
-use rocket::response::status::BadRequest;
-use rocket::serde::json::Json;
-use rocket::serde::Deserialize;
-use rocket::{post, State};
-use rocket_okapi::okapi::schemars;
-use rocket_okapi::{openapi, JsonSchema};
-use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter};
-use sea_orm::{DatabaseConnection, Set};
-use tokio::sync::broadcast::Sender;
-
-#[derive(Deserialize, JsonSchema)]
-#[serde(crate = "rocket::serde")]
-pub struct AddBody {
- name: String,
- force_build: bool,
-}
-
-#[openapi(tag = "test")]
-#[post("/packages/add", data = "")]
-pub async fn package_add(
- db: &State,
- input: Json,
- tx: &State>,
-) -> Result<(), BadRequest> {
- let db = db as &DatabaseConnection;
-
- // remove leading and trailing whitespaces
- let pkg_name = input.name.trim();
-
- let pkg = get_info_by_name(pkg_name)
- .await
- .map_err(|_| BadRequest(Some("couldn't download package metadata".to_string())))?;
-
- let mut pkg_model = match Packages::find()
- .filter(packages::Column::Name.eq(pkg_name))
- .one(db)
- .await
- .map_err(|e| BadRequest(Some(e.to_string())))?
- {
- None => {
- let new_package = packages::ActiveModel {
- name: Set(pkg_name.to_string()),
- status: Set(3),
- latest_aur_version: Set(pkg.version.clone()),
- ..Default::default()
- };
-
- new_package.save(db).await.expect("TODO: panic message")
- }
- Some(p) => p.into(),
- };
-
- let version_model = match Versions::find()
- .filter(versions::Column::Version.eq(pkg.version.clone()))
- .one(db)
- .await
- .map_err(|e| BadRequest(Some(e.to_string())))?
- {
- None => {
- let new_version = versions::ActiveModel {
- version: Set(pkg.version.clone()),
- package_id: Set(pkg_model.id.clone().unwrap()),
- ..Default::default()
- };
-
- new_version.save(db).await.expect("TODO: panic message")
- }
- Some(p) => {
- // todo add check if this version was successfully built
- // if not allow build
- if input.force_build {
- p.into()
- } else {
- return Err(BadRequest(Some("Version already existing".to_string())));
- }
- }
- };
-
- pkg_model.status = Set(3);
- pkg_model.latest_version_id = Set(Some(version_model.id.clone().unwrap()));
- pkg_model.save(db).await.expect("todo error message");
-
- let _ = tx.send(Action::Build(
- pkg.name,
- pkg.version,
- pkg.url_path.unwrap(),
- version_model,
- ));
-
- Ok(())
-}
diff --git a/backend/src/api/backend.rs b/backend/src/api/backend.rs
index c568d9f..cedb98d 100644
--- a/backend/src/api/backend.rs
+++ b/backend/src/api/backend.rs
@@ -1,16 +1,20 @@
-use crate::api::add::okapi_add_operation_for_package_add_;
-use crate::api::add::package_add;
+use crate::api::list::build_output;
use crate::api::list::okapi_add_operation_for_get_build_;
-use crate::api::list::okapi_add_operation_for_get_package_;
use crate::api::list::okapi_add_operation_for_stats_;
-use crate::api::list::{build_output, okapi_add_operation_for_package_list_};
-use crate::api::list::{get_build, get_package, okapi_add_operation_for_list_builds_};
+use crate::api::list::search;
+use crate::api::list::{get_build, okapi_add_operation_for_list_builds_};
use crate::api::list::{list_builds, okapi_add_operation_for_search_};
use crate::api::list::{okapi_add_operation_for_build_output_, stats};
-use crate::api::list::{package_list, search};
-use crate::api::remove::okapi_add_operation_for_package_del_;
+use crate::api::package::okapi_add_operation_for_get_package_;
+use crate::api::package::okapi_add_operation_for_package_del_;
+use crate::api::package::okapi_add_operation_for_package_list_;
+use crate::api::package::okapi_add_operation_for_package_update_;
+use crate::api::package::package_add;
+use crate::api::package::{
+ get_package, okapi_add_operation_for_package_add_, package_del, package_list, package_update,
+};
use crate::api::remove::okapi_add_operation_for_version_del_;
-use crate::api::remove::{package_del, version_del};
+use crate::api::remove::version_del;
use rocket::Route;
use rocket_okapi::openapi_get_routes;
@@ -25,6 +29,7 @@ pub fn build_api() -> Vec {
list_builds,
stats,
get_build,
- get_package
+ get_package,
+ package_update
]
}
diff --git a/backend/src/api/list.rs b/backend/src/api/list.rs
index dfd95c5..68a6e29 100644
--- a/backend/src/api/list.rs
+++ b/backend/src/api/list.rs
@@ -50,62 +50,6 @@ pub struct ListPackageModel {
latest_aur_version: String,
}
-#[openapi(tag = "test")]
-#[get("/packages/list?")]
-pub async fn package_list(
- db: &State,
- limit: Option,
-) -> Result>, NotFound> {
- let db = db as &DatabaseConnection;
-
- let all: Vec = Packages::find()
- .join_rev(JoinType::LeftJoin, versions::Relation::LatestPackage.def())
- .select_only()
- .column(packages::Column::Name)
- .column(packages::Column::Id)
- .column(packages::Column::Status)
- .column_as(packages::Column::OutOfDate, "outofdate")
- .column_as(packages::Column::LatestAurVersion, "latest_aur_version")
- .column_as(versions::Column::Version, "latest_version")
- .column_as(packages::Column::LatestVersionId, "latest_version_id")
- .order_by(packages::Column::Id, Order::Desc)
- .limit(limit)
- .into_model::()
- .all(db)
- .await
- .map_err(|e| NotFound(e.to_string()))?;
-
- Ok(Json(all))
-}
-
-#[openapi(tag = "test")]
-#[get("/package/")]
-pub async fn get_package(
- db: &State,
- id: u64,
-) -> Result, NotFound> {
- let db = db as &DatabaseConnection;
-
- let all: ListPackageModel = Packages::find()
- .join_rev(JoinType::LeftJoin, versions::Relation::LatestPackage.def())
- .filter(packages::Column::Id.eq(id))
- .select_only()
- .column(packages::Column::Name)
- .column(packages::Column::Id)
- .column(packages::Column::Status)
- .column_as(packages::Column::OutOfDate, "outofdate")
- .column_as(packages::Column::LatestAurVersion, "latest_aur_version")
- .column_as(versions::Column::Version, "latest_version")
- .column_as(packages::Column::LatestVersionId, "latest_version_id")
- .into_model::()
- .one(db)
- .await
- .map_err(|e| NotFound(e.to_string()))?
- .ok_or(NotFound("id not found".to_string()))?;
-
- Ok(Json(all))
-}
-
#[openapi(tag = "test")]
#[get("/builds/output?&")]
pub async fn build_output(
diff --git a/backend/src/api/mod.rs b/backend/src/api/mod.rs
index 1de5bb2..1721aac 100644
--- a/backend/src/api/mod.rs
+++ b/backend/src/api/mod.rs
@@ -1,6 +1,6 @@
-mod add;
pub mod backend;
#[cfg(feature = "static")]
pub mod embed;
mod list;
+mod package;
mod remove;
diff --git a/backend/src/api/package.rs b/backend/src/api/package.rs
new file mode 100644
index 0000000..ed2f9c1
--- /dev/null
+++ b/backend/src/api/package.rs
@@ -0,0 +1,225 @@
+use crate::api::list::ListPackageModel;
+use crate::aur::aur::get_info_by_name;
+use crate::builder::types::Action;
+use crate::db::migration::{JoinType, Order};
+use crate::db::prelude::{Packages, Versions};
+use crate::db::{packages, versions};
+use crate::repo::repo::remove_pkg;
+use rocket::response::status::{BadRequest, NotFound};
+use rocket::serde::json::Json;
+use rocket::serde::Deserialize;
+use rocket::{get, post, State};
+use rocket_okapi::okapi::schemars;
+use rocket_okapi::{openapi, JsonSchema};
+use sea_orm::{
+ ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, QueryOrder, QuerySelect, RelationTrait,
+};
+use sea_orm::{DatabaseConnection, Set};
+use tokio::sync::broadcast::Sender;
+
+#[derive(Deserialize, JsonSchema)]
+#[serde(crate = "rocket::serde")]
+pub struct AddBody {
+ name: String,
+}
+
+#[openapi(tag = "Packages")]
+#[post("/packages/add", data = "")]
+pub async fn package_add(
+ db: &State,
+ input: Json,
+ tx: &State>,
+) -> Result<(), BadRequest> {
+ let db = db as &DatabaseConnection;
+
+ // remove leading and trailing whitespaces
+ let pkg_name = input.name.trim();
+
+ let pkg = get_info_by_name(pkg_name)
+ .await
+ .map_err(|_| BadRequest(Some("couldn't download package metadata".to_string())))?;
+
+ if let None = Packages::find()
+ .filter(packages::Column::Name.eq(pkg_name))
+ .one(db)
+ .await
+ .map_err(|e| BadRequest(Some(e.to_string())))?
+ {
+ return Err(BadRequest(Some("Package already exists".to_string())));
+ }
+
+ let mut new_package = packages::ActiveModel {
+ name: Set(pkg_name.to_string()),
+ status: Set(3),
+ latest_aur_version: Set(pkg.version.clone()),
+ ..Default::default()
+ };
+
+ new_package
+ .clone()
+ .save(db)
+ .await
+ .map_err(|e| BadRequest(Some(e.to_string())))?;
+
+ let new_version = versions::ActiveModel {
+ version: Set(pkg.version.clone()),
+ package_id: Set(new_package.id.clone().unwrap()),
+ ..Default::default()
+ };
+
+ new_version
+ .clone()
+ .save(db)
+ .await
+ .map_err(|e| BadRequest(Some(e.to_string())))?;
+
+ new_package.status = Set(3);
+ new_package.latest_version_id = Set(Some(new_version.id.clone().unwrap()));
+ new_package
+ .save(db)
+ .await
+ .map_err(|e| BadRequest(Some(e.to_string())))?;
+
+ let _ = tx.send(Action::Build(
+ pkg.name,
+ pkg.version,
+ pkg.url_path.unwrap(),
+ new_version,
+ ));
+
+ Ok(())
+}
+
+#[derive(Deserialize, JsonSchema)]
+#[serde(crate = "rocket::serde")]
+pub struct UpdateBody {
+ force: bool,
+}
+
+#[openapi(tag = "Packages")]
+#[post("/packages//update", data = "")]
+pub async fn package_update(
+ db: &State,
+ id: i32,
+ input: Json,
+ tx: &State>,
+) -> Result<(), BadRequest> {
+ let db = db as &DatabaseConnection;
+
+ let mut pkg_model: packages::ActiveModel = Packages::find_by_id(id)
+ .one(db)
+ .await
+ .map_err(|e| BadRequest(Some(e.to_string())))?
+ .ok_or(BadRequest(Some("id not found".to_string())))?
+ .into();
+
+ let pkg = get_info_by_name(pkg_model.name.clone().unwrap().as_str())
+ .await
+ .map_err(|_| BadRequest(Some("couldn't download package metadata".to_string())))?;
+
+ let version_model = match Versions::find()
+ .filter(versions::Column::Version.eq(pkg.version.clone()))
+ .filter(versions::Column::PackageId.eq(pkg.id.clone()))
+ .one(db)
+ .await
+ .map_err(|e| BadRequest(Some(e.to_string())))?
+ {
+ None => {
+ let new_version = versions::ActiveModel {
+ version: Set(pkg.version.clone()),
+ package_id: Set(pkg_model.id.clone().unwrap()),
+ ..Default::default()
+ };
+
+ new_version.save(db).await.expect("TODO: panic message")
+ }
+ Some(p) => {
+ // todo add check if this version was successfully built
+ // if not allow build
+ if input.force {
+ p.into()
+ } else {
+ return Err(BadRequest(Some("Version already existing".to_string())));
+ }
+ }
+ };
+
+ pkg_model.status = Set(3);
+ pkg_model.latest_version_id = Set(Some(version_model.id.clone().unwrap()));
+ pkg_model.save(db).await.expect("todo error message");
+
+ let _ = tx.send(Action::Build(
+ pkg.name,
+ pkg.version,
+ pkg.url_path.unwrap(),
+ version_model,
+ ));
+
+ Ok(())
+}
+
+#[openapi(tag = "Packages")]
+#[post("/package/delete/")]
+pub async fn package_del(db: &State, id: i32) -> Result<(), String> {
+ let db = db as &DatabaseConnection;
+
+ remove_pkg(db, id).await.map_err(|e| e.to_string())?;
+
+ Ok(())
+}
+
+#[openapi(tag = "Packages")]
+#[get("/packages/list?")]
+pub async fn package_list(
+ db: &State,
+ limit: Option,
+) -> Result>, NotFound> {
+ let db = db as &DatabaseConnection;
+
+ let all: Vec = Packages::find()
+ .join_rev(JoinType::LeftJoin, versions::Relation::LatestPackage.def())
+ .select_only()
+ .column(packages::Column::Name)
+ .column(packages::Column::Id)
+ .column(packages::Column::Status)
+ .column_as(packages::Column::OutOfDate, "outofdate")
+ .column_as(packages::Column::LatestAurVersion, "latest_aur_version")
+ .column_as(versions::Column::Version, "latest_version")
+ .column_as(packages::Column::LatestVersionId, "latest_version_id")
+ .order_by(packages::Column::Id, Order::Desc)
+ .limit(limit)
+ .into_model::()
+ .all(db)
+ .await
+ .map_err(|e| NotFound(e.to_string()))?;
+
+ Ok(Json(all))
+}
+
+#[openapi(tag = "Packages")]
+#[get("/package/")]
+pub async fn get_package(
+ db: &State,
+ id: u64,
+) -> Result, NotFound> {
+ let db = db as &DatabaseConnection;
+
+ let all: ListPackageModel = Packages::find()
+ .join_rev(JoinType::LeftJoin, versions::Relation::LatestPackage.def())
+ .filter(packages::Column::Id.eq(id))
+ .select_only()
+ .column(packages::Column::Name)
+ .column(packages::Column::Id)
+ .column(packages::Column::Status)
+ .column_as(packages::Column::OutOfDate, "outofdate")
+ .column_as(packages::Column::LatestAurVersion, "latest_aur_version")
+ .column_as(versions::Column::Version, "latest_version")
+ .column_as(packages::Column::LatestVersionId, "latest_version_id")
+ .into_model::()
+ .one(db)
+ .await
+ .map_err(|e| NotFound(e.to_string()))?
+ .ok_or(NotFound("id not found".to_string()))?;
+
+ Ok(Json(all))
+}
diff --git a/backend/src/api/remove.rs b/backend/src/api/remove.rs
index 96344e2..911449d 100644
--- a/backend/src/api/remove.rs
+++ b/backend/src/api/remove.rs
@@ -3,16 +3,6 @@ use rocket::{post, State};
use rocket_okapi::openapi;
use sea_orm::DatabaseConnection;
-#[openapi(tag = "test")]
-#[post("/package/delete/")]
-pub async fn package_del(db: &State, id: i32) -> Result<(), String> {
- let db = db as &DatabaseConnection;
-
- remove_pkg(db, id).await.map_err(|e| e.to_string())?;
-
- Ok(())
-}
-
#[openapi(tag = "test")]
#[post("/versions/delete/")]
pub async fn version_del(db: &State, id: i32) -> Result<(), String> {
diff --git a/backend/src/repo/repo.rs b/backend/src/repo/repo.rs
index fcb773a..8805857 100644
--- a/backend/src/repo/repo.rs
+++ b/backend/src/repo/repo.rs
@@ -4,7 +4,10 @@ use crate::db::prelude::{Builds, Packages};
use crate::db::{builds, versions};
use crate::pkgbuild::build::build_pkgbuild;
use anyhow::anyhow;
-use sea_orm::{ColumnTrait, DatabaseConnection, EntityTrait, ModelTrait, QueryFilter};
+use sea_orm::{
+ ColumnTrait, DatabaseConnection, DatabaseTransaction, EntityTrait, ModelTrait, QueryFilter,
+ TransactionTrait,
+};
use std::fs;
use std::process::Command;
use tokio::sync::broadcast::Sender;
@@ -46,46 +49,55 @@ pub async fn add_pkg(
}
pub async fn remove_pkg(db: &DatabaseConnection, pkg_id: i32) -> anyhow::Result<()> {
+ let txn = db.begin().await?;
+
let pkg = Packages::find_by_id(pkg_id)
- .one(db)
+ .one(&txn)
.await?
.ok_or(anyhow!("id not found"))?;
// remove build dir if available
let _ = fs::remove_dir_all(format!("./builds/{}", pkg.name));
+ // remove package db entry
+ pkg.clone().delete(&txn).await?;
+
let versions = Versions::find()
.filter(versions::Column::PackageId.eq(pkg.id))
- .all(db)
+ .all(&txn)
.await?;
for v in versions {
- rem_ver(db, v).await?;
+ rem_ver(&txn, v).await?;
}
// remove corresponding builds
let builds = Builds::find()
.filter(builds::Column::PkgId.eq(pkg.id))
- .all(db)
+ .all(&txn)
.await?;
for b in builds {
- b.delete(db).await?;
+ b.delete(&txn).await?;
}
- // remove package db entry
- pkg.delete(db).await?;
+ txn.commit().await?;
Ok(())
}
pub async fn remove_version(db: &DatabaseConnection, version_id: i32) -> anyhow::Result<()> {
+ let txn = db.begin().await?;
+
let version = Versions::find()
.filter(versions::Column::PackageId.eq(version_id))
- .one(db)
+ .one(&txn)
.await?;
if let Some(version) = version {
- rem_ver(db, version).await?;
+ rem_ver(&txn, version).await?;
}
+
+ txn.commit().await?;
+
Ok(())
}
@@ -129,7 +141,7 @@ fn repo_remove(pkg_file_name: String) -> anyhow::Result<()> {
Ok(())
}
-async fn rem_ver(db: &DatabaseConnection, version: versions::Model) -> anyhow::Result<()> {
+async fn rem_ver(db: &DatabaseTransaction, version: versions::Model) -> anyhow::Result<()> {
if let Some(filename) = version.file_name.clone() {
// so repo-remove only supports passing a package name and removing the whole package
// it seems that repo-add removes an older version when called
diff --git a/frontend/lib/api/packages.dart b/frontend/lib/api/packages.dart
index 5d9010d..a0cd7c0 100644
--- a/frontend/lib/api/packages.dart
+++ b/frontend/lib/api/packages.dart
@@ -19,9 +19,15 @@ extension PackagesAPI on ApiClient {
return package;
}
- Future addPackage({bool force = false, required String name}) async {
+ Future addPackage({required String name}) async {
+ final resp =
+ await getRawClient().post("/packages/add", data: {'name': name});
+ print(resp.data);
+ }
+
+ Future updatePackage({bool force = false, required int id}) async {
final resp = await getRawClient()
- .post("/packages/add", data: {'force_build': force, 'name': name});
+ .post("/packages/$id/update", data: {'force': force});
print(resp.data);
}
diff --git a/frontend/lib/components/packages_table.dart b/frontend/lib/components/packages_table.dart
index 29e13da..a901049 100644
--- a/frontend/lib/components/packages_table.dart
+++ b/frontend/lib/components/packages_table.dart
@@ -54,11 +54,19 @@ class PackagesTable extends StatelessWidget {
DataCell(IconButton(
icon: Icon(
package.outofdate ? Icons.update : Icons.verified,
- color: package.outofdate ? Color(0xFF6B43A4) : Color(0xFF0A6900),
+ color: package.outofdate
+ ? const Color(0xFF6B43A4)
+ : const Color(0xFF0A6900),
),
onPressed: package.outofdate
- ? () {
- // todo open build info with logs
+ ? () async {
+ await API.updatePackage(id: package.id);
+ Provider.of(context, listen: false)
+ .refresh(context);
+ Provider.of(context, listen: false)
+ .refresh(context);
+ Provider.of(context, listen: false)
+ .refresh(context);
}
: null,
)),
diff --git a/frontend/lib/screens/package_screen.dart b/frontend/lib/screens/package_screen.dart
index 83dd30f..eeb4790 100644
--- a/frontend/lib/screens/package_screen.dart
+++ b/frontend/lib/screens/package_screen.dart
@@ -56,18 +56,19 @@ class _PackageScreenState extends State {
style: const TextStyle(fontSize: 32),
),
),
- Container(
- margin: const EdgeInsets.only(right: 15),
- child: ElevatedButton(
- onPressed: () async {
- final confirmResult =
- await showConfirmationDialog(
- context,
- "Delete Package",
- "Are you sure to delete this Package?",
- () async {
- final succ = await API.deletePackage(pkg.id);
- if (succ) {
+ Row(
+ children: [
+ ElevatedButton(
+ onPressed: () async {
+ final confirmResult =
+ await showConfirmationDialog(
+ context,
+ "Force update Package",
+ "Are you sure to force an Package rebuild?",
+ () async {
+ await API.updatePackage(
+ force: true, id: pkg.id);
+
context.pop();
Provider.of(context,
@@ -79,16 +80,51 @@ class _PackageScreenState extends State {
Provider.of(context,
listen: false)
.refresh(context);
- }
- },
- () {},
- );
- },
- child: const Text(
- "Delete",
- style: TextStyle(color: Colors.redAccent),
+ },
+ () {},
+ );
+ },
+ child: const Text(
+ "Force Update",
+ style: TextStyle(color: Colors.yellowAccent),
+ ),
),
- ),
+ ElevatedButton(
+ onPressed: () async {
+ final confirmResult =
+ await showConfirmationDialog(
+ context,
+ "Delete Package",
+ "Are you sure to delete this Package?",
+ () async {
+ final succ =
+ await API.deletePackage(pkg.id);
+ if (succ) {
+ context.pop();
+
+ Provider.of(context,
+ listen: false)
+ .refresh(context);
+ Provider.of(context,
+ listen: false)
+ .refresh(context);
+ Provider.of(context,
+ listen: false)
+ .refresh(context);
+ }
+ },
+ () {},
+ );
+ },
+ child: const Text(
+ "Delete",
+ style: TextStyle(color: Colors.redAccent),
+ ),
+ ),
+ SizedBox(
+ width: 15,
+ )
+ ],
)
],
),