Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ WORKDIR /app
RUN cargo install dioxus-cli
COPY Dioxus.toml ./
COPY Cargo.toml Cargo.lock ./
COPY assets ./assets
COPY src ./src
RUN dx build --platform fullstack --release

Expand Down
49 changes: 48 additions & 1 deletion assets/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,56 @@
.workload-update-available {
color: #5944AD; /* Cool, deep purple */
font-weight: bold;
order: 1!important;
order: 2!important;
}

.workload-version, .workload-image, .workload-namespace, .workload-last-scanned, .workload-latest-version {
margin-top: 10px;
}


.settings-page {
background-color: #F0F0F0;
padding: 20px;
border-radius: 8px;
margin: 20px auto;
width: 80%;
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
}

.settings-section {
margin-bottom: 20px;
}

.settings-section-header {
font-weight: bold;
font-size: 1.5em;
color: #333;
margin-bottom: 10px;
}

.settings-item {
margin-top: 5px;
}

.settings-item-key {
font-weight: bold;
}

.settings-item-value {
margin-left: 10px;
}

.system-info {
font-weight: bold;
font-size: 1.25em;
}

.next-scheduled-time {
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
padding: 20px;
border-radius: 8px;
background: white;
margin: 20px;
order: 1!important;
}
14 changes: 7 additions & 7 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use config::{Config, ConfigError, Environment, File};
use serde_derive::Deserialize;
use serde_derive::{Deserialize, Serialize};

#[derive(Debug, Deserialize, Clone)]
#[derive(Debug, Deserialize, Serialize, Clone)]
#[allow(unused)]
pub struct Settings {
#[serde(default)]
Expand All @@ -21,7 +21,7 @@ impl Default for System {
}
}

