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:
		@@ -27,9 +27,9 @@ FROM archlinux
 | 
			
		||||
# Copy the built binary from the previous stage
 | 
			
		||||
COPY --from=builder /app/target/release/untitled /usr/local/bin/untitled
 | 
			
		||||
 | 
			
		||||
RUN echo " \
 | 
			
		||||
RUN echo $'\
 | 
			
		||||
[extra]\
 | 
			
		||||
Include = /etc/pacman.d/mirrorlist" >> /etc/pacman.conf
 | 
			
		||||
Include = /etc/pacman.d/mirrorlist' >> /etc/pacman.conf
 | 
			
		||||
 | 
			
		||||
RUN pacman -Syyu --noconfirm
 | 
			
		||||
RUN pacman-key --init && pacman-key --populate
 | 
			
		||||
 
 | 
			
		||||
@@ -1188,8 +1188,8 @@ fi
 | 
			
		||||
 | 
			
		||||
if (( ! INFAKEROOT )); then
 | 
			
		||||
	if (( EUID == 0 )); then
 | 
			
		||||
		error "$(gettext "Running %s as root is not allowed as it can cause permanent,\n\
 | 
			
		||||
catastrophic damage to your system.")" "makepkg"
 | 
			
		||||
		: #error "$(gettext "Running %s as root is not allowed as it can cause permanent,\n\
 | 
			
		||||
#catastrophic damage to your system.")" "makepkg"
 | 
			
		||||
		#exit $E_ROOT
 | 
			
		||||
	fi
 | 
			
		||||
else
 | 
			
		||||
 
 | 
			
		||||
@@ -9,9 +9,9 @@ use rocket::serde::{Deserialize, Serialize};
 | 
			
		||||
use rocket::{get, State};
 | 
			
		||||
use rocket_okapi::okapi::schemars;
 | 
			
		||||
use rocket_okapi::{openapi, JsonSchema};
 | 
			
		||||
use sea_orm::PaginatorTrait;
 | 
			
		||||
use sea_orm::{ColumnTrait, QueryFilter};
 | 
			
		||||
use sea_orm::{DatabaseConnection, EntityTrait, FromQueryResult, QuerySelect, RelationTrait};
 | 
			
		||||
use sea_orm::{Order, PaginatorTrait, QueryOrder};
 | 
			
		||||
 | 
			
		||||
#[derive(Serialize, JsonSchema)]
 | 
			
		||||
#[serde(crate = "rocket::serde")]
 | 
			
		||||
