Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 23529f1f94 | |||
| 963aa3bcfb | |||
| a609a088d5 | |||
| ccfdf4e7ab | |||
| a860dce421 | |||
| e19b748bac | |||
| 01b80429f8 | |||
| 6a76c2ded7 | |||
| f1c5080c97 | |||
| 72681218e7 | |||
| ededef6d30 | |||
| 1aae27b0f4 | |||
| 663dbc3cc2 | |||
| d2d181de4a | |||
| 31d45ca1db | |||
| a086956b48 | |||
| 76cfe62f08 | |||
| 5555747422 | |||
| 729b45f52a | |||
| 66bb21ed88 |
@@ -2,8 +2,8 @@ name: Build Library
|
|||||||
run-name: ${{ gitea.actor }}
|
run-name: ${{ gitea.actor }}
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches-ignore:
|
||||||
- RustImplementation
|
- master
|
||||||
paths:
|
paths:
|
||||||
- airlock_libs/**
|
- airlock_libs/**
|
||||||
|
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ class MultiAgentSelector(Widget):
|
|||||||
|
|
||||||
def compose(self):
|
def compose(self):
|
||||||
yield Header(show_clock=True, icon="⚙")
|
yield Header(show_clock=True, icon="⚙")
|
||||||
title_text = Static("🖥️ Agent Selector", id="selector_title")
|
title_text = Static("🖥️ Agent Selector", id="selector_title")
|
||||||
title_text.styles.margin = (0, 0, 0, 1)
|
title_text.styles.margin = (0, 0, 0, 1)
|
||||||
yield title_text
|
yield title_text
|
||||||
|
|
||||||
@@ -175,7 +175,7 @@ class MultiAgentSelector(Widget):
|
|||||||
match_list.add_option((name, name))
|
match_list.add_option((name, name))
|
||||||
unmatched_label = self.query_one("#unmatched_label", Static)
|
unmatched_label = self.query_one("#unmatched_label", Static)
|
||||||
if unmatched:
|
if unmatched:
|
||||||
unmatched_label.update(f"No matches for: {', '.join(unmatched)}")
|
unmatched_label.update(f"âš ï¸ No matches for: {', '.join(unmatched)}")
|
||||||
else:
|
else:
|
||||||
unmatched_label.update("")
|
unmatched_label.update("")
|
||||||
|
|
||||||
|
|||||||
@@ -23,10 +23,8 @@ import webbrowser
|
|||||||
from textual.containers import Horizontal, Vertical, VerticalScroll
|
from textual.containers import Horizontal, Vertical, VerticalScroll
|
||||||
from textual.message import Message
|
from textual.message import Message
|
||||||
from textual.widget import Widget
|
from textual.widget import Widget
|
||||||
from textual.widgets import Button, Input, Rule, Static, Switch
|
from textual.widgets import Button, Rule, Static
|
||||||
|
|
||||||
from utils.configmanager import get_user_value, save_user_config
|
|
||||||
from utils.setup import get_base_directory
|
|
||||||
from utils.versionchecker import (
|
from utils.versionchecker import (
|
||||||
RELEASES_PAGE_URL,
|
RELEASES_PAGE_URL,
|
||||||
UpdateCheckResult,
|
UpdateCheckResult,
|
||||||
@@ -83,36 +81,6 @@ class SettingsWidget(Widget):
|
|||||||
#light_themes_col {
|
#light_themes_col {
|
||||||
margin-left: 1;
|
margin-left: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Telemetry section */
|
|
||||||
#telemetry_row {
|
|
||||||
height: auto;
|
|
||||||
margin: 1 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#telemetry_label {
|
|
||||||
width: auto;
|
|
||||||
margin-right: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
#telemetry_url_container {
|
|
||||||
height: auto;
|
|
||||||
margin: 1 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#telemetry_url_label {
|
|
||||||
width: auto;
|
|
||||||
margin-right: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
#telemetry_url_input {
|
|
||||||
width: 1fr;
|
|
||||||
}
|
|
||||||
|
|
||||||
.settings_description {
|
|
||||||
color: $text-muted;
|
|
||||||
margin-bottom: 1;
|
|
||||||
}
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class ThemeSelected(Message):
|
class ThemeSelected(Message):
|
||||||
@@ -178,40 +146,6 @@ class SettingsWidget(Widget):
|
|||||||
|
|
||||||
yield Rule()
|
yield Rule()
|
||||||
|
|
||||||
# Telemetry Section
|
|
||||||
with Vertical(id="telemetry_section") as telemetry:
|
|
||||||
telemetry.styles.height = "auto"
|
|
||||||
yield Static(
|
|
||||||
"📊 Telemetry Settings",
|
|
||||||
id="telemetry_title",
|
|
||||||
classes="settings_section_title",
|
|
||||||
)
|
|
||||||
|
|
||||||
telemetry_enabled = get_user_value("TELEMETRY", bool, False)
|
|
||||||
|
|
||||||
with Horizontal(id="telemetry_row"):
|
|
||||||
yield Static("Enable Telemetry: ", id="telemetry_label")
|
|
||||||
yield Switch(value=telemetry_enabled, id="telemetry_switch")
|
|
||||||
|
|
||||||
# URL input container - shown only when telemetry is enabled
|
|
||||||
if telemetry_enabled:
|
|
||||||
telemetry_url = get_user_value("TELEM_URL", str, "")
|
|
||||||
with Horizontal(id="telemetry_url_container"):
|
|
||||||
yield Static("Telemetry URL: ", id="telemetry_url_label")
|
|
||||||
yield Input(
|
|
||||||
value=telemetry_url,
|
|
||||||
placeholder="https://your-telemetry-endpoint.com",
|
|
||||||
id="telemetry_url_input",
|
|
||||||
)
|
|
||||||
|
|
||||||
yield Static(
|
|
||||||
"Help improve Loxide by sending anonymous usage data",
|
|
||||||
id="telemetry_description",
|
|
||||||
classes="settings_description",
|
|
||||||
)
|
|
||||||
|
|
||||||
yield Rule()
|
|
||||||
|
|
||||||
# Theme Section - Three columns: Dark 1, Dark 2, Light
|
# Theme Section - Three columns: Dark 1, Dark 2, Light
|
||||||
with Vertical(id="themes_section") as themes:
|
with Vertical(id="themes_section") as themes:
|
||||||
themes.styles.height = "auto"
|
themes.styles.height = "auto"
|
||||||
@@ -297,85 +231,6 @@ class SettingsWidget(Widget):
|
|||||||
self.post_message(self.ThemeSelected(theme_name))
|
self.post_message(self.ThemeSelected(theme_name))
|
||||||
event.stop()
|
event.stop()
|
||||||
|
|
||||||
def on_switch_changed(self, event: Switch.Changed) -> None:
|
|
||||||
"""Handle telemetry switch toggle."""
|
|
||||||
if event.switch.id == "telemetry_switch":
|
|
||||||
enabled = event.value
|
|
||||||
self._persist_telemetry_setting(enabled)
|
|
||||||
|
|
||||||
if enabled:
|
|
||||||
self._mount_telemetry_url_input()
|
|
||||||
else:
|
|
||||||
self._unmount_telemetry_url_input()
|
|
||||||
|
|
||||||
def on_input_changed(self, event: Input.Changed) -> None:
|
|
||||||
"""Handle telemetry URL input changes."""
|
|
||||||
if event.input.id == "telemetry_url_input":
|
|
||||||
self._persist_telemetry_url(event.value)
|
|
||||||
|
|
||||||
def _persist_telemetry_setting(self, enabled: bool) -> None:
|
|
||||||
"""Store the telemetry opt-in/out setting in the user's config."""
|
|
||||||
base_dir = get_base_directory()
|
|
||||||
config_dir = base_dir / "config"
|
|
||||||
|
|
||||||
try:
|
|
||||||
save_user_config(config_dir, {"TELEMETRY": enabled})
|
|
||||||
logger.debug("Updated user config with TELEMETRY=%s", enabled)
|
|
||||||
status = "enabled" if enabled else "disabled"
|
|
||||||
self.app.notify(f"📊 Telemetry {status}", timeout=2)
|
|
||||||
except Exception as exc:
|
|
||||||
logger.error("Failed to save TELEMETRY setting: %s", exc)
|
|
||||||
self.app.notify(f"⚠️ Failed to save setting: {exc}", severity="warning")
|
|
||||||
|
|
||||||
def _persist_telemetry_url(self, url: str) -> None:
|
|
||||||
"""Store the telemetry URL in the user's config."""
|
|
||||||
base_dir = get_base_directory()
|
|
||||||
config_dir = base_dir / "config"
|
|
||||||
|
|
||||||
try:
|
|
||||||
save_user_config(config_dir, {"TELEM_URL": url})
|
|
||||||
logger.debug("Updated user config with TELEM_URL=%s", url)
|
|
||||||
except Exception as exc:
|
|
||||||
logger.error("Failed to save TELEM_URL setting: %s", exc)
|
|
||||||
|
|
||||||
def _mount_telemetry_url_input(self) -> None:
|
|
||||||
"""Mount the telemetry URL input container."""
|
|
||||||
try:
|
|
||||||
# Check if already mounted
|
|
||||||
self.query_one("#telemetry_url_container")
|
|
||||||
return
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
telemetry_url = get_user_value("TELEM_URL", str, "")
|
|
||||||
|
|
||||||
# Create container with children
|
|
||||||
container = Horizontal(
|
|
||||||
Static("Telemetry URL: ", id="telemetry_url_label"),
|
|
||||||
Input(
|
|
||||||
value=telemetry_url,
|
|
||||||
placeholder="https://your-telemetry-endpoint.com",
|
|
||||||
id="telemetry_url_input",
|
|
||||||
),
|
|
||||||
id="telemetry_url_container",
|
|
||||||
)
|
|
||||||
|
|
||||||
# Mount after the telemetry_row within telemetry_section
|
|
||||||
try:
|
|
||||||
telemetry_section = self.query_one("#telemetry_section")
|
|
||||||
telemetry_row = self.query_one("#telemetry_row")
|
|
||||||
telemetry_section.mount(container, after=telemetry_row)
|
|
||||||
except Exception as exc:
|
|
||||||
logger.error("Failed to mount telemetry URL input: %s", exc)
|
|
||||||
|
|
||||||
def _unmount_telemetry_url_input(self) -> None:
|
|
||||||
"""Unmount the telemetry URL input container."""
|
|
||||||
try:
|
|
||||||
container = self.query_one("#telemetry_url_container")
|
|
||||||
container.remove()
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def _check_for_updates(self) -> None:
|
def _check_for_updates(self) -> None:
|
||||||
"""Check for updates and update UI."""
|
"""Check for updates and update UI."""
|
||||||
if self._checking:
|
if self._checking:
|
||||||
@@ -642,36 +497,6 @@ class ThemeSelector(Widget):
|
|||||||
#light_themes_col {
|
#light_themes_col {
|
||||||
margin-left: 1;
|
margin-left: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Telemetry section */
|
|
||||||
#telemetry_row {
|
|
||||||
height: auto;
|
|
||||||
margin: 1 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#telemetry_label {
|
|
||||||
width: auto;
|
|
||||||
margin-right: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
#telemetry_url_container {
|
|
||||||
height: auto;
|
|
||||||
margin: 1 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#telemetry_url_label {
|
|
||||||
width: auto;
|
|
||||||
margin-right: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
#telemetry_url_input {
|
|
||||||
width: 1fr;
|
|
||||||
}
|
|
||||||
|
|
||||||
.settings_description {
|
|
||||||
color: $text-muted;
|
|
||||||
margin-bottom: 1;
|
|
||||||
}
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class ThemeSelected(Message):
|
class ThemeSelected(Message):
|
||||||
|
|||||||
Generated
+1
-1
@@ -26,7 +26,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "airlock_libs"
|
name = "airlock_libs"
|
||||||
version = "6.1.1"
|
version = "7.4.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"crossbeam",
|
"crossbeam",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "airlock_libs"
|
name = "airlock_libs"
|
||||||
version = "6.1.1"
|
version = "7.4.1"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ build-backend = "maturin"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "airlock_libs"
|
name = "airlock_libs"
|
||||||
version = "6.1.1"
|
version = "7.4.1"
|
||||||
description = "Airlock Digital API Wrapper"
|
description = "Airlock Digital API Wrapper"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
license = { text = "AGPL-3.0-only" }
|
license = { text = "AGPL-3.0-only" }
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
use serde_json::json;
|
|
||||||
|
|
||||||
use crate::modules::datatypes::*;
|
use crate::modules::datatypes::*;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
use opentelemetry::trace::SpanContext;
|
||||||
|
|
||||||
#[pyfunction]
|
#[pyfunction]
|
||||||
pub fn pull_policy_exec_histories(
|
pub fn pull_policy_exec_histories(
|
||||||
@@ -75,8 +74,8 @@ pub fn pull_policy_exec_histories(
|
|||||||
.set_draw_target(ProgressDrawTarget::stderr());
|
.set_draw_target(ProgressDrawTarget::stderr());
|
||||||
progress_bar.lock().unwrap().set_style(
|
progress_bar.lock().unwrap().set_style(
|
||||||
ProgressStyle::default_bar()
|
ProgressStyle::default_bar()
|
||||||
.template("Total Completion: {spinner:.green} [{elapsed_precise}] [{bar:40.green/blue}] {pos}/{len} {message}")
|
.template("Total - Policy Name: {msg}: {spinner:.green} [{elapsed_precise}] [{bar:40.green/blue}] {pos}/{len}")
|
||||||
.unwrap(),
|
.unwrap().progress_chars("⣿⣦⣀")
|
||||||
);
|
);
|
||||||
let client: Client = tracer.in_span("Building HTTP Client", |cx| {
|
let client: Client = tracer.in_span("Building HTTP Client", |cx| {
|
||||||
let client_result: Result<Client, reqwest::Error> = build_client(headers);
|
let client_result: Result<Client, reqwest::Error> = build_client(headers);
|
||||||
@@ -108,9 +107,10 @@ pub fn pull_policy_exec_histories(
|
|||||||
});
|
});
|
||||||
let cutoff: chrono::NaiveDateTime =
|
let cutoff: chrono::NaiveDateTime =
|
||||||
Local::now().naive_local() - chrono::Duration::days(days);
|
Local::now().naive_local() - chrono::Duration::days(days);
|
||||||
let (tx, rx) = unbounded::<Vec<Group>>();
|
let (tx, rx) = unbounded::<(SpanContext, Vec<Group>)>();
|
||||||
let pb_clone = progress_bar.clone();
|
let pb_clone = progress_bar.clone();
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
|
let tracer = global::tracer("loxide");
|
||||||
let mut seen: HashMap<(String, String, String), Group> = if writeable_filepath.exists()
|
let mut seen: HashMap<(String, String, String), Group> = if writeable_filepath.exists()
|
||||||
{
|
{
|
||||||
let contents: String = fs::read_to_string(&writeable_filepath).unwrap_or_default();
|
let contents: String = fs::read_to_string(&writeable_filepath).unwrap_or_default();
|
||||||
@@ -139,7 +139,22 @@ pub fn pull_policy_exec_histories(
|
|||||||
} else {
|
} else {
|
||||||
HashMap::new()
|
HashMap::new()
|
||||||
};
|
};
|
||||||
while let Ok(parsed_responses) = rx.recv() {
|
while let Ok((parent_spancontext, parsed_responses)) = rx.recv() {
|
||||||
|
let parent_ctx = Context::new().with_remote_span_context(parent_spancontext);
|
||||||
|
let span = tracer.build_with_context(
|
||||||
|
tracer
|
||||||
|
.span_builder("Deduplicate and Write")
|
||||||
|
.with_kind(trace::SpanKind::Consumer),
|
||||||
|
&parent_ctx,
|
||||||
|
);
|
||||||
|
let cx = Context::current_with_span(span);
|
||||||
|
cx.span().add_event(
|
||||||
|
"Received Data from Producer",
|
||||||
|
vec![KeyValue::new(
|
||||||
|
"Items to Process",
|
||||||
|
parsed_responses.len().to_string(),
|
||||||
|
)],
|
||||||
|
);
|
||||||
for executions in parsed_responses {
|
for executions in parsed_responses {
|
||||||
if executions.checkpoint.is_empty() || executions.datetime.is_empty() {
|
if executions.checkpoint.is_empty() || executions.datetime.is_empty() {
|
||||||
continue;
|
continue;
|
||||||
@@ -168,11 +183,28 @@ pub fn pull_policy_exec_histories(
|
|||||||
};
|
};
|
||||||
let data_write: String = serde_json::to_string_pretty(&final_response).unwrap();
|
let data_write: String = serde_json::to_string_pretty(&final_response).unwrap();
|
||||||
match fs::write(&writeable_filepath, data_write) {
|
match fs::write(&writeable_filepath, data_write) {
|
||||||
Ok(_) => {}
|
Ok(_) => {
|
||||||
|
cx.span().add_event(
|
||||||
|
"Writing Data to File",
|
||||||
|
vec![KeyValue::new("Success", "Ok".to_string())],
|
||||||
|
);
|
||||||
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("Failed to write to: {:?}: {}", &writeable_filepath, e);
|
cx.span().add_event(
|
||||||
|
"Writing Data to File",
|
||||||
|
vec![KeyValue::new("Failed", e.to_string())],
|
||||||
|
);
|
||||||
|
cx.span()
|
||||||
|
.set_status(Status::error("Failed to Write to File"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
cx.span().add_event(
|
||||||
|
"Finished Deduplicating Data",
|
||||||
|
vec![KeyValue::new(
|
||||||
|
"Items Successfully Processed",
|
||||||
|
seen.len().to_string(),
|
||||||
|
)],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
let mut first_date: Option<NaiveDate> = None;
|
let mut first_date: Option<NaiveDate> = None;
|
||||||
@@ -181,21 +213,42 @@ pub fn pull_policy_exec_histories(
|
|||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.enable_steady_tick(std::time::Duration::from_millis(100));
|
.enable_steady_tick(std::time::Duration::from_millis(100));
|
||||||
|
pb_clone
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.set_message(policy_names.clone().unwrap_or("Statistics".to_string()));
|
||||||
let span: opentelemetry::trace::SpanRef<'_> = cx.span();
|
let span: opentelemetry::trace::SpanRef<'_> = cx.span();
|
||||||
span.set_attribute(KeyValue::new("Days", days.to_string()));
|
span.set_attribute(KeyValue::new("Days", days.to_string()));
|
||||||
span.set_attribute(KeyValue::new(
|
span.set_attribute(KeyValue::new(
|
||||||
"Policy Name",
|
"Policy Name",
|
||||||
policy_names.clone().unwrap_or_default(),
|
policy_names
|
||||||
|
.clone()
|
||||||
|
.unwrap_or("Statistics Monitoring".to_string()),
|
||||||
));
|
));
|
||||||
loop {
|
loop {
|
||||||
let execution_histories = tracer.in_span(checkpoint_number.to_string(), |cx| {
|
let execution_histories = tracer.in_span(checkpoint_number.to_string(), |cx| {
|
||||||
let results: ApiResponse = history_logging(
|
cx.span().add_event(
|
||||||
|
"Retrieving Responses from API",
|
||||||
|
vec![KeyValue::new(
|
||||||
|
"Checkpoint Number",
|
||||||
|
checkpoint_number.to_string(),
|
||||||
|
)],
|
||||||
|
);
|
||||||
|
let results: ApiResponse = rt.block_on(history_logging(
|
||||||
&base_url,
|
&base_url,
|
||||||
&exec_types,
|
&exec_types,
|
||||||
&checkpoint_number,
|
&checkpoint_number,
|
||||||
&policy_names,
|
&policy_names,
|
||||||
&client,
|
&client,
|
||||||
|
));
|
||||||
|
cx.span().add_event(
|
||||||
|
"Got Responses from API",
|
||||||
|
vec![KeyValue::new(
|
||||||
|
"Items in Response",
|
||||||
|
results.response.exechistories.len().to_string(),
|
||||||
|
)],
|
||||||
);
|
);
|
||||||
|
cx.span().set_status(Status::Ok);
|
||||||
cx.span().set_attribute(KeyValue::new(
|
cx.span().set_attribute(KeyValue::new(
|
||||||
"items_in_response",
|
"items_in_response",
|
||||||
results.response.exechistories.len().to_string(),
|
results.response.exechistories.len().to_string(),
|
||||||
@@ -206,7 +259,16 @@ pub fn pull_policy_exec_histories(
|
|||||||
if parsed_responses.is_empty() {
|
if parsed_responses.is_empty() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
tx.send(parsed_responses.clone()).unwrap();
|
match tx.send((cx.span().span_context().clone(), parsed_responses.clone())) {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(e) => {
|
||||||
|
cx.span().add_event(
|
||||||
|
"Failed to Send Items to Processor",
|
||||||
|
vec![KeyValue::new("Response from Processor", e.to_string())],
|
||||||
|
);
|
||||||
|
cx.span().set_status(Status::error("Processor Failed"))
|
||||||
|
}
|
||||||
|
}
|
||||||
checkpoint_number = parsed_responses.last().unwrap().checkpoint.clone();
|
checkpoint_number = parsed_responses.last().unwrap().checkpoint.clone();
|
||||||
if let Some(last_item) = parsed_responses.last()
|
if let Some(last_item) = parsed_responses.last()
|
||||||
&& let Ok(last_date) = NaiveDate::parse_from_str(
|
&& let Ok(last_date) = NaiveDate::parse_from_str(
|
||||||
@@ -255,11 +317,10 @@ fn build_client(headers: HeaderMap) -> Result<reqwest::Client, reqwest::Error> {
|
|||||||
Client::builder()
|
Client::builder()
|
||||||
.danger_accept_invalid_certs(true)
|
.danger_accept_invalid_certs(true)
|
||||||
.default_headers(headers)
|
.default_headers(headers)
|
||||||
.timeout(std::time::Duration::from_secs(300))
|
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tracing::instrument(name = "history_logging")]
|
||||||
async fn history_logging(
|
async fn history_logging(
|
||||||
base_url: &String,
|
base_url: &String,
|
||||||
exec_types: &String,
|
exec_types: &String,
|
||||||
@@ -267,21 +328,28 @@ async fn history_logging(
|
|||||||
policy_names: &Option<String>,
|
policy_names: &Option<String>,
|
||||||
client: &Client,
|
client: &Client,
|
||||||
) -> ApiResponse {
|
) -> ApiResponse {
|
||||||
let payload = json!({
|
let policy_json = match policy_names {
|
||||||
"type": exec_types,
|
Some(name) => format!(r#"[ "{}" ]"#, name), // JSON array with one element
|
||||||
"checkpoint": checkpoint_number,
|
None => "[]".to_string(), // Empty JSON array
|
||||||
"policy": policy_names.as_ref().map(|p| vec![p]),
|
};
|
||||||
});
|
let payload = format!(
|
||||||
|
r#"{{
|
||||||
|
"type": {},
|
||||||
|
"checkpoint": "{}",
|
||||||
|
"policy": {}
|
||||||
|
}}"#,
|
||||||
|
exec_types, checkpoint_number, policy_json
|
||||||
|
);
|
||||||
let res: Result<reqwest::Response, reqwest::Error> = client
|
let res: Result<reqwest::Response, reqwest::Error> = client
|
||||||
.post(format!("{}/v1/logging/exechistories", base_url))
|
.post(format!("{}/v1/logging/exechistories", base_url))
|
||||||
.json(&payload)
|
.body(payload)
|
||||||
.send()
|
.send()
|
||||||
.await;
|
.await;
|
||||||
match res {
|
match res {
|
||||||
Ok(res) => {
|
Ok(res) => {
|
||||||
let first_response: ApiResponse = serde_json::from_str(&res.text().await.unwrap())
|
let first_response: ApiResponse = serde_json::from_str(&res.text().await.unwrap())
|
||||||
.expect("Failed to retrieve response from API");
|
.expect("Failed to retrieve response from API");
|
||||||
return first_response;
|
first_response
|
||||||
}
|
}
|
||||||
Err(_res) => {
|
Err(_res) => {
|
||||||
let failed_response: ApiResponse = ApiResponse {
|
let failed_response: ApiResponse = ApiResponse {
|
||||||
@@ -290,7 +358,7 @@ async fn history_logging(
|
|||||||
exechistories: vec![],
|
exechistories: vec![],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
return failed_response;
|
failed_response
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -357,7 +357,7 @@ Loxide is designed for three primary user classes with varying levels of experti
|
|||||||
|
|
||||||
#### 2.4.3 Network Requirements
|
#### 2.4.3 Network Requirements
|
||||||
|
|
||||||
- Outbound to Airlock server
|
- Outbound HTTPS (port 443) to Airlock server
|
||||||
- Outbound HTTPS to Gitea instance (development only)
|
- Outbound HTTPS to Gitea instance (development only)
|
||||||
- No inbound connections required
|
- No inbound connections required
|
||||||
- Proxy support via standard environment variables
|
- Proxy support via standard environment variables
|
||||||
|
|||||||
+1
-1
@@ -23,4 +23,4 @@ pyperclip==1.11.0
|
|||||||
|
|
||||||
# Custom/Private packages
|
# Custom/Private packages
|
||||||
--extra-index-url https://git.racooncity.org/api/packages/brotoskyj/pypi/simple/
|
--extra-index-url https://git.racooncity.org/api/packages/brotoskyj/pypi/simple/
|
||||||
airlock_libs==6.1.1
|
airlock_libs==7.4.1
|
||||||
@@ -34,7 +34,7 @@ import requests
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
# Current application version - UPDATE THIS ON EACH RELEASE
|
# Current application version - UPDATE THIS ON EACH RELEASE
|
||||||
__version__ = "1.1.0"
|
__version__ = "1.0.0"
|
||||||
|
|
||||||
# Gitea release API configuration
|
# Gitea release API configuration
|
||||||
GITEA_API_BASE = "https://git.racooncity.org/api/v1"
|
GITEA_API_BASE = "https://git.racooncity.org/api/v1"
|
||||||
|
|||||||
Reference in New Issue
Block a user