feat(workforce): add ClickHouse workforce analytics contour

This commit is contained in:
igor04091968
2026-06-23 21:52:57 +03:00
parent 341799ff9d
commit 337da4bb5f
44 changed files with 3973 additions and 461 deletions
@@ -29,7 +29,6 @@ const DEFAULT_DLP_STATE: &str = r"C:\ProgramData\AWatch-rus\dlp-evidence-sync-st
const DEFAULT_DLP_TOKEN: &str = r"C:\ProgramData\AWatch-rus\dlp-evidence-upload-token.txt";
const DEFAULT_REMOTE_ROOT: &str = "/opt/activitywatch/clickhouse-1c/landing";
const DEFAULT_SSH_KEY: &str = r"C:\ProgramData\AWatch-rus\ssh\awops_ed25519";
const DEFAULT_REGISTRY_WORKBOOK: &str = r"E:\USER1\СПИСОК ПРЕДПРИЯТИЙ И ИХ РАСПРЕДЕЛЕНИЕ.xlsx";
#[derive(Parser)]
#[command(about = "AWatch-rus Windows telemetry uploader without PowerShell runtime wrappers")]
@@ -459,8 +458,7 @@ fn run_file1c_upload_inner(args: &mut File1cUpload, log_path: &Path) -> Result<(
if args.registry_workbook_path.is_none() {
args.registry_workbook_path = json_string(automation, &["registryWorkbookPath"])
.filter(|v| !v.trim().is_empty())
.map(PathBuf::from)
.or_else(|| Some(PathBuf::from(DEFAULT_REGISTRY_WORKBOOK)));
.map(PathBuf::from);
}
let scp = system32_path("OpenSSH\\scp.exe");
@@ -1614,6 +1612,10 @@ struct ForegroundWindowContext {
window_handle: isize,
}
fn has_foreground_context(context: &ForegroundWindowContext) -> bool {
context.process_id != 0 || !context.app.trim().is_empty() || !context.title.trim().is_empty()
}
#[derive(Debug, Clone)]
struct WebCategoryRule {
name: String,
@@ -1741,17 +1743,19 @@ fn run_browser_domains_collector(args: BrowserDomainsCollector) -> Result<()> {
loop {
let context = foreground_window_context();
let mut loop_failed = false;
match send_browser_window_event(&runtime, &context) {
Ok(()) => events_sent = events_sent.saturating_add(1),
Err(err) => {
loop_failed = true;
record_collector_send_failure(
&runtime,
&mut problems,
&mut send_failures,
"browser window heartbeat",
&err,
);
if has_foreground_context(&context) {
match send_browser_window_event(&runtime, &context) {
Ok(()) => events_sent = events_sent.saturating_add(1),
Err(err) => {
loop_failed = true;
record_collector_send_failure(
&runtime,
&mut problems,
&mut send_failures,
"browser window heartbeat",
&err,
);
}
}
}
match send_browser_category_health(&runtime, &context, events_sent) {
@@ -5977,7 +5981,7 @@ Connect=File="E:\Bases\Org\Base1";
ID=skip
Connect=Srvr="srv";Ref="x";
"#;
let items = parse_v8i_text(text, "user1", Path::new("ibases.v8i"));
let items = parse_v8i_text(text, "fixture-user", Path::new("ibases.v8i"));
assert_eq!(items.len(), 1);
assert_eq!(items[0].infobase, "База 1");
assert_eq!(items[0].base_id.as_deref(), Some("abc-123"));
@@ -6064,6 +6068,19 @@ Connect=Srvr="srv";Ref="x";
assert!(normalize_browser_url("new tab").is_none());
}
#[test]
fn foreground_context_requires_real_window_signal() {
assert!(!has_foreground_context(&ForegroundWindowContext::default()));
assert!(has_foreground_context(&ForegroundWindowContext {
process_id: 1000,
..ForegroundWindowContext::default()
}));
assert!(has_foreground_context(&ForegroundWindowContext {
title: "1C".to_string(),
..ForegroundWindowContext::default()
}));
}
#[test]
fn dlp_block_is_suppressed_without_native_enforce() {
let policy = dlp_policy_from_value(
@@ -6183,7 +6200,7 @@ SERVICE_NAME: AWatchRusCollectorGuard
#[test]
fn file_operations_queue_token_is_filename_safe() {
let token = queue_name_token(r"DOMAIN\Администратор", 3);
let token = queue_name_token(r"DOMAIN\operator", 3);
assert!(token.ends_with("-s3"));
assert!(!token.contains('\\'));
assert!(
@@ -0,0 +1,18 @@
[package]
name = "aw-workforce-ingest"
version = "0.1.0"
edition.workspace = true
rust-version.workspace = true
license.workspace = true
publish.workspace = true
[dependencies]
anyhow.workspace = true
chrono.workspace = true
clap.workspace = true
detmir-aw-client.workspace = true
reqwest.workspace = true
serde.workspace = true
serde_json.workspace = true
tempfile.workspace = true
urlencoding.workspace = true
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,10 @@
fn main() {
let code = match aw_workforce_ingest::run_from_args() {
Ok(()) => 0,
Err(err) => {
eprintln!("{err:#}");
1
}
};
std::process::exit(code);
}
+4
View File
@@ -286,6 +286,7 @@ fn is_allowed_python_runtime_path(rel: &str) -> bool {
|| rel == "proxmox/tsj_guardian_bot.py"
|| rel == "proxmox/test_tsj_guardian_bot.py"
|| rel == "scripts/package_rust_release_binaries.py"
|| rel == "scripts/public_secret_pattern_check.py"
}
fn is_detmir_retired_runtime_path(rel: &str) -> bool {
@@ -348,6 +349,9 @@ mod tests {
assert!(is_allowed_python_runtime_path(
"scripts/package_rust_release_binaries.py"
));
assert!(is_allowed_python_runtime_path(
"scripts/public_secret_pattern_check.py"
));
}
#[test]