sync every hour the latest version of packages with aur
display current version and if outdated in ui display correct time in output log
This commit is contained in:
parent
eb4ca46562
commit
80e2299dc8
@ -27,9 +27,9 @@ FROM archlinux
|
|||||||
# Copy the built binary from the previous stage
|
# Copy the built binary from the previous stage
|
||||||
COPY --from=builder /app/target/release/untitled /usr/local/bin/untitled
|
COPY --from=builder /app/target/release/untitled /usr/local/bin/untitled
|
||||||
|
|
||||||
RUN echo " \
|
RUN echo $'\
|
||||||
[extra]\
|
[extra]\
|
||||||
Include = /etc/pacman.d/mirrorlist" >> /etc/pacman.conf
|
Include = /etc/pacman.d/mirrorlist' >> /etc/pacman.conf
|
||||||
|
|
||||||
RUN pacman -Syyu --noconfirm
|
RUN pacman -Syyu --noconfirm
|
||||||
RUN pacman-key --init && pacman-key --populate
|
RUN pacman-key --init && pacman-key --populate
|
||||||
|
@ -1188,8 +1188,8 @@ fi
|
|||||||
|
|
||||||
if (( ! INFAKEROOT )); then
|
if (( ! INFAKEROOT )); then
|
||||||
if (( EUID == 0 )); then
|
if (( EUID == 0 )); then
|
||||||
error "$(gettext "Running %s as root is not allowed as it can cause permanent,\n\
|
: #error "$(gettext "Running %s as root is not allowed as it can cause permanent,\n\
|
||||||
catastrophic damage to your system.")" "makepkg"
|
#catastrophic damage to your system.")" "makepkg"
|
||||||
#exit $E_ROOT
|
#exit $E_ROOT
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
|
@ -9,9 +9,9 @@ use rocket::serde::{Deserialize, Serialize};
|
|||||||
use rocket::{get, State};
|
use rocket::{get, State};
|
||||||
use rocket_okapi::okapi::schemars;
|
use rocket_okapi::okapi::schemars;
|
||||||
use rocket_okapi::{openapi, JsonSchema};
|
use rocket_okapi::{openapi, JsonSchema};
|
||||||
use sea_orm::PaginatorTrait;
|
|
||||||
use sea_orm::{ColumnTrait, QueryFilter};
|
use sea_orm::{ColumnTrait, QueryFilter};
|
||||||
use sea_orm::{DatabaseConnection, EntityTrait, FromQueryResult, QuerySelect, RelationTrait};
|
use sea_orm::{DatabaseConnection, EntityTrait, FromQueryResult, QuerySelect, RelationTrait};
|
||||||
|
use sea_orm::{Order, PaginatorTrait, QueryOrder};
|
||||||
|
|
||||||
#[derive(Serialize, JsonSchema)]
|
#[derive(Serialize, JsonSchema)]
|
||||||
#[serde(crate = "rocket::serde")]
|
#[serde(crate = "rocket::serde")]
|
||||||
@ -43,8 +43,11 @@ pub async fn search(query: &str) -> Result<Json<Vec<ApiPackage>>, String> {
|
|||||||
pub struct ListPackageModel {
|
pub struct ListPackageModel {
|
||||||
id: i32,
|
id: i32,
|
||||||
name: String,
|
name: String,
|
||||||
count: i32,
|
|
||||||
status: i32,
|
status: i32,
|
||||||
|
outofdate: bool,
|
||||||
|
latest_version: String,
|
||||||
|
latest_version_id: i32,
|
||||||
|
latest_aur_version: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[openapi(tag = "test")]
|
#[openapi(tag = "test")]
|
||||||
@ -55,13 +58,15 @@ pub async fn package_list(
|
|||||||
let db = db as &DatabaseConnection;
|
let db = db as &DatabaseConnection;
|
||||||
|
|
||||||
let all: Vec<ListPackageModel> = Packages::find()
|
let all: Vec<ListPackageModel> = Packages::find()
|
||||||
.join_rev(JoinType::InnerJoin, versions::Relation::Packages.def())
|
.join_rev(JoinType::InnerJoin, versions::Relation::LatestPackage.def())
|
||||||
.select_only()
|
.select_only()
|
||||||
.column_as(versions::Column::Id.count(), "count")
|
|
||||||
.column(packages::Column::Name)
|
.column(packages::Column::Name)
|
||||||
.column(packages::Column::Id)
|
.column(packages::Column::Id)
|
||||||
.column(packages::Column::Status)
|
.column(packages::Column::Status)
|
||||||
.group_by(packages::Column::Name)
|
.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::<ListPackageModel>()
|
.into_model::<ListPackageModel>()
|
||||||
.all(db)
|
.all(db)
|
||||||
.await
|
.await
|
||||||
@ -146,6 +151,8 @@ pub struct ListBuildsModel {
|
|||||||
pkg_name: String,
|
pkg_name: String,
|
||||||
version: String,
|
version: String,
|
||||||
status: i32,
|
status: i32,
|
||||||
|
start_time: Option<u32>,
|
||||||
|
end_time: Option<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[openapi(tag = "test")]
|
#[openapi(tag = "test")]
|
||||||
@ -165,6 +172,9 @@ pub async fn list_builds(
|
|||||||
.column(builds::Column::Status)
|
.column(builds::Column::Status)
|
||||||
.column_as(packages::Column::Name, "pkg_name")
|
.column_as(packages::Column::Name, "pkg_name")
|
||||||
.column(versions::Column::Version)
|
.column(versions::Column::Version)
|
||||||
|
.column(builds::Column::EndTime)
|
||||||
|
.column(builds::Column::StartTime)
|
||||||
|
.order_by(builds::Column::StartTime, Order::Desc)
|
||||||
.limit(limit);
|
.limit(limit);
|
||||||
|
|
||||||
let build = match pkgid {
|
let build = match pkgid {
|
||||||
@ -197,6 +207,8 @@ pub async fn get_build(
|
|||||||
.column(builds::Column::Status)
|
.column(builds::Column::Status)
|
||||||
.column_as(packages::Column::Name, "pkg_name")
|
.column_as(packages::Column::Name, "pkg_name")
|
||||||
.column(versions::Column::Version)
|
.column(versions::Column::Version)
|
||||||
|
.column(builds::Column::EndTime)
|
||||||
|
.column(builds::Column::StartTime)
|
||||||
.into_model::<ListBuildsModel>()
|
.into_model::<ListBuildsModel>()
|
||||||
.one(db)
|
.one(db)
|
||||||
.await
|
.await
|
||||||
|
@ -5,6 +5,7 @@ use crate::repo::repo::add_pkg;
|
|||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use sea_orm::{ActiveModelTrait, DatabaseConnection, EntityTrait, Set};
|
use sea_orm::{ActiveModelTrait, DatabaseConnection, EntityTrait, Set};
|
||||||
use std::ops::Add;
|
use std::ops::Add;
|
||||||
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
use tokio::sync::broadcast;
|
use tokio::sync::broadcast;
|
||||||
use tokio::sync::broadcast::error::RecvError;
|
use tokio::sync::broadcast::error::RecvError;
|
||||||
use tokio::sync::broadcast::Sender;
|
use tokio::sync::broadcast::Sender;
|
||||||
@ -16,12 +17,17 @@ pub async fn init(db: DatabaseConnection, tx: Sender<Action>) {
|
|||||||
// add a package to parallel build
|
// add a package to parallel build
|
||||||
Action::Build(name, version, url, mut version_model) => {
|
Action::Build(name, version, url, mut version_model) => {
|
||||||
let db = db.clone();
|
let db = db.clone();
|
||||||
|
|
||||||
let build = builds::ActiveModel {
|
let build = builds::ActiveModel {
|
||||||
pkg_id: version_model.package_id.clone(),
|
pkg_id: version_model.package_id.clone(),
|
||||||
version_id: version_model.id.clone(),
|
version_id: version_model.id.clone(),
|
||||||
ouput: Set(None),
|
ouput: Set(None),
|
||||||
status: Set(Some(0)),
|
status: Set(Some(0)),
|
||||||
|
start_time: Set(Some(
|
||||||
|
SystemTime::now()
|
||||||
|
.duration_since(UNIX_EPOCH)
|
||||||
|
.unwrap()
|
||||||
|
.as_secs() as u32,
|
||||||
|
)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let mut new_build = build.save(&db).await.unwrap();
|
let mut new_build = build.save(&db).await.unwrap();
|
||||||
@ -62,6 +68,8 @@ pub async fn init(db: DatabaseConnection, tx: Sender<Action>) {
|
|||||||
let _ = set_pkg_status(
|
let _ = set_pkg_status(
|
||||||
&db,
|
&db,
|
||||||
version_model.package_id.clone().unwrap(),
|
version_model.package_id.clone().unwrap(),
|
||||||
|
version_model.id.clone().unwrap(),
|
||||||
|
Some(false),
|
||||||
1,
|
1,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
@ -70,18 +78,32 @@ pub async fn init(db: DatabaseConnection, tx: Sender<Action>) {
|
|||||||
let _ = version_model.update(&db).await;
|
let _ = version_model.update(&db).await;
|
||||||
|
|
||||||
new_build.status = Set(Some(1));
|
new_build.status = Set(Some(1));
|
||||||
|
new_build.end_time = Set(Some(
|
||||||
|
SystemTime::now()
|
||||||
|
.duration_since(UNIX_EPOCH)
|
||||||
|
.unwrap()
|
||||||
|
.as_secs() as u32,
|
||||||
|
));
|
||||||
let _ = new_build.update(&db).await;
|
let _ = new_build.update(&db).await;
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let _ = set_pkg_status(
|
let _ = set_pkg_status(
|
||||||
&db,
|
&db,
|
||||||
version_model.package_id.clone().unwrap(),
|
version_model.package_id.clone().unwrap(),
|
||||||
|
version_model.id.clone().unwrap(),
|
||||||
|
None,
|
||||||
2,
|
2,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
let _ = version_model.update(&db).await;
|
let _ = version_model.update(&db).await;
|
||||||
|
|
||||||
new_build.status = Set(Some(2));
|
new_build.status = Set(Some(2));
|
||||||
|
new_build.end_time = Set(Some(
|
||||||
|
SystemTime::now()
|
||||||
|
.duration_since(UNIX_EPOCH)
|
||||||
|
.unwrap()
|
||||||
|
.as_secs() as u32,
|
||||||
|
));
|
||||||
let _ = new_build.update(&db).await;
|
let _ = new_build.update(&db).await;
|
||||||
|
|
||||||
println!("Error: {e}")
|
println!("Error: {e}")
|
||||||
@ -98,6 +120,8 @@ pub async fn init(db: DatabaseConnection, tx: Sender<Action>) {
|
|||||||
async fn set_pkg_status(
|
async fn set_pkg_status(
|
||||||
db: &DatabaseConnection,
|
db: &DatabaseConnection,
|
||||||
package_id: i32,
|
package_id: i32,
|
||||||
|
version_id: i32,
|
||||||
|
outofdate: Option<bool>,
|
||||||
status: i32,
|
status: i32,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
let mut pkg: packages::ActiveModel = Packages::find_by_id(package_id)
|
let mut pkg: packages::ActiveModel = Packages::find_by_id(package_id)
|
||||||
@ -107,6 +131,10 @@ async fn set_pkg_status(
|
|||||||
.into();
|
.into();
|
||||||
|
|
||||||
pkg.status = Set(status);
|
pkg.status = Set(status);
|
||||||
|
pkg.latest_version_id = Set(Some(version_id));
|
||||||
|
if outofdate.is_some() {
|
||||||
|
pkg.out_of_date = Set(outofdate.unwrap() as i32)
|
||||||
|
}
|
||||||
pkg.update(db).await?;
|
pkg.update(db).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,8 @@ pub struct Model {
|
|||||||
pub version_id: i32,
|
pub version_id: i32,
|
||||||
pub ouput: Option<String>,
|
pub ouput: Option<String>,
|
||||||
pub status: Option<i32>,
|
pub status: Option<i32>,
|
||||||
|
pub start_time: Option<u32>,
|
||||||
|
pub end_time: Option<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||||
|
@ -19,7 +19,9 @@ create table builds
|
|||||||
pkg_id integer not null,
|
pkg_id integer not null,
|
||||||
version_id integer not null,
|
version_id integer not null,
|
||||||
ouput TEXT,
|
ouput TEXT,
|
||||||
status integer
|
status integer,
|
||||||
|
start_time INTEGER,
|
||||||
|
end_time integer
|
||||||
);
|
);
|
||||||
|
|
||||||
create table packages
|
create table packages
|
||||||
@ -27,7 +29,12 @@ create table packages
|
|||||||
id integer not null
|
id integer not null
|
||||||
primary key autoincrement,
|
primary key autoincrement,
|
||||||
name text not null,
|
name text not null,
|
||||||
status integer default 0 not null
|
status integer default 0 not null,
|
||||||
|
out_of_date INTEGER default 0 not null,
|
||||||
|
latest_version_id integer
|
||||||
|
constraint packages_versions_id_fk
|
||||||
|
references versions,
|
||||||
|
latest_aur_version TEXT
|
||||||
);
|
);
|
||||||
|
|
||||||
create table status
|
create table status
|
||||||
|
@ -12,6 +12,9 @@ pub struct Model {
|
|||||||
pub id: i32,
|
pub id: i32,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub status: i32,
|
pub status: i32,
|
||||||
|
pub out_of_date: i32,
|
||||||
|
pub latest_version_id: Option<i32>,
|
||||||
|
pub latest_aur_version: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ActiveModelBehavior for ActiveModel {}
|
impl ActiveModelBehavior for ActiveModel {}
|
||||||
@ -22,6 +25,8 @@ pub enum Relation {
|
|||||||
Versions,
|
Versions,
|
||||||
#[sea_orm(has_many = "super::builds::Entity")]
|
#[sea_orm(has_many = "super::builds::Entity")]
|
||||||
Builds,
|
Builds,
|
||||||
|
#[sea_orm(has_one = "super::versions::Entity")]
|
||||||
|
LatestVersion,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Related<super::versions::Entity> for Entity {
|
impl Related<super::versions::Entity> for Entity {
|
||||||
@ -30,8 +35,14 @@ impl Related<super::versions::Entity> for Entity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// impl Related<super::versions::Entity> for Entity {
|
||||||
|
// fn to() -> RelationDef {
|
||||||
|
// Relation::LatestVersion.def()
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
impl Related<super::builds::Entity> for crate::db::versions::Entity {
|
impl Related<super::builds::Entity> for crate::db::versions::Entity {
|
||||||
fn to() -> RelationDef {
|
fn to() -> RelationDef {
|
||||||
crate::db::versions::Relation::Builds.def()
|
Relation::Builds.def()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,12 @@ pub enum Relation {
|
|||||||
to = "super::packages::Column::Id"
|
to = "super::packages::Column::Id"
|
||||||
)]
|
)]
|
||||||
Packages,
|
Packages,
|
||||||
|
#[sea_orm(
|
||||||
|
belongs_to = "super::packages::Entity",
|
||||||
|
from = "Column::Id",
|
||||||
|
to = "super::packages::Column::LatestVersionId"
|
||||||
|
)]
|
||||||
|
LatestPackage,
|
||||||
#[sea_orm(has_many = "super::builds::Entity")]
|
#[sea_orm(has_many = "super::builds::Entity")]
|
||||||
Builds,
|
Builds,
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ mod builder;
|
|||||||
mod db;
|
mod db;
|
||||||
mod pkgbuild;
|
mod pkgbuild;
|
||||||
mod repo;
|
mod repo;
|
||||||
|
mod scheduler;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
use crate::api::backend;
|
use crate::api::backend;
|
||||||
@ -11,6 +12,7 @@ use crate::api::backend;
|
|||||||
use crate::api::embed::CustomHandler;
|
use crate::api::embed::CustomHandler;
|
||||||
use crate::builder::types::Action;
|
use crate::builder::types::Action;
|
||||||
use crate::db::migration::Migrator;
|
use crate::db::migration::Migrator;
|
||||||
|
use crate::scheduler::aur_version_update::start_aur_version_checking;
|
||||||
use rocket::config::Config;
|
use rocket::config::Config;
|
||||||
use rocket::fs::FileServer;
|
use rocket::fs::FileServer;
|
||||||
use rocket::futures::future::join_all;
|
use rocket::futures::future::join_all;
|
||||||
@ -48,6 +50,8 @@ fn main() {
|
|||||||
builder::builder::init(db2, tx2).await;
|
builder::builder::init(db2, tx2).await;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
start_aur_version_checking(db.clone());
|
||||||
|
|
||||||
let backend_handle = tokio::spawn(async {
|
let backend_handle = tokio::spawn(async {
|
||||||
let mut config = Config::default();
|
let mut config = Config::default();
|
||||||
config.address = "0.0.0.0".parse().unwrap();
|
config.address = "0.0.0.0".parse().unwrap();
|
||||||
|
55
backend/src/scheduler/aur_version_update.rs
Normal file
55
backend/src/scheduler/aur_version_update.rs
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
use crate::db::packages;
|
||||||
|
use crate::db::prelude::Packages;
|
||||||
|
use anyhow::anyhow;
|
||||||
|
use aur_rs::{Package, Request};
|
||||||
|
use sea_orm::ActiveValue::Set;
|
||||||
|
use sea_orm::{ActiveModelTrait, DatabaseConnection, EntityTrait};
|
||||||
|
use std::time::Duration;
|
||||||
|
use tokio::time::sleep;
|
||||||
|
|
||||||
|
pub fn start_aur_version_checking(db: DatabaseConnection) {
|
||||||
|
tokio::spawn(async move {
|
||||||
|
sleep(Duration::from_secs(10)).await;
|
||||||
|
loop {
|
||||||
|
println!("performing aur version checks");
|
||||||
|
match aur_check_versions(db.clone()).await {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(e) => {
|
||||||
|
println!("Failed to perform aur version check: {e}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sleep(Duration::from_secs(3600)).await;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn aur_check_versions(db: DatabaseConnection) -> anyhow::Result<()> {
|
||||||
|
let packages = Packages::find().all(&db).await?;
|
||||||
|
let names: Vec<&str> = packages.iter().map(|x| x.name.as_str()).collect();
|
||||||
|
|
||||||
|
let request = Request::default();
|
||||||
|
let response = request.search_multi_info_by_names(names.as_slice()).await;
|
||||||
|
|
||||||
|
let results: Vec<Package> = response
|
||||||
|
.map_err(|_| anyhow!("couldn't download version update"))?
|
||||||
|
.results;
|
||||||
|
|
||||||
|
if results.len() != packages.len() {
|
||||||
|
println!("Package nr in repo and aur api response has different size");
|
||||||
|
}
|
||||||
|
|
||||||
|
for package in packages {
|
||||||
|
match results.iter().find(|x1| x1.name == package.name) {
|
||||||
|
None => {
|
||||||
|
println!("Couldn't find {} in AUR response", package.name)
|
||||||
|
}
|
||||||
|
Some(result) => {
|
||||||
|
let mut package: packages::ActiveModel = package.into();
|
||||||
|
|
||||||
|
package.latest_aur_version = Set(Some(result.version.clone()));
|
||||||
|
let _ = package.update(&db).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
1
backend/src/scheduler/mod.rs
Normal file
1
backend/src/scheduler/mod.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
pub mod aur_version_update;
|
@ -58,7 +58,10 @@ class _YourPackagesState extends State<YourPackages> {
|
|||||||
label: Text("Package Name"),
|
label: Text("Package Name"),
|
||||||
),
|
),
|
||||||
DataColumn(
|
DataColumn(
|
||||||
label: Text("Number of versions"),
|
label: Text("Version"),
|
||||||
|
),
|
||||||
|
DataColumn(
|
||||||
|
label: Text("Up-To-Date"),
|
||||||
),
|
),
|
||||||
DataColumn(
|
DataColumn(
|
||||||
label: Text("Status"),
|
label: Text("Status"),
|
||||||
@ -85,14 +88,25 @@ class _YourPackagesState extends State<YourPackages> {
|
|||||||
cells: [
|
cells: [
|
||||||
DataCell(Text(package.id.toString())),
|
DataCell(Text(package.id.toString())),
|
||||||
DataCell(Text(package.name)),
|
DataCell(Text(package.name)),
|
||||||
DataCell(Text(package.count.toString())),
|
DataCell(Text(package.latest_version.toString())),
|
||||||
|
DataCell(IconButton(
|
||||||
|
icon: Icon(
|
||||||
|
package.outofdate ? Icons.update : Icons.verified,
|
||||||
|
color: package.outofdate ? Color(0xFF6B43A4) : Color(0xFF0A6900),
|
||||||
|
),
|
||||||
|
onPressed: package.outofdate
|
||||||
|
? () {
|
||||||
|
// todo open build info with logs
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
)),
|
||||||
DataCell(IconButton(
|
DataCell(IconButton(
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
switchSuccessIcon(package.status),
|
switchSuccessIcon(package.status),
|
||||||
color: switchSuccessColor(package.status),
|
color: switchSuccessColor(package.status),
|
||||||
),
|
),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
// todo open build info with logs
|
//context.push("/build/${package.latest_version_id}");
|
||||||
},
|
},
|
||||||
)),
|
)),
|
||||||
DataCell(
|
DataCell(
|
||||||
|
@ -3,17 +3,22 @@ class Build {
|
|||||||
final String pkg_name;
|
final String pkg_name;
|
||||||
final String version;
|
final String version;
|
||||||
final int status;
|
final int status;
|
||||||
|
final int? start_time, end_time;
|
||||||
|
|
||||||
Build(
|
Build(
|
||||||
{required this.id,
|
{required this.id,
|
||||||
required this.pkg_name,
|
required this.pkg_name,
|
||||||
required this.version,
|
required this.version,
|
||||||
|
required this.start_time,
|
||||||
|
required this.end_time,
|
||||||
required this.status});
|
required this.status});
|
||||||
|
|
||||||
factory Build.fromJson(Map<String, dynamic> json) {
|
factory Build.fromJson(Map<String, dynamic> json) {
|
||||||
return Build(
|
return Build(
|
||||||
id: json["id"] as int,
|
id: json["id"] as int,
|
||||||
status: json["status"] as int,
|
status: json["status"] as int,
|
||||||
|
start_time: json["start_time"] as int?,
|
||||||
|
end_time: json["end_time"] as int?,
|
||||||
pkg_name: json["pkg_name"] as String,
|
pkg_name: json["pkg_name"] as String,
|
||||||
version: json["version"] as String,
|
version: json["version"] as String,
|
||||||
);
|
);
|
||||||
|
@ -1,21 +1,28 @@
|
|||||||
class Package {
|
class Package {
|
||||||
final int id;
|
final int id, latest_version_id;
|
||||||
final String name;
|
final String name;
|
||||||
final int count;
|
final bool outofdate;
|
||||||
final int status;
|
final int status;
|
||||||
|
final String latest_version, latest_aur_version;
|
||||||
|
|
||||||
Package(
|
Package(
|
||||||
{required this.id,
|
{required this.id,
|
||||||
|
required this.latest_version_id,
|
||||||
required this.name,
|
required this.name,
|
||||||
required this.count,
|
required this.status,
|
||||||
required this.status});
|
required this.latest_version,
|
||||||
|
required this.latest_aur_version,
|
||||||
|
required this.outofdate});
|
||||||
|
|
||||||
factory Package.fromJson(Map<String, dynamic> json) {
|
factory Package.fromJson(Map<String, dynamic> json) {
|
||||||
return Package(
|
return Package(
|
||||||
id: json["id"] as int,
|
id: json["id"] as int,
|
||||||
count: json["count"] as int,
|
outofdate: json["outofdate"] as bool,
|
||||||
status: json["status"] as int,
|
status: json["status"] as int,
|
||||||
name: json["name"] as String,
|
name: json["name"] as String,
|
||||||
|
latest_version: json["latest_version"] as String,
|
||||||
|
latest_version_id: json["latest_version_id"] as int,
|
||||||
|
latest_aur_version: json["latest_aur_version"] as String,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,11 @@
|
|||||||
import 'dart:async';
|
|
||||||
|
|
||||||
import 'package:aurcache/api/builds.dart';
|
|
||||||
import 'package:aurcache/components/build_output.dart';
|
import 'package:aurcache/components/build_output.dart';
|
||||||
import 'package:aurcache/models/build.dart';
|
import 'package:aurcache/models/build.dart';
|
||||||
import 'package:aurcache/components/api/APIBuilder.dart';
|
import 'package:aurcache/components/api/APIBuilder.dart';
|
||||||
import 'package:aurcache/providers/build_provider.dart';
|
import 'package:aurcache/providers/build_provider.dart';
|
||||||
|
import 'package:aurcache/utils/time_formatter.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
import '../api/API.dart';
|
|
||||||
import '../components/dashboard/your_packages.dart';
|
import '../components/dashboard/your_packages.dart';
|
||||||
|
|
||||||
class BuildScreen extends StatefulWidget {
|
class BuildScreen extends StatefulWidget {
|
||||||
@ -29,6 +26,9 @@ class _BuildScreenState extends State<BuildScreen> {
|
|||||||
interval: const Duration(seconds: 10),
|
interval: const Duration(seconds: 10),
|
||||||
onLoad: () => const Text("no data"),
|
onLoad: () => const Text("no data"),
|
||||||
onData: (buildData) {
|
onData: (buildData) {
|
||||||
|
final start_time = DateTime.fromMillisecondsSinceEpoch(
|
||||||
|
(buildData.start_time ?? 0) * 1000);
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
@ -58,7 +58,7 @@ class _BuildScreenState extends State<BuildScreen> {
|
|||||||
const SizedBox(
|
const SizedBox(
|
||||||
width: 10,
|
width: 10,
|
||||||
),
|
),
|
||||||
const Text("triggered 2 months ago")
|
Text("triggered ${start_time.readableDuration()}")
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
|
@ -23,6 +23,7 @@ class _DashboardScreenState extends State<DashboardScreen> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return APIBuilder<StatsProvider, Stats, Object>(
|
return APIBuilder<StatsProvider, Stats, Object>(
|
||||||
|
interval: const Duration(seconds: 10),
|
||||||
onData: (stats) {
|
onData: (stats) {
|
||||||
return SafeArea(
|
return SafeArea(
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
|
20
frontend/lib/utils/time_formatter.dart
Normal file
20
frontend/lib/utils/time_formatter.dart
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
extension TimeFormatter on DateTime {
|
||||||
|
String readableDuration() {
|
||||||
|
final now = DateTime.now();
|
||||||
|
final duration = now.difference(this);
|
||||||
|
|
||||||
|
if (duration.inSeconds < 60) {
|
||||||
|
return '${duration.inSeconds} seconds ago';
|
||||||
|
} else if (duration.inMinutes < 60) {
|
||||||
|
return '${duration.inMinutes} minutes ago';
|
||||||
|
} else if (duration.inHours < 24) {
|
||||||
|
return '${duration.inHours} hours ago';
|
||||||
|
} else if (duration.inDays < 30) {
|
||||||
|
return '${duration.inDays} days ago';
|
||||||
|
} else if ((duration.inDays / 30) < 12) {
|
||||||
|
return '${duration.inDays ~/ 30} months ago';
|
||||||
|
} else {
|
||||||
|
return '${duration.inDays ~/ 365} years ago';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user