@@ -43,8 +43,11 @@ pub async fn search(query: &str) -> Result<Json<Vec<ApiPackage>>, String> {
 | 
			
		||||
pub struct ListPackageModel {
 | 
			
		||||
    id: i32,
 | 
			
		||||
    name: String,
 | 
			
		||||
    count: i32,
 | 
			
		||||
    status: i32,
 | 
			
		||||
    outofdate: bool,
 | 
			
		||||
    latest_version: String,
 | 
			
		||||
    latest_version_id: i32,
 | 
			
		||||
    latest_aur_version: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[openapi(tag = "test")]
 | 
			
		||||
@@ -55,13 +58,15 @@ pub async fn package_list(
 | 
			
		||||
    let db = db as &DatabaseConnection;
 | 
			
		||||
 | 
			
		||||
    let all: Vec<ListPackageModel> = Packages::find()
 | 
			
		||||
        .join_rev(JoinType::InnerJoin, versions::Relation::Packages.def())
 | 
			
		||||
        .join_rev(JoinType::InnerJoin, versions::Relation::LatestPackage.def())
 | 
			
		||||
        .select_only()
 | 
			
		||||
        .column_as(versions::Column::Id.count(), "count")
 | 
			
		||||
        .column(packages::Column::Name)
 | 
			
		||||
        .column(packages::Column::Id)
 | 
			
		||||
        .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>()
 | 
			
		||||
        .all(db)
 | 
			
		||||
        .await
 | 
			
		||||
@@ -146,6 +151,8 @@ pub struct ListBuildsModel {
 | 
			
		||||
    pkg_name: String,
 | 
			
		||||
    version: String,
 | 
			
		||||
    status: i32,
 | 
			
		||||
    start_time: Option<u32>,
 | 
			
		||||
    end_time: Option<u32>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[openapi(tag = "test")]
 | 
			
		||||
@@ -165,6 +172,9 @@ pub async fn list_builds(
 | 
			
		||||
        .column(builds::Column::Status)
 | 
			
		||||
        .column_as(packages::Column::Name, "pkg_name")
 | 
			
		||||
        .column(versions::Column::Version)
 | 
			
		||||
        .column(builds::Column::EndTime)
 | 
			
		||||
        .column(builds::Column::StartTime)
 | 
			
		||||
        .order_by(builds::Column::StartTime, Order::Desc)
 | 
			
		||||
        .limit(limit);
 | 
			
		||||
 | 
			
		||||
    let build = match pkgid {
 | 
			
		||||
@@ -197,6 +207,8 @@ pub async fn get_build(
 | 
			
		||||
        .column(builds::Column::Status)
 | 
			
		||||
        .column_as(packages::Column::Name, "pkg_name")
 | 
			
		||||
        .column(versions::Column::Version)
 | 
			
		||||
        .column(builds::Column::EndTime)
 | 
			
		||||
        .column(builds::Column::StartTime)
 | 
			
		||||
        .into_model::<ListBuildsModel>()
 | 
			
		||||
        .one(db)
 | 
			
		||||
        .await
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@ use crate::repo::repo::add_pkg;
 | 
			
		||||
use anyhow::anyhow;
 | 
			
		||||
use sea_orm::{ActiveModelTrait, DatabaseConnection, EntityTrait, Set};
 | 
			
		||||
use std::ops::Add;
 | 
			
		||||
use std::time::{SystemTime, UNIX_EPOCH};
 | 
			
		||||
use tokio::sync::broadcast;
 | 
			
		||||
use tokio::sync::broadcast::error::RecvError;
 | 
			
		||||
use tokio::sync::broadcast::Sender;
 | 
			
		||||
@@ -16,12 +17,17 @@ pub async fn init(db: DatabaseConnection, tx: Sender<Action>) {
 | 
			
		||||
                // add a package to parallel build
 | 
			
		||||
                Action::Build(name, version, url, mut version_model) => {
 | 
			
		||||
                    let db = db.clone();
 | 
			
		||||
 | 
			
		||||
                    let build = builds::ActiveModel {
 | 
			
		||||
                        pkg_id: version_model.package_id.clone(),
 | 
			
		||||
                        version_id: version_model.id.clone(),
 | 
			
		||||
                        ouput: Set(None),
 | 
			
		||||
                        status: Set(Some(0)),
 | 
			
		||||
                        start_time: Set(Some(
 | 
			
		||||
                            SystemTime::now()
 | 
			
		||||
                                .duration_since(UNIX_EPOCH)
 | 
			
		||||
                                .unwrap()
 | 
			
		||||
                                .as_secs() as u32,
 | 
			
		||||
                        )),
 | 
			
		||||
                        ..Default::default()
 | 
			
		||||
                    };
 | 
			
		||||
                    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(
 | 
			
		||||
                                    &db,
 | 
			
		||||
                                    version_model.package_id.clone().unwrap(),
 | 
			
		||||
                                    version_model.id.clone().unwrap(),
 | 
			
		||||
                                    Some(false),
 | 
			
		||||
                                    1,
 | 
			
		||||
                                )
 | 
			
		||||
                                .await;
 | 
			
		||||
@@ -70,18 +78,32 @@ pub async fn init(db: DatabaseConnection, tx: Sender<Action>) {
 | 
			
		||||
                                let _ = version_model.update(&db).await;
 | 
			
		||||
 | 
			
		||||
                                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;
 | 
			
		||||
                            }
 | 
			
		||||
                            Err(e) => {
 | 
			
		||||
                                let _ = set_pkg_status(
 | 
			
		||||
                                    &db,
 | 
			
		||||
                                    version_model.package_id.clone().unwrap(),
 | 
			
		||||
                                    version_model.id.clone().unwrap(),
 | 
			
		||||
                                    None,
 | 
			
		||||
                                    2,
 | 
			
		||||
                                )
 | 
			
		||||
                                .await;
 | 
			
		||||
                                let _ = version_model.update(&db).await;
 | 
			
		||||
 | 
			
		||||
                                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;
 | 
			
		||||
 | 
			
		||||
                                println!("Error: {e}")
 | 
			
		||||
@@ -98,6 +120,8 @@ pub async fn init(db: DatabaseConnection, tx: Sender<Action>) {
 | 
			
		||||
async fn set_pkg_status(
 | 
			
		||||
    db: &DatabaseConnection,
 | 
			
		||||
    package_id: i32,
 | 
			
		||||
    version_id: i32,
 | 
			
		||||
    outofdate: Option<bool>,
 | 
			
		||||
    status: i32,
 | 
			
		||||
) -> anyhow::Result<()> {
 | 
			
		||||
    let mut pkg: packages::ActiveModel = Packages::find_by_id(package_id)
 | 
			
		||||
@@ -107,6 +131,10 @@ async fn set_pkg_status(
 | 
			
		||||
        .into();
 | 
			
		||||
 | 
			
		||||
    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?;
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -14,6 +14,8 @@ pub struct Model {
 | 
			
		||||
    pub version_id: i32,
 | 
			
		||||
    pub ouput: Option<String>,
 | 
			
		||||
    pub status: Option<i32>,
 | 
			
		||||
    pub start_time: Option<u32>,
 | 
			
		||||
    pub end_time: Option<u32>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
 | 
			
		||||
 
 | 
			
		||||
@@ -19,7 +19,9 @@ create table builds
 | 
			
		||||
	pkg_id integer not null,
 | 
			
		||||
	version_id integer not null,
 | 
			
		||||
	ouput TEXT,
 | 
			
		||||
	status integer
 | 
			
		||||
	status integer,
 | 
			
		||||
	start_time INTEGER,
 | 
			
		||||
	end_time integer
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
create table packages
 | 
			
		||||
@@ -27,7 +29,12 @@ create table packages
 | 
			
		||||
	id integer not null
 | 
			
		||||
		primary key autoincrement,
 | 
			
		||||
	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
 | 
			
		||||
 
 | 
			
		||||
@@ -12,6 +12,9 @@ pub struct Model {
 | 
			
		||||
    pub id: i32,
 | 
			
		||||
    pub name: String,
 | 
			
		||||
    pub status: i32,
 | 
			
		||||
    pub out_of_date: i32,
 | 
			
		||||
    pub latest_version_id: Option<i32>,
 | 
			
		||||
    pub latest_aur_version: Option<String>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ActiveModelBehavior for ActiveModel {}
 | 
			
		||||
@@ -22,6 +25,8 @@ pub enum Relation {
 | 
			
		||||
    Versions,
 | 
			
		||||
    #[sea_orm(has_many = "super::builds::Entity")]
 | 
			
		||||
    Builds,
 | 
			
		||||
    #[sea_orm(has_one = "super::versions::Entity")]
 | 
			
		||||
    LatestVersion,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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 {
 | 
			
		||||
    fn to() -> RelationDef {
 | 
			
		||||
        crate::db::versions::Relation::Builds.def()
 | 
			
		||||
        Relation::Builds.def()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -23,6 +23,12 @@ pub enum Relation {
 | 
			
		||||
        to = "super::packages::Column::Id"
 | 
			
		||||
    )]
 | 
			
		||||
    Packages,
 | 
			
		||||
    #[sea_orm(
 | 
			
		||||
        belongs_to = "super::packages::Entity",
 | 
			
		||||
        from = "Column::Id",
 | 
			
		||||
        to = "super::packages::Column::LatestVersionId"
 | 
			
		||||
    )]
 | 
			
		||||
    LatestPackage,
 | 
			
		||||
    #[sea_orm(has_many = "super::builds::Entity")]
 | 
			
		||||
    Builds,
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,7 @@ mod builder;
 | 
			
		||||
mod db;
 | 
			
		||||
mod pkgbuild;
 | 
			
		||||
mod repo;
 | 
			
		||||
mod scheduler;
 | 
			
		||||
mod utils;
 | 
			
		||||
 | 
			
		||||
use crate::api::backend;
 | 
			
		||||
@@ -11,6 +12,7 @@ use crate::api::backend;
 | 
			
		||||
use crate::api::embed::CustomHandler;
 | 
			
		||||
use crate::builder::types::Action;
 | 
			
		||||
use crate::db::migration::Migrator;
 | 
			
		||||
use crate::scheduler::aur_version_update::start_aur_version_checking;
 | 
			
		||||
use rocket::config::Config;
 | 
			
		||||
use rocket::fs::FileServer;
 | 
			
		||||
use rocket::futures::future::join_all;
 | 
			
		||||
@@ -48,6 +50,8 @@ fn main() {
 | 
			
		||||
            builder::builder::init(db2, tx2).await;
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        start_aur_version_checking(db.clone());
 | 
			
		||||
 | 
			
		||||
        let backend_handle = tokio::spawn(async {
 | 
			
		||||
            let mut config = Config::default();
 | 
			
		||||
            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"),
 | 
			
		||||
                        ),
 | 
			
		||||
                        DataColumn(
 | 
			
		||||
                          label: Text("Number of versions"),
 | 
			
		||||
                          label: Text("Version"),
 | 
			
		||||
                        ),
 | 
			
		||||
                        DataColumn(
 | 
			
		||||
                          label: Text("Up-To-Date"),
 | 
			
		||||
                        ),
 | 
			
		||||
                        DataColumn(
 | 
			
		||||
                          label: Text("Status"),
 | 
			
		||||
@@ -85,14 +88,25 @@ class _YourPackagesState extends State<YourPackages> {
 | 
			
		||||
      cells: [
 | 
			
		||||
        DataCell(Text(package.id.toString())),
 | 
			
		||||
        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(
 | 
			
		||||
          icon: Icon(
 | 
			
		||||
            switchSuccessIcon(package.status),
 | 
			
		||||
            color: switchSuccessColor(package.status),
 | 
			
		||||
          ),
 | 
			
		||||
          onPressed: () {
 | 
			
		||||
            // todo open build info with logs
 | 
			
		||||
            //context.push("/build/${package.latest_version_id}");
 | 
			
		||||
          },
 | 
			
		||||
        )),
 | 
			
		||||
        DataCell(
 | 
			
		||||
 
 | 
			
		||||
@@ -3,17 +3,22 @@ class Build {
 | 
			
		||||
  final String pkg_name;
 | 
			
		||||
  final String version;
 | 
			
		||||
  final int status;
 | 
			
		||||
  final int? start_time, end_time;
 | 
			
		||||
 | 
			
		||||
  Build(
 | 
			
		||||
      {required this.id,
 | 
			
		||||
      required this.pkg_name,
 | 
			
		||||
      required this.version,
 | 
			
		||||
      required this.start_time,
 | 
			
		||||
      required this.end_time,
 | 
			
		||||
      required this.status});
 | 
			
		||||
 | 
			
		||||
  factory Build.fromJson(Map<String, dynamic> json) {
 | 
			
		||||
    return Build(
 | 
			
		||||
      id: json["id"] 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,
 | 
			
		||||
      version: json["version"] as String,
 | 
			
		||||
    );
 | 
			
		||||
 
 | 
			
		||||
@@ -1,21 +1,28 @@
 | 
			
		||||
class Package {
 | 
			
		||||
  final int id;
 | 
			
		||||
  final int id, latest_version_id;
 | 
			
		||||
  final String name;
 | 
			
		||||
  final int count;
 | 
			
		||||
  final bool outofdate;
 | 
			
		||||
  final int status;
 | 
			
		||||
  final String latest_version, latest_aur_version;
 | 
			
		||||
 | 
			
		||||
  Package(
 | 
			
		||||
      {required this.id,
 | 
			
		||||
      required this.latest_version_id,
 | 
			
		||||
      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) {
 | 
			
		||||
    return Package(
 | 
			
		||||
      id: json["id"] as int,
 | 
			
		||||
      count: json["count"] as int,
 | 
			
		||||
      outofdate: json["outofdate"] as bool,
 | 
			
		||||
      status: json["status"] as int,
 | 
			
		||||
      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/models/build.dart';
 | 
			
		||||
import 'package:aurcache/components/api/APIBuilder.dart';
 | 
			
		||||
import 'package:aurcache/providers/build_provider.dart';
 | 
			
		||||
import 'package:aurcache/utils/time_formatter.dart';
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:go_router/go_router.dart';
 | 
			
		||||
 | 
			
		||||
import '../api/API.dart';
 | 
			
		||||
import '../components/dashboard/your_packages.dart';
 | 
			
		||||
 | 
			
		||||
class BuildScreen extends StatefulWidget {
 | 
			
		||||
@@ -29,6 +26,9 @@ class _BuildScreenState extends State<BuildScreen> {
 | 
			
		||||
          interval: const Duration(seconds: 10),
 | 
			
		||||
          onLoad: () => const Text("no data"),
 | 
			
		||||
          onData: (buildData) {
 | 
			
		||||
            final start_time = DateTime.fromMillisecondsSinceEpoch(
 | 
			
		||||
                (buildData.start_time ?? 0) * 1000);
 | 
			
		||||
 | 
			
		||||
            return Column(
 | 
			
		||||
              crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
              mainAxisAlignment: MainAxisAlignment.start,
 | 
			
		||||
@@ -58,7 +58,7 @@ class _BuildScreenState extends State<BuildScreen> {
 | 
			
		||||
                    const SizedBox(
 | 
			
		||||
                      width: 10,
 | 
			
		||||
                    ),
 | 
			
		||||
                    const Text("triggered 2 months ago")
 | 
			
		||||
                    Text("triggered ${start_time.readableDuration()}")
 | 
			
		||||
                  ],
 | 
			
		||||
                ),
 | 
			
		||||
                const SizedBox(
 | 
			
		||||
 
 | 
			
		||||
@@ -23,6 +23,7 @@ class _DashboardScreenState extends State<DashboardScreen> {
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return APIBuilder<StatsProvider, Stats, Object>(
 | 
			
		||||
      interval: const Duration(seconds: 10),
 | 
			
		||||
      onData: (stats) {
 | 
			
		||||
        return SafeArea(
 | 
			
		||||
          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';
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user