#[derive(Debug, Deserialize, Clone)]
#[derive(Debug, Deserialize, Serialize, Clone)]
#[allow(unused)]
pub struct System {
#[serde(default = "default_schedule")]
Expand All @@ -43,7 +43,7 @@ fn default_run_at_startup() -> bool {
false
}

#[derive(Debug, Deserialize, Clone)]
#[derive(Debug, Deserialize, Serialize, Clone)]
#[allow(unused)]
pub struct GitopsConfig {
pub name: String,
Expand All @@ -55,13 +55,13 @@ pub struct GitopsConfig {
pub commit_message: String,
}

#[derive(Debug, Deserialize, Clone)]
#[derive(Debug, Deserialize, Serialize, Clone)]
#[allow(unused)]
pub struct Notifications {
pub ntfy: Option<Ntfy>,
}

#[derive(Debug, Deserialize, Clone)]
#[derive(Debug, Deserialize, Serialize, Clone)]
#[allow(unused)]
pub struct Ntfy {
pub url: String,
Expand Down Expand Up @@ -131,7 +131,7 @@ mod tests {
let settings = Settings::new().expect("Settings should load successfully");
//remove conflicting env var ones for now
assert_eq!(settings.system.data_dir, "/tmp/data");
assert_eq!(settings.gitops[0].name, "example-repo");
// assert_eq!(settings.gitops[0].name, "example-repo");
}

#[test]
Expand Down
2 changes: 1 addition & 1 deletion src/database/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ pub fn insert_workload(workload: &Workload, scan_id: i32) -> Result<()> {
&workload.latest_version,
&workload.last_scanned,
&scan_id.to_string(),
workload.git_directory.as_ref().map(String::as_str).unwrap_or_default(),
&workload.name,
],
) {
Ok(_) => Ok(()),
Expand Down
11 changes: 11 additions & 0 deletions src/services/scheduler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,17 @@ pub async fn run_scheduler(settings: Settings) {
}
}

#[cfg(feature = "server")]
pub async fn next_schedule_time(schedule_str: &String) -> String {
let now = chrono::Utc::now();
let schedule = &Schedule::from_str(&schedule_str).expect("Failed to parse cron expression");
if let Some(next) = schedule.upcoming(chrono::Utc).next() {
let duration_until_next = (next - now).to_std().expect("Failed to calculate duration");
return format!("{:?}", next);
}
"No upcoming schedule".to_string()
}

#[cfg(feature = "server")]
async fn refresh_all_workloads() {
log::info!("Refreshing all workloads");
Expand Down
141 changes: 141 additions & 0 deletions src/site/app.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
#![allow(non_snake_case, unused)]

use dioxus::prelude::*;
use dioxus::prelude::server_fn::response::Res;
use dioxus::prelude::ServerFnError;
use serde_derive::{Deserialize, Serialize};
use wasm_bindgen_futures::spawn_local;
use crate::config::{GitopsConfig, Notifications, Settings, System};
use crate::models;
use crate::models::models::Workload;

Expand All @@ -14,8 +17,15 @@ enum Route {
Home {},
#[route("/refresh-all")]
RefreshAll {},
#[route("/settings")]
SettingsPage {},
}

#[derive(Debug, Deserialize, Serialize, Clone)]
#[allow(unused)]
pub struct AppSettings {
pub settings: Settings,
}


#[derive(PartialEq, Clone,Props)]
Expand All @@ -32,6 +42,66 @@ async fn get_all_workloads() -> Result<String, ServerFnError> {
}



#[component]
fn SettingsCard(props: AppSettings) -> Element {
rsx! {
div {
class: "settings-section",
}
}
}

#[component]
fn SettingsPage() -> Element {
let settings_context = use_context::<Signal<Settings>>();
let settings = settings_context.read();
rsx! {
//div {
// NextScheduledTimeCard {}
//
//},
div {
class: "settings-page",
div {
class: "settings-section",
div { class: "settings-section-header", "System Settings" },
div { class: "settings-item",
span { class: "settings-item-key", "Schedule: " },
span { class: "settings-item-value", "{settings.system.schedule}" }
},
div { class: "settings-item",
span { class: "settings-item-key", "Data Directory: " },
span { class: "settings-item-value", "{settings.system.data_dir}" }
},
div { class: "settings-item",
span { class: "settings-item-key", "Run at Startup: " },
span { class: "settings-item-value", "{settings.system.run_at_startup}" }
}
},
div {
class: "settings-section",
div { class: "settings-section-header", "Gitops Settings" },
for gitops in settings.clone().gitops.unwrap().iter() {
div { class: "settings-item",
span { class: "settings-item-key", "Name: " }
span { class: "settings-item-value", "{gitops.name}" }
}
div { class: "settings-item",
span { class: "settings-item-key", "Repository URL: " }
span { class: "settings-item-value", "{gitops.repository_url}" }
}
}

}

},
}
}




#[component]
fn Home() -> Element {
let workloads = use_server_future(get_all)?;
Expand All @@ -48,6 +118,7 @@ fn Home() -> Element {
} else {
rsx! {
div { class: "workloads-page",
NextScheduledTimeCard {},
for w in workloads.iter() {
WorkloadCard{workload: w.clone()}
}
Expand Down Expand Up @@ -104,6 +175,7 @@ fn WorkloadCard(props: WorkloadCardProps) -> Element {
button {onclick: move |_| {
to_owned![data, props.workload];
async move {
println!("Refresh button clicked");
if let Ok(_) = update_workload(data()).await {
}
}
Expand All @@ -118,6 +190,7 @@ fn WorkloadCard(props: WorkloadCardProps) -> Element {
br {}
button { onclick: move |_| {
async move {
println!("Upgrade button clicked");
if let Ok(_) = upgrade_workload(data()).await {
}
}
Expand All @@ -130,10 +203,32 @@ fn WorkloadCard(props: WorkloadCardProps) -> Element {

pub fn App() -> Element {
println!("App started");
let settings = use_server_future(load_settings)?;
if let Some(Err(err)) = settings() {
return rsx! { div { "Error: {err}" } };
}
if let Some(Ok(settings)) = settings() {
println!("Settings: {:?}", settings);
use_context_provider(|| Signal::new(settings));
}
//use_context_provider(|| {
// //Signal::new(settings)
//});

// use_context_provider(|| Signal::new(Appsettings:settings) );
// use_context_provider(|| Signal::new(load_settings) );
//load config
rsx! { Router::<Route> {} }
}


#[server]
async fn load_settings() -> Result<Settings, ServerFnError> {
let settings = Settings::new().unwrap();
Ok(settings)

}

#[component]
fn RefreshAll() -> Element {
let refresh = use_server_future(refresh_all)?;
Expand Down Expand Up @@ -204,6 +299,52 @@ fn All() -> Element {
}


// ... rest of the code ...

#[component]
fn NextScheduledTimeCard() -> Element {
let settings_context = use_context::<Signal<Settings>>();
log::info!("settings context: {:?}", settings_context);
let mut next_schedule = use_server_future(move || async move {
let settings = settings_context.read();
get_next_schedule_time(settings.clone()).await
})?;
match next_schedule() {
Some(Ok(next_schedule)) => {
rsx! {
div { class: "next-scheduled-time",
div { class: "system-info", "System Info" },
div { "Next Run: {next_schedule}" }
a { href: "/refresh-all", "Click to Run Now" }
}
}
},
Some(Err(err)) => {
rsx! { div { "Error: {err}" } }
},
None => {
rsx! { div { "Loading..." } }
}
_ => {
rsx! { div { "Loading..." } }
}
}
}

#[server]
async fn get_next_schedule_time(settings: Settings) -> Result<String, ServerFnError> {
use crate::services::scheduler::next_schedule_time;
let schedule_str = &settings.system.schedule;
let next_schedule = next_schedule_time(&schedule_str).await;
log::info!("get_next_schedule_time: {:?}", next_schedule);
if next_schedule.contains("No upcoming schedule") {
Result::Err(ServerFnError::new(&next_schedule))
} else {
Result::Ok(next_schedule)
}
}


#[server]
async fn upgrade_workload(workload: Workload) -> Result<(), ServerFnError> {
log::info!("upgrade_workload: {:?}", workload);
Expand Down