From 48223fbeebfb974cdc9a87a82163bd5e495dcff2 Mon Sep 17 00:00:00 2001 From: igor04091968 Date: Mon, 27 Apr 2026 23:18:32 +0300 Subject: [PATCH] chore(install): sync playbooks and installers with pve audit --- .../provision_proxmox_ct_and_deploy_aw.yml | 3 + ...vision_proxmox_ct_matrix_and_deploy_aw.yml | 3 + aw-server/apply_webui_ru_patch.sh | 22 +- aw-server/install_aw_server.sh | 31 + install-kit-awindows-20260427-211240.tar.gz | Bin 29117 -> 59176 bytes install-kit-awindows-20260427-211240.zip | Bin 41467 -> 80007 bytes .../MANIFEST.txt | 65 +- .../README-INSTALL-KIT.txt | 6 +- .../ansible/deploy_aw_pfsense_poller.yml | 66 + .../ansible/deploy_aw_server.yml | 229 +++ .../ansible/group_vars/all.example.yml | 24 + .../group_vars/pfsense-poller.example.yml | 30 + .../group_vars/proxmox-matrix.example.yml | 38 + .../ansible/group_vars/proxmox.example.yml | 18 + .../{ => group_vars}/windows.example.yml | 0 .../ansible/inventory.example.ini | 8 + .../provision_proxmox_ct_and_deploy_aw.yml | 40 + ...vision_proxmox_ct_matrix_and_deploy_aw.yml | 50 + .../tasks/provision_ct_and_deploy_aw.yml | 217 +++ .../aw-server/activitywatch-server.service | 24 + .../aw-server/apply_webui_ru_patch.sh | 88 + .../aw-server/aw-host-groups.json | 47 + .../aw-server/aw-ru-patch.js | 1518 +++++++++++++++++ .../aw-server/aw-server.env.example | 11 + .../aw-server/aw-sw-cleanup.js | 18 + .../aw-server/install_aw_server.sh | 117 ++ .../aw-server/settings/classes-worktime.json | 90 + .../aw-server/settings/views-default.json | 39 + proxmox/push-aw-artifacts.sh | 7 +- 29 files changed, 2769 insertions(+), 40 deletions(-) create mode 100644 install-kit-awindows-20260427-211240/ansible/deploy_aw_pfsense_poller.yml create mode 100644 install-kit-awindows-20260427-211240/ansible/deploy_aw_server.yml create mode 100644 install-kit-awindows-20260427-211240/ansible/group_vars/all.example.yml create mode 100644 install-kit-awindows-20260427-211240/ansible/group_vars/pfsense-poller.example.yml create mode 100644 install-kit-awindows-20260427-211240/ansible/group_vars/proxmox-matrix.example.yml create mode 100644 install-kit-awindows-20260427-211240/ansible/group_vars/proxmox.example.yml rename install-kit-awindows-20260427-211240/ansible/{ => group_vars}/windows.example.yml (100%) create mode 100644 install-kit-awindows-20260427-211240/ansible/inventory.example.ini create mode 100644 install-kit-awindows-20260427-211240/ansible/provision_proxmox_ct_and_deploy_aw.yml create mode 100644 install-kit-awindows-20260427-211240/ansible/provision_proxmox_ct_matrix_and_deploy_aw.yml create mode 100644 install-kit-awindows-20260427-211240/ansible/tasks/provision_ct_and_deploy_aw.yml create mode 100755 install-kit-awindows-20260427-211240/aw-server/activitywatch-server.service create mode 100755 install-kit-awindows-20260427-211240/aw-server/apply_webui_ru_patch.sh create mode 100644 install-kit-awindows-20260427-211240/aw-server/aw-host-groups.json create mode 100755 install-kit-awindows-20260427-211240/aw-server/aw-ru-patch.js create mode 100755 install-kit-awindows-20260427-211240/aw-server/aw-server.env.example create mode 100755 install-kit-awindows-20260427-211240/aw-server/aw-sw-cleanup.js create mode 100755 install-kit-awindows-20260427-211240/aw-server/install_aw_server.sh create mode 100644 install-kit-awindows-20260427-211240/aw-server/settings/classes-worktime.json create mode 100644 install-kit-awindows-20260427-211240/aw-server/settings/views-default.json diff --git a/ansible/provision_proxmox_ct_and_deploy_aw.yml b/ansible/provision_proxmox_ct_and_deploy_aw.yml index 0c08284..2a932a7 100644 --- a/ansible/provision_proxmox_ct_and_deploy_aw.yml +++ b/ansible/provision_proxmox_ct_and_deploy_aw.yml @@ -12,6 +12,9 @@ - aw-server.env.example - aw-ru-patch.js - aw-sw-cleanup.js + - aw-host-groups.json + - settings/classes-worktime.json + - settings/views-default.json tasks: - name: Execute single-CT provisioning workflow diff --git a/ansible/provision_proxmox_ct_matrix_and_deploy_aw.yml b/ansible/provision_proxmox_ct_matrix_and_deploy_aw.yml index d343ad9..b228f66 100644 --- a/ansible/provision_proxmox_ct_matrix_and_deploy_aw.yml +++ b/ansible/provision_proxmox_ct_matrix_and_deploy_aw.yml @@ -12,6 +12,9 @@ - aw-server.env.example - aw-ru-patch.js - aw-sw-cleanup.js + - aw-host-groups.json + - settings/classes-worktime.json + - settings/views-default.json tasks: - name: Validate CT matrix is provided diff --git a/aw-server/apply_webui_ru_patch.sh b/aw-server/apply_webui_ru_patch.sh index d35c71e..2d37b7b 100755 --- a/aw-server/apply_webui_ru_patch.sh +++ b/aw-server/apply_webui_ru_patch.sh @@ -9,13 +9,16 @@ fi source "$ENV_FILE" -WEBUI_DIR="${AW_SERVER_WEBUI_DIR:-/opt/activitywatch/webui-ru}" +WEBUI_DIR="${AW_SERVER_WEBUI_DIR:-${AW_WEBUI_DIR:-/opt/activitywatch/webui-ru}}" PATCH_JS_SRC="/root/bootstrap/aw-ru-patch.js" SW_CLEANUP_SRC="/root/bootstrap/aw-sw-cleanup.js" HOST_GROUPS_SRC="/root/bootstrap/aw-host-groups.json" INDEX_HTML="$WEBUI_DIR/index.html" SERVICE_WORKER="$WEBUI_DIR/service-worker.js" TS=$(date +%Y%m%d%H%M%S) +PATCH_TARGET="$WEBUI_DIR/js/ru-patch-v5.js" +SW_TARGET="$WEBUI_DIR/js/sw-cleanup.js" +HOST_GROUPS_TARGET="$WEBUI_DIR/js/aw-host-groups.json" TRENDS_NEEDLE='this.activityStore.query_category_time_by_period(r)' TRENDS_REPLACEMENT='this.activityStore.ensure_loaded(r)' TIMESPIRAL_NEEDLE='start:new Date("2022-08-08")' @@ -27,14 +30,17 @@ TIMESPIRAL_REPLACEMENT='start:new Date(Date.now()-12*36e5)' [[ -f "$INDEX_HTML" ]] || { echo "missing $INDEX_HTML" >&2; exit 1; } install -d "$WEBUI_DIR/js" -install -m 0644 "$PATCH_JS_SRC" "$WEBUI_DIR/js/aw-ru-patch.js" -install -m 0644 "$SW_CLEANUP_SRC" "$WEBUI_DIR/js/aw-sw-cleanup.js" -install -m 0644 "$HOST_GROUPS_SRC" "$WEBUI_DIR/js/aw-host-groups.json" +install -m 0644 "$PATCH_JS_SRC" "$PATCH_TARGET" +install -m 0644 "$SW_CLEANUP_SRC" "$SW_TARGET" +install -m 0644 "$HOST_GROUPS_SRC" "$HOST_GROUPS_TARGET" cp "$INDEX_HTML" "$INDEX_HTML.bak.$TS" -sed -i '/aw-ru-patch.js/d;/aw-sw-cleanup.js/d' "$INDEX_HTML" -sed -i 's###' "$INDEX_HTML" -sed -i 's###' "$INDEX_HTML" +patch_hash="$(sha1sum "$PATCH_TARGET" | awk '{print substr($1,1,12)}')" +sw_hash="$(sha1sum "$SW_TARGET" | awk '{print substr($1,1,12)}')" + +sed -i '/ru-patch-v5.js/d;/sw-cleanup.js/d;/aw-ru-patch.js/d;/aw-sw-cleanup.js/d' "$INDEX_HTML" +sed -i "s###" "$INDEX_HTML" +sed -i "s###" "$INDEX_HTML" cp "$SW_CLEANUP_SRC" "$SERVICE_WORKER" trends_chunk="$(grep -Rsl "$TRENDS_NEEDLE" "$WEBUI_DIR/js"/*.js 2>/dev/null | head -n 1 || true)" @@ -79,4 +85,4 @@ else echo "Timespiral hotfix skipped: chunk not found" fi -echo "RU patch applied to $WEBUI_DIR" +echo "RU patch applied to $WEBUI_DIR (ru-patch-v5.js?v=$patch_hash)" diff --git a/aw-server/install_aw_server.sh b/aw-server/install_aw_server.sh index fa2dc58..4d6262e 100755 --- a/aw-server/install_aw_server.sh +++ b/aw-server/install_aw_server.sh @@ -21,6 +21,10 @@ required_vars=( AW_SERVER_GROUP ) +BOOTSTRAP_DIR="/root/bootstrap" +VIEWS_JSON="$BOOTSTRAP_DIR/settings/views-default.json" +CLASSES_JSON="$BOOTSTRAP_DIR/settings/classes-worktime.json" + for var_name in "${required_vars[@]}"; do if [[ -z "${!var_name:-}" ]]; then echo "missing required variable: $var_name" >&2 @@ -84,3 +88,30 @@ systemctl daemon-reload systemctl enable activitywatch-server.service systemctl restart activitywatch-server.service systemctl --no-pager --full status activitywatch-server.service || true + +for _ in $(seq 1 20); do + if curl -fsS "http://127.0.0.1:${AW_SERVER_PORT}/api/0/info" >/dev/null 2>&1; then + break + fi + sleep 2 +done + +if [[ -f "$CLASSES_JSON" ]]; then + curl -fsS -X POST \ + -H 'Content-Type: application/json' \ + --data-binary @"$CLASSES_JSON" \ + "http://127.0.0.1:${AW_SERVER_PORT}/api/0/settings/classes" >/dev/null + echo "Applied worktime classes from $CLASSES_JSON" +else + echo "Worktime classes bootstrap not found, skipped: $CLASSES_JSON" +fi + +if [[ -f "$VIEWS_JSON" ]]; then + curl -fsS -X POST \ + -H 'Content-Type: application/json' \ + --data-binary @"$VIEWS_JSON" \ + "http://127.0.0.1:${AW_SERVER_PORT}/api/0/settings/views" >/dev/null + echo "Applied baseline views from $VIEWS_JSON" +else + echo "Views bootstrap not found, skipped: $VIEWS_JSON" +fi diff --git a/install-kit-awindows-20260427-211240.tar.gz b/install-kit-awindows-20260427-211240.tar.gz index 278a3e182b2a7dfe0321035709f91064c2bf8ac7..7c7680fdb4fc4da251ea1b068ae65bbb44a34b21 100644 GIT binary patch literal 59176 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 g-N|2<5AIKV|2VvI+t6f1`T{V*mgE literal 29117 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 z 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 E51IeD7XSbN delta 1434 zcmZqw$nyIz6HkCQGYc032rO7Rk;jHrXZicg-02QVj3OHo*Ki3=SvfmKy>0gTgFpqs zj0_CclRu_uOm^dzW|CMwd7`3deQe)GKW0M#*6RAB(^wA9b@3F})Kv0t?wYc^ckh#) zYjLM{pDV2QcUtFA^4@dt#Wj16dT;sJ@yz|kgty79$!8qZT4TeU(<-{3-O;PgYy5K6 z_HYUNFZT+LkchN^_+-OcCjB!{d|$oy^E6W7?0s4GIo&4P|NacF$?MjsosshQ`5PN+ z{J=!#-NK#~UoM^P{Gae#Yb}rXnp^rY+i(19l)wEoZU5V#f_DmQt9GUC?{;qsdipww zW7ftO8f+N_SreHfe6H-22ZewL(0d#V$AF$SJHn*M$i%?#oP&YE5bW7wJc9Mbz)%6< z(h6<{MwS=M3=Cl6=oH`lM+O3I?|+K!UH|6BId;Av{=kJ%A-9|zUkO@%W7+fiMxe}s zFW1*|UtITXo6y4QlLw7I?^&O2QyI0!eDl%Qd$;WqKek~0F84<())u+gV?=(e_;+9F z>G_MMTVHuw`$s?OWAV>T6?4{K+fo01eJbni&Ce~P_N=+~w5W5Q*y4cJue*#4E)*_4 z#-x?eYn^+f&^RUU*4z&VKCCoM&g3cNdA}%=x2N&j1=$78mu^Po+b&`~Z1Q}DP*c5R zuYS0Llj*HTr+K9}{GHHOUy;85$borpH~(KHzv)FU56|;L&Fb0v6tGXf@MUshhYzJ=>#1OJ@h6=o0qzYE;*D{hm{e7Dk5-8RB6Qx2{4iBRNZpLW?~ zxzn;a1(O0ijAmXvaB0`0l?kS-ifXLWwK!Jq-u+tUaa>8oZ$ZY^T?(bso_85XT~u11 zxh~7mLe=!V%;pvk&s9yDvouZ$M{+z^^o!+WijY$H?;jU(zi>}JI7w>qzAnw_0xFCg zoAX2+N|~mtoV=mS1)4=B-r<@o+8sQ3r?l|q>TWM~aY$l|Ts=F6Nxsh02AJ45Ss54{ zCSROuAXlE5my%yzte0Amm|KvOs#lqt6B@$Fz?`}2eP-sW&C4f;GckrwuGx~vmI@R} zUA6h~mS3zqj7)OOuuMEXQ=ZX_Q3sTTC8h@`GqP>JB+uBx#EX)Xr)MiMhEDz{Dm?vx zDkF~qM#5*10F?*~zzol@rO}dSdY}fQ`gApAMnT46xWOt+OZh+&`qP&xGdeNx@PjyJ z)8$nd{g^I@fH{*FNDEEhuEOZRv|0+HR6~_9f~iCq#4(s`rzJ7{lqyiRSRE`o*+x$i zq+D%!l^UZIlZnpsLQO`$=}*)cU6{7GL-f@7DNnCe2kMDPfSCABoiUN=Vj_ss&Xki5 z5#-clOk?`i2-07o0P;E5J5WLw>N^Go0fxVhYAw?P^%(V8Lqk{@m|;O8Fg->K=z?B| z-1OyIjOx=A8QBytA`K;=oO{4VO<$wUD8ZDuYPzEmqby_i^e#h2S*D+T(-{pIEhit_ cBEkx^C>1O#EW*kLa+Ej_Ht{epBu@qL0E5;sZ~y=R diff --git a/install-kit-awindows-20260427-211240/MANIFEST.txt b/install-kit-awindows-20260427-211240/MANIFEST.txt index 9569ca6..7513f6f 100644 --- a/install-kit-awindows-20260427-211240/MANIFEST.txt +++ b/install-kit-awindows-20260427-211240/MANIFEST.txt @@ -1,28 +1,37 @@ -KIT_DIR=install-kit-awindows-20260427-211240 -CREATED_AT=2026-04-27T21:14:11+03:00 - -FILES: -/home/igor/tmp/AWatch-rus/install-kit-awindows-20260427-211240/MANIFEST.txt -/home/igor/tmp/AWatch-rus/install-kit-awindows-20260427-211240/README-INSTALL-KIT.txt -/home/igor/tmp/AWatch-rus/install-kit-awindows-20260427-211240/ansible/README.md -/home/igor/tmp/AWatch-rus/install-kit-awindows-20260427-211240/ansible/deploy_aw_windows_phase2.yml -/home/igor/tmp/AWatch-rus/install-kit-awindows-20260427-211240/ansible/windows.example.yml -/home/igor/tmp/AWatch-rus/install-kit-awindows-20260427-211240/server-configs-192.168.100.21/phase2-admin.deployment-config.json -/home/igor/tmp/AWatch-rus/install-kit-awindows-20260427-211240/server-configs-192.168.100.21/phase2-u2u5.deployment-config.json -/home/igor/tmp/AWatch-rus/install-kit-awindows-20260427-211240/server-configs-192.168.100.21/phase2-user1.deployment-config.json -/home/igor/tmp/AWatch-rus/install-kit-awindows-20260427-211240/windows/ActivityWatch.Windows.Common.psd1 -/home/igor/tmp/AWatch-rus/install-kit-awindows-20260427-211240/windows/ActivityWatch.Windows.Common.psm1 -/home/igor/tmp/AWatch-rus/install-kit-awindows-20260427-211240/windows/browser-domains-native-collector.ps1 -/home/igor/tmp/AWatch-rus/install-kit-awindows-20260427-211240/windows/deploy-domain-users.ps1 -/home/igor/tmp/AWatch-rus/install-kit-awindows-20260427-211240/windows/deploy-ensemble.ps1 -/home/igor/tmp/AWatch-rus/install-kit-awindows-20260427-211240/windows/deploy-single-user.ps1 -/home/igor/tmp/AWatch-rus/install-kit-awindows-20260427-211240/windows/dlp-endpoint-signals-collector.ps1 -/home/igor/tmp/AWatch-rus/install-kit-awindows-20260427-211240/windows/dlp-policy.example.json -/home/igor/tmp/AWatch-rus/install-kit-awindows-20260427-211240/windows/hardening-recovery.ps1 -/home/igor/tmp/AWatch-rus/install-kit-awindows-20260427-211240/windows/validate-deployment.ps1 -/home/igor/tmp/AWatch-rus/install-kit-awindows-20260427-211240/windows/web-category-rules.example.json - - -SHA256: -40bdf651a876f1f545c06c9191ff5d9ec3ca1ce75d3edf1d647e3e2fbafece52 /home/igor/tmp/AWatch-rus/install-kit-awindows-20260427-211240.tar.gz -e7e020bff342070d55d2489d9a5e40822543a518863a97cd46865ccf00f64399 /home/igor/tmp/AWatch-rus/install-kit-awindows-20260427-211240.zip +6e1f304f468d77f12df67face6afaaca5bbcfb1496f43df4e4fa0557cf847829 install-kit-awindows-20260427-211240/README-INSTALL-KIT.txt +a11f41827769be915f73d0de2c5503b05f61ccf56f70a50845771bcb79c5ebb7 install-kit-awindows-20260427-211240/ansible/README.md +02ca96f5ecc6abf89ab3271bd08add5795dbba2281168f158d96166f557e33f0 install-kit-awindows-20260427-211240/ansible/deploy_aw_pfsense_poller.yml +7c1ad9363412e802f4272f2e91a1d9f26be722eaf22e654c0a2512b0cebbbfd0 install-kit-awindows-20260427-211240/ansible/deploy_aw_server.yml +d5c42e6fe49c14a0769517ff28184139e467d40f22d4a632630c42ed1ff34ce5 install-kit-awindows-20260427-211240/ansible/deploy_aw_windows_phase2.yml +bc791462b9c00adc8c68ed81e2a7697c560bdbc46f156b4840c7bca1dee2157d install-kit-awindows-20260427-211240/ansible/group_vars/all.example.yml +95696c243ab331f06e77a40a9800c4b6668de77675ebbdf2ef54ae49e1b18874 install-kit-awindows-20260427-211240/ansible/group_vars/pfsense-poller.example.yml +c5cab36645065815571c99f6d360f910dcccbb54b780c8bfd526a6cdc3684e19 install-kit-awindows-20260427-211240/ansible/group_vars/proxmox-matrix.example.yml +35a33c8a1c75ded5e85c6b79e0b3efde07959ff61ee5f66d83b7e0c2abe87fc5 install-kit-awindows-20260427-211240/ansible/group_vars/proxmox.example.yml +69368b7adb7711fa81304866373e61ed464bcc23a08e4509fa54655b05f95790 install-kit-awindows-20260427-211240/ansible/group_vars/windows.example.yml +00064ce5187569fb3221ddd45c2ad7eb37cd50b2a0a832ddf7843e4ff461849a install-kit-awindows-20260427-211240/ansible/inventory.example.ini +bbef175cb77dd53aa07452dbb2fe8797f38b58f42372005c3404c8dc9d6f8e13 install-kit-awindows-20260427-211240/ansible/provision_proxmox_ct_and_deploy_aw.yml +b8f8b6bc504a51cd87db3f46c35a27295b395ea516533068f96af35f8b720434 install-kit-awindows-20260427-211240/ansible/provision_proxmox_ct_matrix_and_deploy_aw.yml +d35bc97b6de18f0006cbbad4adf8a8a5db8ed912997d8fc47c7f11fa9247e907 install-kit-awindows-20260427-211240/ansible/tasks/provision_ct_and_deploy_aw.yml +a50dbadbf619342c2178e255b68f69a36503756daf80eedd9170311c63964f2e install-kit-awindows-20260427-211240/aw-server/activitywatch-server.service +1ef77d865937a8fac15e47fd95b046fc713306f7e0b5a386a9dadd1d4c6ad0be install-kit-awindows-20260427-211240/aw-server/apply_webui_ru_patch.sh +07d4e583f6e9757a11f01558e1f15cfd73c4d82695f1768204f2f50621712168 install-kit-awindows-20260427-211240/aw-server/aw-host-groups.json +76d9d3f3b1984fb2d668bc3bab6bb2a6300c0e5e2cae1b123da7237ca13484e3 install-kit-awindows-20260427-211240/aw-server/aw-ru-patch.js +7c5952f8f0a8590e849ea8381bfcd7059b138250bca8551bd5625f395eb66cd8 install-kit-awindows-20260427-211240/aw-server/aw-server.env.example +98c0bed353bbda0fa7a69df23f3b008cb0e8e70cdff6cc63330d4caf79fd3280 install-kit-awindows-20260427-211240/aw-server/aw-sw-cleanup.js +dce731fdfdcfd773c154d12dbd6b9e621a0bff17ced5a05a65f0fdcb1adcb70f install-kit-awindows-20260427-211240/aw-server/install_aw_server.sh +1856e9f44636030b0cb9ece37ba2a0618eb5187fa82c7969976c1bb5f10fc622 install-kit-awindows-20260427-211240/aw-server/settings/classes-worktime.json +ff07b90cb6a7f09b27d522307cf55b0359e136a2e695190b8564e859f14f9204 install-kit-awindows-20260427-211240/aw-server/settings/views-default.json +59307d284caa74eb3dc129765f9db93b6e8dfd5b1d960b98f347a332b23f82dc install-kit-awindows-20260427-211240/server-configs-192.168.100.21/phase2-admin.deployment-config.json +f2cee1872bf274f15dcfb8fb595fb20a11d228a4bae0930dc6cb918a6f800756 install-kit-awindows-20260427-211240/server-configs-192.168.100.21/phase2-u2u5.deployment-config.json +6aefedcdac8c1d3823c9f4065b051a67a221a3e9424c913a673667b2a23ea1e7 install-kit-awindows-20260427-211240/server-configs-192.168.100.21/phase2-user1.deployment-config.json +33aa34b89246d6c079ef9afe2f5cd153bd9d5946b69a175ff6fd678c77f61da5 install-kit-awindows-20260427-211240/windows/ActivityWatch.Windows.Common.psd1 +d506614168227fa01fa481289079b432b6d8846d5ee8961cff8c21fd0bf7ea8f install-kit-awindows-20260427-211240/windows/ActivityWatch.Windows.Common.psm1 +2a0b94ddad43a6bc684037243e636a54c168d8d4ad25b29778c4d78180da2532 install-kit-awindows-20260427-211240/windows/browser-domains-native-collector.ps1 +98bfcf5dca972f1ba1845bbca546133c192824ec40dd2e55f4e6d59c24a834d1 install-kit-awindows-20260427-211240/windows/deploy-domain-users.ps1 +a19c2a98e6483eb921457f472d1cf62a76e344f2ef3621f04fafa3d7dc2df353 install-kit-awindows-20260427-211240/windows/deploy-ensemble.ps1 +2542425e02acd8a8b02ed701ca2382cf7ae612be28441d1a10a40e48c6a13ad0 install-kit-awindows-20260427-211240/windows/deploy-single-user.ps1 +a2f963927c8b263a21aaffc0a926a058dcec65a04a5fa57c271d3dbe59f9347c install-kit-awindows-20260427-211240/windows/dlp-endpoint-signals-collector.ps1 +aef0032edd9b1e0c54f7b575664ed511dfc6cb53364e7496cbc95e137678e11a install-kit-awindows-20260427-211240/windows/dlp-policy.example.json +ade74a55ce00d9295f2efa0fd72f987688154c93f1142ce0eb6e982f07271be7 install-kit-awindows-20260427-211240/windows/hardening-recovery.ps1 +88ffe06093ef5f7247bd2b990b0c8f801c706a26dc87725bb1194504fab7e306 install-kit-awindows-20260427-211240/windows/validate-deployment.ps1 +731098681d89b9af6f3872abd586ac3b1faba2d7f9340211e503f52ad0243b3f install-kit-awindows-20260427-211240/windows/web-category-rules.example.json diff --git a/install-kit-awindows-20260427-211240/README-INSTALL-KIT.txt b/install-kit-awindows-20260427-211240/README-INSTALL-KIT.txt index d09d6bb..204ed7a 100644 --- a/install-kit-awindows-20260427-211240/README-INSTALL-KIT.txt +++ b/install-kit-awindows-20260427-211240/README-INSTALL-KIT.txt @@ -2,9 +2,9 @@ ActivityWatch DetMir Windows Install Kit Includes: - windows/* (deploy scripts, collectors, common module, configs/examples) -- ansible/deploy_aw_windows_phase2.yml -- ansible/group_vars/windows.example.yml -- ansible/README.md +- ansible/* (Windows and AW server playbooks, examples, inventory, tasks) +- aw-server/* (server installer, RU patch loader, host groups, default settings) +- server-configs-192.168.100.21/* (working Windows Phase2 config snapshots) Source: - Local project snapshot at build time. diff --git a/install-kit-awindows-20260427-211240/ansible/deploy_aw_pfsense_poller.yml b/install-kit-awindows-20260427-211240/ansible/deploy_aw_pfsense_poller.yml new file mode 100644 index 0000000..3eea27c --- /dev/null +++ b/install-kit-awindows-20260427-211240/ansible/deploy_aw_pfsense_poller.yml @@ -0,0 +1,66 @@ +--- +- name: Deploy pfSense ActivityWatch poller + hosts: aw_pfsense_pollers + become: true + gather_facts: true + + vars: + aw_pfsense_install_root: "/opt/aw-pfsense" + aw_pfsense_config_dir: "/etc/aw-pfsense" + aw_pfsense_service_name: "aw-pfsense-poller.service" + + tasks: + - name: Install required packages + ansible.builtin.apt: + name: + - python3 + state: present + update_cache: true + + - name: Ensure directories exist + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + loop: + - "{{ aw_pfsense_install_root }}" + - "{{ aw_pfsense_config_dir }}" + + - name: Install pfSense poller script + ansible.builtin.copy: + src: "{{ aw_repo_root }}/pfsense/pfsense-aw-poller.py" + dest: "{{ aw_pfsense_install_root }}/pfsense-aw-poller.py" + mode: "0755" + + - name: Install systemd service + ansible.builtin.copy: + src: "{{ aw_repo_root }}/pfsense/pfsense-aw-poller.service" + dest: "/etc/systemd/system/{{ aw_pfsense_service_name }}" + mode: "0644" + notify: + - Reload systemd + + - name: Write pfSense poller config + ansible.builtin.copy: + dest: "{{ aw_pfsense_config_dir }}/poller.json" + mode: "0600" + content: "{{ aw_pfsense_poller_config | to_nice_json }}" + notify: + - Restart pfSense poller + + - name: Enable and start pfSense poller + ansible.builtin.systemd: + name: "{{ aw_pfsense_service_name }}" + enabled: true + state: restarted + daemon_reload: true + + handlers: + - name: Reload systemd + ansible.builtin.systemd: + daemon_reload: true + + - name: Restart pfSense poller + ansible.builtin.systemd: + name: "{{ aw_pfsense_service_name }}" + state: restarted diff --git a/install-kit-awindows-20260427-211240/ansible/deploy_aw_server.yml b/install-kit-awindows-20260427-211240/ansible/deploy_aw_server.yml new file mode 100644 index 0000000..d5f7fda --- /dev/null +++ b/install-kit-awindows-20260427-211240/ansible/deploy_aw_server.yml @@ -0,0 +1,229 @@ +--- +- name: Deploy AWatch-rus server + hosts: aw_server + become: true + gather_facts: true + + vars: + aw_release_root: "/opt/activitywatch/releases" + aw_release_dir: "{{ aw_release_root }}/{{ aw_server_version }}" + aw_archive_path: "/tmp/activitywatch-{{ aw_server_version }}.zip" + aw_bootstrap_dir: "/tmp/aw-rus-bootstrap" + aw_ru_patch_cache_bust: "{{ lookup('file', aw_repo_root + '/aw-server/aw-ru-patch.js') | hash('sha1') | truncate(12, true, '') }}" + aw_sw_cleanup_cache_bust: "{{ lookup('file', aw_repo_root + '/aw-server/aw-sw-cleanup.js') | hash('sha1') | truncate(12, true, '') }}" + aw_host_groups_cache_bust: "{{ lookup('file', aw_repo_root + '/aw-server/aw-host-groups.json') | hash('sha1') | truncate(12, true, '') }}" + aw_worktime_classes: "{{ lookup('file', aw_repo_root + '/aw-server/settings/classes-worktime.json') | from_json }}" + aw_default_views: "{{ lookup('file', aw_repo_root + '/aw-server/settings/views-default.json') | from_json }}" + + tasks: + - name: Install base packages + ansible.builtin.apt: + name: + - curl + - unzip + state: present + update_cache: true + + - name: Ensure service account exists + ansible.builtin.user: + name: "{{ aw_server_user }}" + group: "{{ aw_server_group }}" + home: "{{ aw_server_data_dir }}" + shell: /usr/sbin/nologin + system: true + create_home: false + + - name: Ensure required directories + ansible.builtin.file: + path: "{{ item }}" + state: directory + owner: "{{ aw_server_user }}" + group: "{{ aw_server_group }}" + mode: "0755" + loop: + - "{{ aw_release_root }}" + - "{{ aw_release_dir }}" + - "{{ aw_server_webui_dir }}" + - "{{ aw_server_data_dir }}" + - "{{ aw_server_log_dir }}" + - /etc/activitywatch + - "{{ aw_bootstrap_dir }}" + + - name: Download ActivityWatch release archive + ansible.builtin.get_url: + url: "{{ aw_server_download_url }}" + dest: "{{ aw_archive_path }}" + mode: "0644" + + - name: Unpack ActivityWatch release + ansible.builtin.unarchive: + src: "{{ aw_archive_path }}" + dest: "{{ aw_release_dir }}" + remote_src: true + extra_opts: ["-o"] + + - name: Discover extracted AW directory + ansible.builtin.find: + paths: "{{ aw_release_dir }}" + file_type: directory + patterns: "activitywatch*" + register: aw_release_find + + - name: Set release extracted path + ansible.builtin.set_fact: + aw_release_extracted: "{{ (aw_release_find.files | sort(attribute='path') | map(attribute='path') | list | first) }}" + + - name: Verify extracted directory exists + ansible.builtin.assert: + that: + - aw_release_extracted is defined + - aw_release_extracted | length > 0 + fail_msg: "Cannot locate extracted ActivityWatch release directory." + + - name: Sync release content to /opt/activitywatch + ansible.builtin.command: + cmd: "rsync -a --delete {{ aw_release_extracted }}/ /opt/activitywatch/" + + - name: Copy bootstrap files from repository + ansible.builtin.copy: + src: "{{ item.src }}" + dest: "{{ item.dest }}" + mode: "{{ item.mode }}" + loop: + - { src: "{{ aw_repo_root }}/aw-server/activitywatch-server.service", dest: "/etc/systemd/system/activitywatch-server.service", mode: "0644" } + - { src: "{{ aw_repo_root }}/aw-server/aw-ru-patch.js", dest: "{{ aw_server_webui_dir }}/js/ru-patch-v5.js", mode: "0644" } + - { src: "{{ aw_repo_root }}/aw-server/aw-sw-cleanup.js", dest: "{{ aw_server_webui_dir }}/js/sw-cleanup.js", mode: "0644" } + - { src: "{{ aw_repo_root }}/aw-server/aw-host-groups.json", dest: "{{ aw_server_webui_dir }}/js/aw-host-groups.json", mode: "0644" } + notify: + - Reload systemd + - Restart activitywatch + + - name: Copy WebUI index template from installed distribution + ansible.builtin.copy: + remote_src: true + src: "/opt/activitywatch/aw-webui/index.html" + dest: "{{ aw_server_webui_dir }}/index.html" + mode: "0644" + + - name: Insert RU patch scripts into index.html + ansible.builtin.replace: + path: "{{ aw_server_webui_dir }}/index.html" + regexp: '' + replace: '' + + - name: Insert RU patch loader before body end + ansible.builtin.replace: + path: "{{ aw_server_webui_dir }}/index.html" + regexp: '' + replace: '' + + - name: Write /etc/activitywatch/aw-server.env + ansible.builtin.copy: + dest: /etc/activitywatch/aw-server.env + mode: "0640" + content: | + AW_SERVER_HOST={{ aw_server_bind_host }} + AW_SERVER_PORT={{ aw_server_port }} + AW_DATA_DIR={{ aw_server_data_dir }} + AW_LOG_DIR={{ aw_server_log_dir }} + AW_WEBUI_DIR={{ aw_server_webui_dir }} + AW_SERVER_USER={{ aw_server_user }} + AW_SERVER_GROUP={{ aw_server_group }} + + - name: Enable and start service + ansible.builtin.systemd: + name: activitywatch-server.service + enabled: true + state: restarted + daemon_reload: true + + - name: Wait for API + ansible.builtin.uri: + url: "http://127.0.0.1:{{ aw_server_port }}/api/0/info" + method: GET + status_code: 200 + register: aw_api + retries: 10 + delay: 3 + until: aw_api.status == 200 + + - name: Apply baseline worktime settings (classes) + ansible.builtin.uri: + url: "http://127.0.0.1:{{ aw_server_port }}/api/0/settings/classes" + method: POST + body: "{{ aw_worktime_classes }}" + body_format: json + status_code: 201 + when: aw_apply_worktime_settings | default(false) | bool + + - name: Apply baseline views (include DLP and worktime) + ansible.builtin.uri: + url: "http://127.0.0.1:{{ aw_server_port }}/api/0/settings/views" + method: POST + body: "{{ aw_default_views }}" + body_format: json + status_code: 201 + when: aw_apply_worktime_settings | default(false) | bool + + - name: Derive worktime durationDefault from aw_worktime_from/to + ansible.builtin.set_fact: + aw_worktime_from_h: "{{ (aw_worktime_from | default('08:00')).split(':')[0] | int }}" + aw_worktime_from_m: "{{ (aw_worktime_from | default('08:00')).split(':')[1] | int }}" + aw_worktime_to_h: "{{ (aw_worktime_to | default('17:00')).split(':')[0] | int }}" + aw_worktime_to_m: "{{ (aw_worktime_to | default('17:00')).split(':')[1] | int }}" + aw_worktime_duration_default_derived: >- + {{ + ( + ( + ((aw_worktime_to_h | int) * 60 + (aw_worktime_to_m | int)) - + ((aw_worktime_from_h | int) * 60 + (aw_worktime_from_m | int)) + ) * 60 + ) + }} + when: aw_apply_worktime_settings | default(false) | bool + + - name: Normalize derived durationDefault for overnight shifts + ansible.builtin.set_fact: + aw_worktime_duration_default_effective: >- + {{ + (aw_worktime_duration_default_derived | int) + if (aw_worktime_duration_default_derived | int) > 0 + else ((aw_worktime_duration_default_derived | int) + 86400) + }} + when: aw_apply_worktime_settings | default(false) | bool + + - name: Validate derived durationDefault is sane + ansible.builtin.assert: + that: + - aw_worktime_duration_default_effective | int > 0 + - aw_worktime_duration_default_effective | int <= 86400 + fail_msg: "Invalid worktime window: {{ aw_worktime_from }}..{{ aw_worktime_to }}" + when: aw_apply_worktime_settings | default(false) | bool + + - name: Apply baseline worktime period (startOfDay) + ansible.builtin.uri: + url: "http://127.0.0.1:{{ aw_server_port }}/api/0/settings/startOfDay" + method: POST + body: "{{ aw_worktime_start_of_day }}" + body_format: json + status_code: 200 + when: aw_apply_worktime_settings | default(false) | bool + + - name: Apply baseline worktime period (durationDefault seconds) + ansible.builtin.uri: + url: "http://127.0.0.1:{{ aw_server_port }}/api/0/settings/durationDefault" + method: POST + body: "{{ aw_worktime_duration_default_effective }}" + body_format: json + status_code: 200 + when: aw_apply_worktime_settings | default(false) | bool + + handlers: + - name: Reload systemd + ansible.builtin.systemd: + daemon_reload: true + + - name: Restart activitywatch + ansible.builtin.systemd: + name: activitywatch-server.service + state: restarted diff --git a/install-kit-awindows-20260427-211240/ansible/group_vars/all.example.yml b/install-kit-awindows-20260427-211240/ansible/group_vars/all.example.yml new file mode 100644 index 0000000..8a41775 --- /dev/null +++ b/install-kit-awindows-20260427-211240/ansible/group_vars/all.example.yml @@ -0,0 +1,24 @@ +aw_server_version: "v0.13.2" +aw_server_download_url: "https://github.com/ActivityWatch/activitywatch/releases/download/v0.13.2/activitywatch-v0.13.2-linux-x86_64.zip" +aw_server_bind_host: "0.0.0.0" +aw_server_port: 5600 +aw_server_webui_dir: "/opt/activitywatch/webui-ru" +aw_server_data_dir: "/var/lib/activitywatch" +aw_server_log_dir: "/var/log/activitywatch" +aw_server_user: "activitywatch" +aw_server_group: "activitywatch" + +aw_repo_root: "/home/igor/tmp/AWatch-rus" + +# Optional: apply a baseline worktime-focused categorization and views via AW settings API. +# WARNING: this overwrites existing server-side settings/classes/views. +aw_apply_worktime_settings: false + +# Optional defaults for the worktime period in Web UI. +# startOfDay controls day-boundary and default report window start. +# durationDefault controls default time range (seconds) shown in UI. +# +# Recommended: set worktime window explicitly and let the playbook derive duration. +aw_worktime_from: "08:00" +aw_worktime_to: "17:00" +aw_worktime_start_of_day: "{{ aw_worktime_from }}" diff --git a/install-kit-awindows-20260427-211240/ansible/group_vars/pfsense-poller.example.yml b/install-kit-awindows-20260427-211240/ansible/group_vars/pfsense-poller.example.yml new file mode 100644 index 0000000..87aabaa --- /dev/null +++ b/install-kit-awindows-20260427-211240/ansible/group_vars/pfsense-poller.example.yml @@ -0,0 +1,30 @@ +aw_pfsense_poller_config: + poll_interval_seconds: 60 + aw: + server_host: "10.10.10.13" + server_port: 5600 + hostname: "PFSENSE-EDGE01" + pulse_time_seconds: 120 + timeout_seconds: 15 + pfsense: + name: "pfSense Edge 01" + host: "10.10.10.1" + scheme: "https" + verify_tls: false + timeout_seconds: 15 + headers: + X-API-Key: "replace-me" + X-API-Secret: "replace-me" + endpoints: + - name: "system-status" + path: "/api/v2/status/system" + bucket_prefix: "aw-pfsense-health" + bucket_type: "aw.pfsense.health" + - name: "interfaces" + path: "/api/v2/interface" + bucket_prefix: "aw-pfsense-interfaces" + bucket_type: "aw.pfsense.interfaces" + - name: "gateways" + path: "/api/v2/status/gateways" + bucket_prefix: "aw-pfsense-gateways" + bucket_type: "aw.pfsense.gateways" diff --git a/install-kit-awindows-20260427-211240/ansible/group_vars/proxmox-matrix.example.yml b/install-kit-awindows-20260427-211240/ansible/group_vars/proxmox-matrix.example.yml new file mode 100644 index 0000000..ffe4b88 --- /dev/null +++ b/install-kit-awindows-20260427-211240/ansible/group_vars/proxmox-matrix.example.yml @@ -0,0 +1,38 @@ +proxmox_ct_matrix: + - id: "203" + hostname: "activitywatch-user1" + storage: "local-lvm" + template: "local:vztmpl/debian-12-standard_12.7-1_amd64.tar.zst" + rootfs_size: "8G" + cores: "2" + memory: "2048" + swap: "512" + bridge: "vmbr10" + ip: "10.20.30.13/24" + gw: "10.20.30.1" + vlan: "" + nameserver: "1.1.1.1 8.8.8.8" + searchdomain: "example.internal" + password: "CHANGE_ME" + unprivileged: "1" + onboot: "1" + features: "nesting=1,keyctl=1" + + - id: "204" + hostname: "activitywatch-user2" + storage: "local-lvm" + template: "local:vztmpl/debian-12-standard_12.7-1_amd64.tar.zst" + rootfs_size: "8G" + cores: "2" + memory: "2048" + swap: "512" + bridge: "vmbr10" + ip: "10.20.30.14/24" + gw: "10.20.30.1" + vlan: "" + nameserver: "1.1.1.1 8.8.8.8" + searchdomain: "example.internal" + password: "CHANGE_ME" + unprivileged: "1" + onboot: "1" + features: "nesting=1,keyctl=1" diff --git a/install-kit-awindows-20260427-211240/ansible/group_vars/proxmox.example.yml b/install-kit-awindows-20260427-211240/ansible/group_vars/proxmox.example.yml new file mode 100644 index 0000000..07e6eda --- /dev/null +++ b/install-kit-awindows-20260427-211240/ansible/group_vars/proxmox.example.yml @@ -0,0 +1,18 @@ +proxmox_ct_id: "203" +proxmox_ct_hostname: "activitywatch-server" +proxmox_ct_storage: "local-lvm" +proxmox_ct_template: "local:vztmpl/debian-12-standard_12.7-1_amd64.tar.zst" +proxmox_ct_rootfs_size: "8G" +proxmox_ct_cores: "2" +proxmox_ct_memory: "2048" +proxmox_ct_swap: "512" +proxmox_ct_bridge: "vmbr10" +proxmox_ct_ip: "10.20.30.13/24" +proxmox_ct_gw: "10.20.30.1" +proxmox_ct_vlan: "" +proxmox_ct_nameserver: "1.1.1.1 8.8.8.8" +proxmox_ct_searchdomain: "example.internal" +proxmox_ct_password: "CHANGE_ME" +proxmox_ct_unprivileged: "1" +proxmox_ct_onboot: "1" +proxmox_ct_features: "nesting=1,keyctl=1" diff --git a/install-kit-awindows-20260427-211240/ansible/windows.example.yml b/install-kit-awindows-20260427-211240/ansible/group_vars/windows.example.yml similarity index 100% rename from install-kit-awindows-20260427-211240/ansible/windows.example.yml rename to install-kit-awindows-20260427-211240/ansible/group_vars/windows.example.yml diff --git a/install-kit-awindows-20260427-211240/ansible/inventory.example.ini b/install-kit-awindows-20260427-211240/ansible/inventory.example.ini new file mode 100644 index 0000000..f73f446 --- /dev/null +++ b/install-kit-awindows-20260427-211240/ansible/inventory.example.ini @@ -0,0 +1,8 @@ +[proxmox] +pve-main ansible_host=192.168.10.2 ansible_user=root ansible_port=22 + +[aw_server] +aw-ct ansible_host=10.20.30.13 ansible_user=root ansible_port=22 + +[aw_windows] +win-node1 ansible_host=192.168.100.21 ansible_user=Administrator ansible_password=CHANGE_ME ansible_connection=winrm ansible_winrm_transport=ntlm ansible_port=5985 ansible_winrm_server_cert_validation=ignore diff --git a/install-kit-awindows-20260427-211240/ansible/provision_proxmox_ct_and_deploy_aw.yml b/install-kit-awindows-20260427-211240/ansible/provision_proxmox_ct_and_deploy_aw.yml new file mode 100644 index 0000000..2a932a7 --- /dev/null +++ b/install-kit-awindows-20260427-211240/ansible/provision_proxmox_ct_and_deploy_aw.yml @@ -0,0 +1,40 @@ +--- +- name: Provision single Proxmox CT and deploy AWatch-rus + hosts: proxmox + gather_facts: false + + vars: + proxmox_bootstrap_dir: "/tmp/aw-rus-bootstrap" + aw_bootstrap_files: + - install_aw_server.sh + - apply_webui_ru_patch.sh + - activitywatch-server.service + - aw-server.env.example + - aw-ru-patch.js + - aw-sw-cleanup.js + - aw-host-groups.json + - settings/classes-worktime.json + - settings/views-default.json + + tasks: + - name: Execute single-CT provisioning workflow + ansible.builtin.include_tasks: tasks/provision_ct_and_deploy_aw.yml + vars: + ct_id: "{{ proxmox_ct_id }}" + ct_hostname: "{{ proxmox_ct_hostname }}" + ct_storage: "{{ proxmox_ct_storage }}" + ct_template: "{{ proxmox_ct_template }}" + ct_rootfs_size: "{{ proxmox_ct_rootfs_size }}" + ct_cores: "{{ proxmox_ct_cores }}" + ct_memory: "{{ proxmox_ct_memory }}" + ct_swap: "{{ proxmox_ct_swap }}" + ct_bridge: "{{ proxmox_ct_bridge }}" + ct_ip: "{{ proxmox_ct_ip }}" + ct_gw: "{{ proxmox_ct_gw }}" + ct_vlan: "{{ proxmox_ct_vlan | default('') }}" + ct_nameserver: "{{ proxmox_ct_nameserver | default('') }}" + ct_searchdomain: "{{ proxmox_ct_searchdomain | default('') }}" + ct_password: "{{ proxmox_ct_password }}" + ct_unprivileged: "{{ proxmox_ct_unprivileged }}" + ct_onboot: "{{ proxmox_ct_onboot }}" + ct_features: "{{ proxmox_ct_features }}" diff --git a/install-kit-awindows-20260427-211240/ansible/provision_proxmox_ct_matrix_and_deploy_aw.yml b/install-kit-awindows-20260427-211240/ansible/provision_proxmox_ct_matrix_and_deploy_aw.yml new file mode 100644 index 0000000..b228f66 --- /dev/null +++ b/install-kit-awindows-20260427-211240/ansible/provision_proxmox_ct_matrix_and_deploy_aw.yml @@ -0,0 +1,50 @@ +--- +- name: Provision Proxmox CT matrix and deploy AWatch-rus with RU patch + hosts: proxmox + gather_facts: false + + vars: + proxmox_bootstrap_dir: "/tmp/aw-rus-bootstrap" + aw_bootstrap_files: + - install_aw_server.sh + - apply_webui_ru_patch.sh + - activitywatch-server.service + - aw-server.env.example + - aw-ru-patch.js + - aw-sw-cleanup.js + - aw-host-groups.json + - settings/classes-worktime.json + - settings/views-default.json + + tasks: + - name: Validate CT matrix is provided + ansible.builtin.assert: + that: + - proxmox_ct_matrix is defined + - proxmox_ct_matrix | length > 0 + fail_msg: "Define proxmox_ct_matrix in group_vars/proxmox-matrix.yml" + + - name: Execute provisioning workflow for each CT + ansible.builtin.include_tasks: tasks/provision_ct_and_deploy_aw.yml + vars: + ct_id: "{{ item.id }}" + ct_hostname: "{{ item.hostname }}" + ct_storage: "{{ item.storage }}" + ct_template: "{{ item.template }}" + ct_rootfs_size: "{{ item.rootfs_size }}" + ct_cores: "{{ item.cores }}" + ct_memory: "{{ item.memory }}" + ct_swap: "{{ item.swap }}" + ct_bridge: "{{ item.bridge }}" + ct_ip: "{{ item.ip }}" + ct_gw: "{{ item.gw }}" + ct_vlan: "{{ item.vlan | default('') }}" + ct_nameserver: "{{ item.nameserver | default('') }}" + ct_searchdomain: "{{ item.searchdomain | default('') }}" + ct_password: "{{ item.password }}" + ct_unprivileged: "{{ item.unprivileged }}" + ct_onboot: "{{ item.onboot }}" + ct_features: "{{ item.features }}" + loop: "{{ proxmox_ct_matrix }}" + loop_control: + label: "ct={{ item.id }} host={{ item.hostname }} ip={{ item.ip }}" diff --git a/install-kit-awindows-20260427-211240/ansible/tasks/provision_ct_and_deploy_aw.yml b/install-kit-awindows-20260427-211240/ansible/tasks/provision_ct_and_deploy_aw.yml new file mode 100644 index 0000000..4cceb17 --- /dev/null +++ b/install-kit-awindows-20260427-211240/ansible/tasks/provision_ct_and_deploy_aw.yml @@ -0,0 +1,217 @@ +--- +- name: Validate required per-CT variables + ansible.builtin.assert: + that: + - ct_id is defined + - ct_hostname is defined + - ct_storage is defined + - ct_template is defined + - ct_rootfs_size is defined + - ct_cores is defined + - ct_memory is defined + - ct_swap is defined + - ct_bridge is defined + - ct_ip is defined + - ct_gw is defined + - ct_password is defined + - ct_unprivileged is defined + - ct_onboot is defined + - ct_features is defined + - aw_repo_root is defined + - aw_server_version is defined + - aw_server_download_url is defined + - aw_server_bind_host is defined + - aw_server_port is defined + - aw_server_webui_dir is defined + - aw_server_data_dir is defined + - aw_server_log_dir is defined + - aw_server_user is defined + - aw_server_group is defined + fail_msg: "Missing required variables for CT provisioning/deploy." + +- name: Build CT network string + ansible.builtin.set_fact: + ct_net0: >- + name=eth0,bridge={{ ct_bridge }},ip={{ ct_ip }},gw={{ ct_gw }}{% if (ct_vlan | default('') | string | length) > 0 %},tag={{ ct_vlan }}{% endif %} + +- name: Check whether CT already exists + ansible.builtin.command: + argv: + - pct + - status + - "{{ ct_id }}" + register: ct_status_check + failed_when: false + changed_when: false + +- name: Create CT when absent + ansible.builtin.command: + argv: + - pct + - create + - "{{ ct_id }}" + - "{{ ct_template }}" + - --hostname + - "{{ ct_hostname }}" + - --cores + - "{{ ct_cores }}" + - --memory + - "{{ ct_memory }}" + - --swap + - "{{ ct_swap }}" + - --rootfs + - "{{ ct_storage }}:{{ ct_rootfs_size }}" + - --password + - "{{ ct_password }}" + - --unprivileged + - "{{ ct_unprivileged }}" + - --onboot + - "{{ ct_onboot }}" + - --features + - "{{ ct_features }}" + - --net0 + - "{{ ct_net0 }}" + - --nameserver + - "{{ ct_nameserver | default('') }}" + - --searchdomain + - "{{ ct_searchdomain | default('') }}" + - --ostype + - debian + when: ct_status_check.rc != 0 + +- name: Check current CT runtime state + ansible.builtin.command: + argv: + - pct + - status + - "{{ ct_id }}" + register: ct_runtime_status + changed_when: false + +- name: Start CT when stopped + ansible.builtin.command: + argv: + - pct + - start + - "{{ ct_id }}" + when: "'stopped' in ct_runtime_status.stdout" + +- name: Ensure bootstrap directory on Proxmox host + ansible.builtin.file: + path: "{{ proxmox_bootstrap_dir }}" + state: directory + mode: "0700" + +- name: Copy AW bootstrap files to Proxmox host temp + ansible.builtin.copy: + src: "{{ aw_repo_root }}/aw-server/{{ item }}" + dest: "{{ proxmox_bootstrap_dir }}/{{ item }}" + mode: "0644" + loop: "{{ aw_bootstrap_files }}" + +- name: Bootstrap CT OS dependencies + ansible.builtin.command: + argv: + - pct + - exec + - "{{ ct_id }}" + - -- + - bash + - -lc + - | + set -euo pipefail + export DEBIAN_FRONTEND=noninteractive + apt-get update + apt-get install -y curl ca-certificates bash unzip xz-utils jq rsync openssh-server + mkdir -p /root/bootstrap /etc/activitywatch + systemctl enable ssh || true + systemctl restart ssh || true + +- name: Push bootstrap files into CT + ansible.builtin.command: + argv: + - pct + - push + - "{{ ct_id }}" + - "{{ proxmox_bootstrap_dir }}/{{ item }}" + - "/root/bootstrap/{{ item }}" + loop: "{{ aw_bootstrap_files }}" + +- name: Write AW server env file on Proxmox host temp + ansible.builtin.copy: + dest: "{{ proxmox_bootstrap_dir }}/aw-server.env" + mode: "0600" + content: | + AW_SERVER_VERSION={{ aw_server_version }} + AW_SERVER_DOWNLOAD_URL={{ aw_server_download_url }} + AW_SERVER_BIND_HOST={{ aw_server_bind_host }} + AW_SERVER_PORT={{ aw_server_port }} + AW_SERVER_WEBUI_DIR={{ aw_server_webui_dir }} + AW_SERVER_DATA_DIR={{ aw_server_data_dir }} + AW_SERVER_LOG_DIR={{ aw_server_log_dir }} + AW_SERVER_USER={{ aw_server_user }} + AW_SERVER_GROUP={{ aw_server_group }} + +- name: Push AW server env into CT + ansible.builtin.command: + argv: + - pct + - push + - "{{ ct_id }}" + - "{{ proxmox_bootstrap_dir }}/aw-server.env" + - /etc/activitywatch/aw-server.env + +- name: Set mode for env inside CT + ansible.builtin.command: + argv: + - pct + - exec + - "{{ ct_id }}" + - -- + - chmod + - "0600" + - /etc/activitywatch/aw-server.env + +- name: Install server and apply RU patch inside CT + ansible.builtin.command: + argv: + - pct + - exec + - "{{ ct_id }}" + - -- + - bash + - -lc + - | + set -euo pipefail + chmod +x /root/bootstrap/install_aw_server.sh /root/bootstrap/apply_webui_ru_patch.sh + bash /root/bootstrap/install_aw_server.sh + bash /root/bootstrap/apply_webui_ru_patch.sh + systemctl restart activitywatch-server.service + +- name: Validate AW API from inside CT + ansible.builtin.command: + argv: + - pct + - exec + - "{{ ct_id }}" + - -- + - bash + - -lc + - "curl -fsS http://127.0.0.1:{{ aw_server_port }}/api/0/info >/dev/null" + +- name: Validate RU patch hooks in index + ansible.builtin.command: + argv: + - pct + - exec + - "{{ ct_id }}" + - -- + - bash + - -lc + - "grep -q 'ru-patch-v5.js' {{ aw_server_webui_dir }}/index.html && grep -q 'sw-cleanup.js' {{ aw_server_webui_dir }}/index.html" + +- name: Show final endpoint + ansible.builtin.debug: + msg: + - "CT {{ ct_id }} is provisioned and configured." + - "ActivityWatch endpoint: http://{{ ct_ip | regex_replace('/[0-9]+$', '') }}:{{ aw_server_port }}" diff --git a/install-kit-awindows-20260427-211240/aw-server/activitywatch-server.service b/install-kit-awindows-20260427-211240/aw-server/activitywatch-server.service new file mode 100755 index 0000000..e8f26e3 --- /dev/null +++ b/install-kit-awindows-20260427-211240/aw-server/activitywatch-server.service @@ -0,0 +1,24 @@ +[Unit] +Description=ActivityWatch Server (Rust) +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple +EnvironmentFile=/etc/activitywatch/aw-server.env +User=__AW_SERVER_USER__ +Group=__AW_SERVER_GROUP__ +WorkingDirectory=__AW_SERVER_DATA_DIR__ +ExecStart=/bin/sh -lc 'exec /opt/activitywatch/bin/aw-server-rust --host "$AW_SERVER_BIND_HOST" --port "$AW_SERVER_PORT"' +Restart=on-failure +RestartSec=5s +StateDirectory=activitywatch +LogsDirectory=activitywatch +NoNewPrivileges=true +PrivateTmp=true +ProtectSystem=full +ProtectHome=true +LimitNOFILE=65535 + +[Install] +WantedBy=multi-user.target diff --git a/install-kit-awindows-20260427-211240/aw-server/apply_webui_ru_patch.sh b/install-kit-awindows-20260427-211240/aw-server/apply_webui_ru_patch.sh new file mode 100755 index 0000000..2d37b7b --- /dev/null +++ b/install-kit-awindows-20260427-211240/aw-server/apply_webui_ru_patch.sh @@ -0,0 +1,88 @@ +#!/bin/bash +set -euo pipefail + +ENV_FILE="/etc/activitywatch/aw-server.env" +if [[ ! -f "$ENV_FILE" ]]; then + echo "missing env file: $ENV_FILE" >&2 + exit 1 +fi + +source "$ENV_FILE" + +WEBUI_DIR="${AW_SERVER_WEBUI_DIR:-${AW_WEBUI_DIR:-/opt/activitywatch/webui-ru}}" +PATCH_JS_SRC="/root/bootstrap/aw-ru-patch.js" +SW_CLEANUP_SRC="/root/bootstrap/aw-sw-cleanup.js" +HOST_GROUPS_SRC="/root/bootstrap/aw-host-groups.json" +INDEX_HTML="$WEBUI_DIR/index.html" +SERVICE_WORKER="$WEBUI_DIR/service-worker.js" +TS=$(date +%Y%m%d%H%M%S) +PATCH_TARGET="$WEBUI_DIR/js/ru-patch-v5.js" +SW_TARGET="$WEBUI_DIR/js/sw-cleanup.js" +HOST_GROUPS_TARGET="$WEBUI_DIR/js/aw-host-groups.json" +TRENDS_NEEDLE='this.activityStore.query_category_time_by_period(r)' +TRENDS_REPLACEMENT='this.activityStore.ensure_loaded(r)' +TIMESPIRAL_NEEDLE='start:new Date("2022-08-08")' +TIMESPIRAL_REPLACEMENT='start:new Date(Date.now()-12*36e5)' + +[[ -f "$PATCH_JS_SRC" ]] || { echo "missing $PATCH_JS_SRC" >&2; exit 1; } +[[ -f "$SW_CLEANUP_SRC" ]] || { echo "missing $SW_CLEANUP_SRC" >&2; exit 1; } +[[ -f "$HOST_GROUPS_SRC" ]] || { echo "missing $HOST_GROUPS_SRC" >&2; exit 1; } +[[ -f "$INDEX_HTML" ]] || { echo "missing $INDEX_HTML" >&2; exit 1; } + +install -d "$WEBUI_DIR/js" +install -m 0644 "$PATCH_JS_SRC" "$PATCH_TARGET" +install -m 0644 "$SW_CLEANUP_SRC" "$SW_TARGET" +install -m 0644 "$HOST_GROUPS_SRC" "$HOST_GROUPS_TARGET" +cp "$INDEX_HTML" "$INDEX_HTML.bak.$TS" + +patch_hash="$(sha1sum "$PATCH_TARGET" | awk '{print substr($1,1,12)}')" +sw_hash="$(sha1sum "$SW_TARGET" | awk '{print substr($1,1,12)}')" + +sed -i '/ru-patch-v5.js/d;/sw-cleanup.js/d;/aw-ru-patch.js/d;/aw-sw-cleanup.js/d' "$INDEX_HTML" +sed -i "s###" "$INDEX_HTML" +sed -i "s###" "$INDEX_HTML" +cp "$SW_CLEANUP_SRC" "$SERVICE_WORKER" + +trends_chunk="$(grep -Rsl "$TRENDS_NEEDLE" "$WEBUI_DIR/js"/*.js 2>/dev/null | head -n 1 || true)" +if [[ -n "$trends_chunk" ]]; then + cp "$trends_chunk" "$trends_chunk.bak.$TS" + python3 - "$trends_chunk" "$TRENDS_NEEDLE" "$TRENDS_REPLACEMENT" <<'PY' +from pathlib import Path +import sys + +path = Path(sys.argv[1]) +old = sys.argv[2] +new = sys.argv[3] +content = path.read_text() +if old in content: + path.write_text(content.replace(old, new, 1)) + print(f"Trends hotfix applied to {path}") +else: + print(f"Trends hotfix already present in {path}") +PY +else + echo "Trends hotfix skipped: chunk not found" +fi + +timespiral_chunk="$(grep -Rsl "$TIMESPIRAL_NEEDLE" "$WEBUI_DIR/js"/*.js 2>/dev/null | head -n 1 || true)" +if [[ -n "$timespiral_chunk" ]]; then + cp "$timespiral_chunk" "$timespiral_chunk.bak.$TS" + python3 - "$timespiral_chunk" "$TIMESPIRAL_NEEDLE" "$TIMESPIRAL_REPLACEMENT" <<'PY' +from pathlib import Path +import sys + +path = Path(sys.argv[1]) +old = sys.argv[2] +new = sys.argv[3] +content = path.read_text() +if old in content: + path.write_text(content.replace(old, new, 1)) + print(f"Timespiral hotfix applied to {path}") +else: + print(f"Timespiral hotfix already present in {path}") +PY +else + echo "Timespiral hotfix skipped: chunk not found" +fi + +echo "RU patch applied to $WEBUI_DIR (ru-patch-v5.js?v=$patch_hash)" diff --git a/install-kit-awindows-20260427-211240/aw-server/aw-host-groups.json b/install-kit-awindows-20260427-211240/aw-server/aw-host-groups.json new file mode 100644 index 0000000..f3d0118 --- /dev/null +++ b/install-kit-awindows-20260427-211240/aw-server/aw-host-groups.json @@ -0,0 +1,47 @@ +{ + "groups": [ + { + "id": "pve-detmir", + "name": "pve-detmir", + "description": "Выделенный клиент DetMir в разделе Активность.", + "patterns": [ + "^pve-detmir$" + ], + "links": [ + { "label": "Активность", "type": "activity", "view": "pve_audit" }, + { "label": "Web-admin аудит", "type": "bucket", "bucket_prefix": "aw-pve-webadmin-events_" }, + { "label": "PVE tasks", "type": "bucket", "bucket_prefix": "aw-pve-task-events_" }, + { "label": "SSH сессии", "type": "bucket", "bucket_prefix": "aw-ssh-sessions_" }, + { "label": "Команды shell", "type": "bucket", "bucket_prefix": "aw-console-commands_" }, + { "label": "Web категории", "type": "bucket", "bucket_prefix": "aw-detmir-web-category_" }, + { "label": "Все бакеты", "type": "buckets" } + ] + }, + { + "id": "windows-rdp", + "name": "Windows RDP", + "description": "Пользовательские Windows/RDP хосты с активностью, DLP и рабочим временем.", + "patterns": [ + "^(SHARKON|WIN|RDP|TERM|TS-|WS-)" + ], + "links": [ + { "label": "Активность", "type": "activity" }, + { "label": "DLP", "type": "bucket", "bucket_prefix": "aw-dlp-endpoint-signals_" } + ] + }, + { + "id": "virtual-infra", + "name": "Virtual servers + Proxmox", + "description": "Инфраструктурные VM и сетевые узлы. Здесь должны лежать Proxmox, pfSense, Debian и Ubuntu серверы.", + "patterns": [ + "^(PFSENSE|PVE|PROXMOX|DEBIAN|UBUNTU|LINUX|VM-|SRV-|INFRA-)" + ], + "links": [ + { "label": "pfSense health", "type": "bucket", "bucket_prefix": "aw-pfsense-health_" }, + { "label": "pfSense gateways", "type": "bucket", "bucket_prefix": "aw-pfsense-gateways_" }, + { "label": "Все бакеты", "type": "buckets" } + ] + } + ], + "ungrouped_name": "Прочие хосты" +} diff --git a/install-kit-awindows-20260427-211240/aw-server/aw-ru-patch.js b/install-kit-awindows-20260427-211240/aw-server/aw-ru-patch.js new file mode 100755 index 0000000..1052651 --- /dev/null +++ b/install-kit-awindows-20260427-211240/aw-server/aw-ru-patch.js @@ -0,0 +1,1518 @@ +(function () { + window.__awRuPatchVersion = "template-v10-pve-detmir-audit-stable"; + document.documentElement.setAttribute("data-aw-ru-patch", "template-v10-pve-detmir-audit-stable"); + + const exact = new Map([ + ["ActivityWatch", "АктивВотч"], + ["Home", "Главная"], + ["Activity", "Активность"], + ["Developer settings", "Настройки разработчика"], + ["Timeline", "Таймлайн"], + ["Trends", "Тренды"], + ["Report", "Отчеты"], + ["Settings", "Настройки"], + ["Search", "Поиск"], + ["Buckets", "Бакеты"], + ["Stopwatch", "Секундомер"], + ["Timespiral", "Временная спираль"], + ["Categorization helper", "Помощник категоризации"], + ["Categorization", "Категоризация"], + ["Tools", "Инструменты"], + ["Raw Data", "Сырые данные"], + ["Summary", "Сводка"], + ["All", "Все"], + ["None", "Нет"], + ["Date", "Дата"], + ["Time", "Время"], + ["to", "до"], + ["Start of day", "Начало дня"], + ["Start of week", "Начало недели"], + ["Duration default value", "Значение длительности по умолчанию"], + ["Landing page", "Стартовая страница"], + ["Theme", "Тема"], + ["New release notification", "Уведомление о новом релизе"], + ["Use fallback colors", "Использовать резервные цвета"], + ["Always count as active pattern", "Шаблон всегда считать активным"], + ["Hostname", "Имя хоста"], + ["Hostname:", "Имя хоста:"], + ["Range", "Диапазон"], + ["Range:", "Диапазон:"], + ["Options", "Параметры"], + ["Toggles", "Переключатели"], + ["Enabled", "Включено"], + ["Host", "Хост"], + ["Version", "Версия"], + ["day", "день"], + ["week", "неделя"], + ["month", "месяц"], + ["year", "год"], + ["Monday", "Понедельник"], + ["Saturday", "Суббота"], + ["Sunday", "Воскресенье"], + ["Start", "Начало"], + ["Stop", "Конец"], + ["End", "Конец"], + ["Refresh", "Обновить"], + ["Apply", "Применить"], + ["Filters", "Фильтры"], + ["Show options", "Показать параметры"], + ["Show last", "Показать за"], + ["Show from", "Показать с"], + ["Last update", "Последнее обновление"], + ["Events shown", "Показано событий"], + ["Window", "Окно"], + ["New view", "Новый вид"], + ["Edit view", "Изменить вид"], + ["Show percent", "Показать проценты"], + ["Top Window Titles", "Топ заголовков окон"], + ["Download", "Скачать"], + ["Upload", "Загрузить"], + ["Show", "Показать"], + ["Hide", "Скрыть"], + ["Close", "Закрыть"], + ["Week", "Неделя"], + ["Month", "Месяц"], + ["Year", "Год"], + ["Today", "Сегодня"], + ["Yesterday", "Вчера"], + ["No data", "Нет данных"], + ["Category", "Категория"], + ["Categories", "Категории"], + ["Duration", "Длительность"], + ["Applications", "Приложения"], + ["Hosts", "Хосты"], + ["Date Range", "Диапазон дат"], + ["Generate", "Сформировать"], + ["Loading", "Загрузка"], + ["Loading...", "Загрузка..."], + ["Dark", "Темная"], + ["Light", "Светлая"], + ["Save", "Сохранить"], + ["Cancel", "Отмена"], + ["Delete", "Удалить"], + ["API Browser", "API-браузер"], + ["Documentation", "Документация"], + ["Restore defaults", "Восстановить значения по умолчанию"], + ["Import", "Импорт"], + ["Export", "Экспорт"], + ["Category Builder", "Конструктор категорий"], + ["Query Explorer", "Конструктор запросов"], + ["Event List", "Список событий"], + ["Raw JSON", "Сырой JSON"], + ["Expand list", "Развернуть список"], + ["Alerts", "Оповещения"], + ["Graph", "Граф"], + ["Developer zone", "Зона разработчика"], + ["History", "История"], + ["Running", "Запущено"], + ["Edit", "Изменить"], + ["No stopwatch running", "Нет активного секундомера"], + ["No label", "Без метки"], + ["Start new", "Новый запуск"], + ["Check", "Проверить"], + ["Name", "Имя"], + ["New alert", "Новое правило"], + ["Custom regex", "Свое регулярное выражение"], + ["Use existing categories", "Использовать существующие категории"], + ["Choose a tag...", "Выберите тег..."], + ["Rule", "Правило"], + ["Open", "Открыть"], + ["More", "Еще"], + ["Bucket ID", "ID бакета"], + ["Updated", "Обновлено"], + ["First seen", "Впервые замечен"], + ["Last updated", "Последнее обновление"], + ["Toggle navigation", "Переключить навигацию"], + ["unknown", "неизвестно"], + ["Uncategorized", "Без категории"] + ]); + + const partial = [ + ["Hello early user,", "Здравствуйте, ранний пользователь,"], + ["early days for ActivityWatch.", "ранний этап для ActivityWatch."], + ["still early days for ActivityWatch.", "все еще ранний этап для ActivityWatch."], + ["It's still early days for ActivityWatch.", "ActivityWatch еще находится на раннем этапе."], + ["We've come a long way but we need users (like you!) to provide feedback and help us turn ActivityWatch into a successful project.", "Мы уже прошли большой путь, но нам нужны пользователи вроде вас, чтобы давать обратную связь и помогать развивать проект."], + ["Early users like you mean a lot to us, and we hope you'll reach out to us with any ideas you have for improvements!", "Такие ранние пользователи очень важны, и мы рассчитываем на ваши идеи по улучшению системы."], + ["If you have a minute, we'd really appreciate you taking our short user survey!", "Если у вас есть минута, пожалуйста, пройдите наш короткий опрос пользователей."], + ["If you have a minute to spare, please take the time to fill out our user survey, vote on features in the forum, or just share ActivityWatch with your friends and colleagues.", "Если у вас есть немного времени, пожалуйста, заполните наш пользовательский опрос, проголосуйте за функции на форуме или просто расскажите об ActivityWatch друзьям и коллегам."], + ["Spread the word", "Расскажите другим"], + ["It's still early days for ActivityWatch. We've come a long way but we need users (like you!) to provide feedback and help us turn ActivityWatch into a successful project. Early users like you mean a lot to us, and we hope you'll reach out to us with any ideas you have for improvements!", "ActivityWatch еще находится на раннем этапе. Мы уже прошли большой путь, но нам нужны пользователи вроде вас, чтобы давать обратную связь и помогать развивать проект. Такие ранние пользователи очень важны, и мы рассчитываем на ваши идеи по улучшению системы."], + ["If you are a developer, we hope you can contribute by writing a watcher, visualization, or something else, and share it with us on the forum!", "Если вы разработчик, вы можете помочь проекту: написать watcher, визуализацию или что-то еще и поделиться этим на форуме."], + ["Thank you for using ActivityWatch!", "Спасибо за использование ActivityWatch!"], + ["If you are not interested in this message, then just ignore it. We won't show it very often.", "Если это сообщение вам не нужно, просто проигнорируйте его. Мы показываем его нечасто."], + ["Trends for ", "Тренды за "], + ["Activity for ", "Активность за "], + ["Активность for ", "Активность за "], + ["7 days", "7 дней"], + ["30 days", "30 дней"], + ["Time active:", "Активное время:"], + ["This feature is still in early development.", "Эта функция пока находится на ранней стадии разработки."], + ["This is a work-in-progress experiment.", "Это экспериментальная функция, она ещё не доведена до готового состояния."], + ["Bucket: ", "Бакет: "], + ["Events: ", "События: "], + ["This tool will help you create categories from your uncategorized time.", "Этот инструмент поможет создавать категории из некатегоризированного времени."], + ["Note: These settings are meant for developers who (hopefully) know what they are doing, and as such, may break things unexpectedly.", "Примечание: эти настройки предназначены для разработчиков, которые понимают, что делают, и поэтому могут неожиданно что-нибудь сломать."], + ["It works by fetching all uncategorized time for a recent timeperiod, and then finds the most common words (by time, not count) each of which may then either be ignored (if too broad/irrelevant), or used to create a new (sub)category, or to append the word to a pre-existing category rule. Words with less than 60s of time will not be shown.", "Инструмент получает некатегоризированное время за недавний период и ищет самые частые слова по длительности, а не по количеству. Их можно игнорировать, если они слишком общие, использовать для создания новой подкатегории или добавить в уже существующее правило. Слова с длительностью меньше 60 секунд не показываются."], + ["When you're done, you can inspect the categories in the Settings page.", "После завершения вы сможете проверить категории на странице Настройки."], + ['The time at which days "start", since humans don\'t always go to bed before midnight. Set to 04:00 by default.', 'Время, с которого начинается новый день, так как люди не всегда ложатся спать до полуночи. По умолчанию установлено 04:00.'], + ["The weekday which starts a new week.", "День недели, с которого начинается новая неделя."], + ["The default duration used for 'show last' in the timeline view.", "Длительность по умолчанию для режима 'показать последние' в таймлайне."], + ["The page to open when opening ActivityWatch, or clicking the logo in the top menu.", "Страница, которая открывается при запуске ActivityWatch или при нажатии на логотип в верхнем меню."], + ["Change color theme of the application (you need to change categories colors manually to be suitable with dark mode).", "Изменение цветовой темы приложения. Цвета категорий для темного режима нужно настраивать вручную."], + ["Devmode enables some features that are still work-in-progress.", "Devmode включает некоторые функции, которые всё ещё находятся в стадии разработки."], + ["Querying an entire year is a very heavy operation, and is likely to lead to timeouts. However, the query might be fast enough if you're running aw-server-rust.", "Запрос за целый год является очень тяжёлой операцией и может приводить к таймаутам. Но если у вас работает aw-server-rust, такой запрос может выполняться достаточно быстро."], + ["Multidevice query is where events are collected from several hosts in the Activity view. It is an early experiment, that currently does not support browser buckets (or the audible-as-active feature).", "Multidevice query собирает события с нескольких хостов в представлении Активность. Это ранний эксперимент, который пока не поддерживает бакеты браузера и функцию активной вкладки со звуком."], + ["The maximum amount of time a server request can take before timing out. Setting this to a high value can be useful for large queries. Note that you need to reload the web UI for it to apply.", "Максимальное время выполнения серверного запроса до срабатывания таймаута. Увеличенное значение может быть полезно для больших запросов. Чтобы изменение вступило в силу, нужно перезагрузить Web UI."], + ["We will send you a notification if there is a new release available for download, this check will happen at most once per day.", "При появлении нового релиза для скачивания будет показано уведомление. Проверка выполняется не чаще одного раза в день."], + ["Uses the old coloring style for some visualizations when uncategorized or no category color.", "Использует старую схему раскраски для некоторых визуализаций, когда категория не задана или у нее нет цвета."], + ["Apps or titles matching this regular expression will never be counted as AFK.", "Приложения или заголовки, подходящие под это регулярное выражение, никогда не будут считаться AFK."], + ["Can be used to count time as active, despite no input (like meetings, or games with controllers). An empty string disables it.", "Позволяет считать время активным даже без ввода, например на встречах или в играх с контроллером. Пустая строка отключает функцию."], + ["Example expression:", "Пример выражения:"], + ["Rules for categorizing events. An event can only have one category. If several categories match, the deepest one will be chosen.", "Правила категоризации событий. Событие может иметь только одну категорию. Если подходят несколько, будет выбрана самая глубокая."], + ["You can use the Category Builder to quickly create categories from uncategorized activity.", "Через Конструктор категорий можно быстро создавать категории из некатегоризированной активности."], + ["You can also find and share categorization rule presets on the forum.", "Готовые наборы правил категоризации можно находить и публиковать на форуме."], + ["For help on how to write categorization rules, see the documentation.", "Как писать правила категоризации, описано в документации."], + ["Generate a report of time spent on a certain category of device activity.", "Сформировать отчет по времени в выбранной категории активности устройства."], + ["See the documentation for help on how to write queries.", "Как писать запросы, смотрите в документации."], + ["See the documentation for help", "См. документацию для справки"], + ["See the documentation", "См. документацию"], + ["on how to write queries.", "по написанию запросов."], + ["Number of events:", "Количество событий:"], + ["Query", "Запрос"], + ["EventsShowing", "Показано событий"], + ["Drag to pan and scroll to zoom", "Перетаскивайте для прокрутки и используйте колесо мыши для масштабирования"], + ["Made with", "Сделано с"], + ["by the", "командой"], + ["Сделано скомандой", "Сделано командой"], + ["ActivityWatch developers", "разработчиков ActivityWatch"], + ["Report a bug", "Сообщить об ошибке"], + ["Ask for help", "Получить помощь"], + ["Vote on features", "Голосовать за функции"], + ["Donate", "Поддержать"], + ["TwitterGitHub", "Twitter GitHub"], + ["Using bucket:", "Используется бакет:"], + ["This is an early experiment. Data entered here is not shown in the Activity view, yet.", "Это ранний эксперимент. Данные, введенные здесь, пока не отображаются в представлении Активность."], + ["Started ", "Запущен "], + ["hours ago", "часов назад"], + ["days ago", "дней назад"], + ["0s ago", "0 с назад"], + ["minutes", "минут"], + ["Generate a report", "Сформировать отчет"], + ["Goal name:", "Имя цели:"], + ["Category:", "Категория:"], + ["Current:", "Текущее:"], + ["Toggle autorefresh every ", "Автообновление каждые "], + ["No events match selected criteria. Timeline is not updated.", "Нет событий, соответствующих выбранным критериям. Таймлайн не обновлен."], + ["Last update:", "Последнее обновление:"], + ["See PR aw-webui#365 for more information.", "Подробности см. в PR aw-webui#365."], + ["See PR aw-webui#365", "См. PR aw-webui#365"], + ["for more information.", "для дополнительной информации."], + ["Displays a graph of categories and their transitions.", "Показывает граф категорий и переходов между ними."], + ["Max category depth", "Максимальная глубина категории"], + ["Exclude uncategorized", "Исключать некатегоризированное"], + ["Just some tools to aid in development and debugging.", "Набор инструментов для разработки и отладки."], + ["Nothing to see here right now...", "Сейчас здесь ничего полезного нет..."], + ["Are you looking to collect more data? Check out the docs for more watchers.", "Нужно собирать больше данных? Посмотрите документацию по дополнительным watcher-модулям."], + ["Are you looking to collect more data?", "Нужно собирать больше данных?"], + ["Check out the docs for more watchers.", "Посмотрите документацию по дополнительным watcher-модулям."], + ["Check out the docs for more watchers", "Посмотрите документацию по дополнительным watcher-модулям"], + ["Click to sort ascending", "Нажмите для сортировки по возрастанию"], + ["Host:", "Хост:"], + ["Hostname:", "Имя хоста:"], + ["Range:", "Диапазон:"], + ["serverVersion", "Версия"], + ["Version:", "Версия:"], + ["Last updated:", "Последнее обновление:"], + ["First seen:", "Впервые замечен:"], + ["When you're done, you can inspect the categories", "После завершения вы сможете проверить категории"], + ["in the Settings page.", "на странице Настройки."], + ["Activity (", "Активность ("], + ["Exclude time away from computer", "Исключать время отсутствия за компьютером"], + ['Common words in "Uncategorized" events', 'Частые слова в событиях "Без категории"'], + ["No words with significant duration. You're good to go!", "Нет слов со значимой длительностью. Здесь всё в порядке."], + ["Top apps", "Топ приложений"], + ["Top titles", "Топ заголовков"], + ["Top URLs", "Топ URL"], + ["Top domains", "Топ доменов"], + ["Top Browser Domains", "Топ доменов браузера"], + ["Top Browser URLs", "Топ URL браузера"], + ["Top Browser Titles", "Топ заголовков браузера"], + ["Top Categories", "Топ категорий"], + ["Category Tree", "Дерево категорий"], + ["Timeline (barchart)", "Таймлайн (гистограмма)"], + ["Calculate Work Time", "Рассчитать рабочее время"], + ["Export CSV", "Экспорт CSV"], + ["Export JSON", "Экспорт JSON"], + ["No duplicate events found.", "Дубликаты событий не найдены."], + ["No overlapping events found.", "Пересекающиеся события не найдены."], + ["No zero-duration events found.", "События нулевой длительности не найдены."] + ]; + + const hiddenNavLabels = new Set([ + "Raw Data", + "Сырые данные" + ]); + + const hiddenNavHrefPatterns = [ + /\/raw-data\b/i, + /\/raw\b/i + ]; + + const dlpVerdictOptions = [ + { value: "false_positive", label: "Ложное срабатывание" }, + { value: "allowed", label: "Разрешено" }, + { value: "review_needed", label: "На проверку" }, + { value: "incident", label: "Инцидент" } + ]; + + function replaceText(text) { + if (!text) return text; + if (exact.has(text.trim())) { + return text.replace(text.trim(), exact.get(text.trim())); + } + let result = text; + for (const [en, ru] of partial) { + result = result.split(en).join(ru); + } + return result; + } + + function walk(root) { + const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, null); + const nodes = []; + while (walker.nextNode()) nodes.push(walker.currentNode); + for (const node of nodes) { + const nextValue = replaceText(node.nodeValue); + if (nextValue !== node.nodeValue) { + node.nodeValue = nextValue; + } + } + } + + function translateAttributes(root) { + const elements = root.querySelectorAll("[title],[placeholder],[aria-label]"); + for (const element of elements) { + ["title", "placeholder", "aria-label"].forEach(function (attr) { + const value = element.getAttribute(attr); + if (value) { + element.setAttribute(attr, replaceText(value)); + } + }); + } + } + + function injectStyles() { + if (document.getElementById("aw-ru-hide-noise-style")) return; + const style = document.createElement("style"); + style.id = "aw-ru-hide-noise-style"; + style.textContent = [ + '[href*="/raw-data"], [href*="/raw"], a[data-testid*="raw"], button[data-testid*="raw"] { display: none !important; }', + '[aria-label="Raw Data"], [aria-label="Сырые данные"] { display: none !important; }', + '.aw-ru-dlp-center { margin: 16px 0; padding: 16px; border: 1px solid rgba(120,120,120,.35); border-radius: 8px; background: rgba(20,20,20,.03); }', + '.aw-ru-dlp-toolbar { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; margin-bottom: 12px; }', + '.aw-ru-dlp-toolbar input, .aw-ru-dlp-toolbar select, .aw-ru-dlp-toolbar textarea { min-height: 32px; }', + '.aw-ru-dlp-toolbar button, .aw-ru-dlp-row button { min-height: 32px; padding: 4px 10px; }', + '.aw-ru-dlp-table { width: 100%; border-collapse: collapse; font-size: 13px; }', + '.aw-ru-dlp-table th, .aw-ru-dlp-table td { border: 1px solid rgba(120,120,120,.25); padding: 6px; vertical-align: top; }', + '.aw-ru-dlp-table td input, .aw-ru-dlp-table td select { width: 100%; box-sizing: border-box; }', + '.aw-ru-dlp-muted { opacity: .55; }', + '.aw-ru-dlp-pill { display: inline-block; padding: 2px 8px; border-radius: 999px; background: rgba(90,140,255,.15); font-size: 12px; }', + '.aw-ru-dlp-status { margin-left: auto; font-size: 12px; opacity: .8; }', + '.aw-ru-dlp-message { margin-top: 8px; font-size: 12px; }', + '.aw-ru-dlp-actions { display: flex; gap: 6px; flex-wrap: wrap; }', + '.aw-ru-dlp-section { margin-top: 18px; }', + '.aw-ru-dlp-section h5 { margin: 0 0 8px; }', + '.aw-ru-host-groups { margin: 16px 0; padding: 16px; border: 1px solid rgba(120,120,120,.35); border-radius: 8px; background: rgba(20,20,20,.03); }', + '.aw-ru-host-groups-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 16px; }', + '.aw-ru-host-group-card { border: 1px solid rgba(120,120,120,.25); border-radius: 8px; padding: 12px; background: rgba(255,255,255,.02); }', + '.aw-ru-host-group-card h4 { margin: 0 0 8px; }', + '.aw-ru-host-group-card p { margin: 0 0 12px; font-size: 13px; opacity: .85; }', + '.aw-ru-host-list { display: flex; flex-direction: column; gap: 8px; }', + '.aw-ru-host-item { border: 1px solid rgba(120,120,120,.2); border-radius: 6px; padding: 8px; }', + '.aw-ru-host-item-title { font-weight: 600; margin-bottom: 6px; }', + '.aw-ru-host-links { display: flex; flex-wrap: wrap; gap: 6px; }', + '.aw-ru-host-links a { display: inline-block; padding: 4px 8px; border-radius: 999px; background: rgba(90,140,255,.15); text-decoration: none; }', + '.aw-ru-pve-audit { margin: 16px 0; padding: 16px; border: 1px solid rgba(120,120,120,.35); border-radius: 8px; background: rgba(10,20,40,.04); }', + '.aw-ru-pve-audit-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 12px; margin: 12px 0 16px; }', + '.aw-ru-pve-audit-card { border: 1px solid rgba(120,120,120,.22); border-radius: 8px; padding: 12px; background: rgba(255,255,255,.02); }', + '.aw-ru-pve-audit-card h5 { margin: 0 0 6px; font-size: 13px; opacity: .8; }', + '.aw-ru-pve-audit-value { font-size: 24px; font-weight: 700; }', + '.aw-ru-pve-audit-table { width: 100%; border-collapse: collapse; margin-top: 8px; }', + '.aw-ru-pve-audit-table th, .aw-ru-pve-audit-table td { padding: 6px 8px; border-bottom: 1px solid rgba(120,120,120,.18); vertical-align: top; text-align: left; font-size: 13px; }', + '.aw-ru-pve-audit-muted { opacity: .72; font-size: 13px; }' + ].join("\n"); + document.head.appendChild(style); + } + + function hideNoiseNavigation(root) { + const links = root.querySelectorAll("a, button, [role='button']"); + for (const element of links) { + const text = (element.textContent || "").trim(); + const href = (element.getAttribute("href") || "").trim(); + if (hiddenNavLabels.has(text) || hiddenNavHrefPatterns.some((pattern) => pattern.test(href))) { + const container = element.closest("li, nav, div") || element; + container.style.display = "none"; + } + } + } + + function getCurrentHostFromHash() { + const hash = window.location.hash || ""; + const activityMatch = hash.match(/#\/activity\/([^/?#]+)/); + if (activityMatch && activityMatch[1]) return decodeURIComponent(activityMatch[1]); + const trendsMatch = hash.match(/#\/trends\/([^/?#]+)/); + if (trendsMatch && trendsMatch[1]) return decodeURIComponent(trendsMatch[1]); + return ""; + } + + function isPveLikeHost(host) { + return /^pve[-_]/i.test(String(host || "")); + } + + function enforceSafeActivityViewForPveHost() { + const hash = window.location.hash || ""; + const match = hash.match(/^#\/activity\/([^/]+)\/day\/([^/]+)\/view\/([^/?#]+)/i); + if (!match) return; + const host = decodeURIComponent(match[1] || ""); + const day = decodeURIComponent(match[2] || ""); + const viewId = decodeURIComponent(match[3] || ""); + if (!isPveLikeHost(host)) return; + const safeHash = "#/activity/" + encodeURIComponent(host) + "/day/" + encodeURIComponent(day) + "/view/" + encodeURIComponent("pve_audit"); + if (safeHash !== hash && !/^pve_audit$/i.test(viewId)) { + window.location.replace(safeHash); + } + } + + function getDlpHostFromSettings(settings) { + const routeHost = getCurrentHostFromHash(); + if (routeHost) return routeHost; + const bucketHost = getDlpHostFromBucketId(getDlpBucketIdFromHash()); + if (bucketHost) return bucketHost; + return getTrendsHostFromSettings(settings); + } + + function getDlpHref(host) { + if (!host) return "#/buckets"; + return "#/buckets/" + encodeURIComponent("aw-dlp-endpoint-signals_" + host); + } + + function isDlpSignalBucketRoute() { + return /^#\/buckets\/aw-dlp-endpoint-signals_/i.test(window.location.hash || ""); + } + + function isAlertsRoute() { + return /^#\/alerts(?:[/?#]|$)/i.test(window.location.hash || ""); + } + + function getDlpBucketIdFromHash() { + const hash = window.location.hash || ""; + const match = hash.match(/^#\/buckets\/([^/?#]+)/i); + return match && match[1] ? decodeURIComponent(match[1]) : ""; + } + + function getDlpHostFromBucketId(bucketId) { + const prefix = "aw-dlp-endpoint-signals_"; + return bucketId.startsWith(prefix) ? bucketId.slice(prefix.length) : ""; + } + + function escapeHtml(value) { + return String(value == null ? "" : value) + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """) + .replace(/'/g, "'"); + } + + function normalizeText(value) { + return String(value == null ? "" : value).trim(); + } + + function buildDlpKey(event) { + const data = event && event.data ? event.data : {}; + return [ + event && event.timestamp || "", + data.signalType || "", + data.username || "", + data.owner || "", + data.documentName || "", + data.printerName || "" + ].join("|"); + } + + function generateDlpId(prefix) { + return prefix + "-" + Date.now().toString(36) + "-" + Math.random().toString(36).slice(2, 10); + } + + async function awApiJson(url, options) { + const response = await fetch(url, Object.assign({ + credentials: "same-origin", + headers: { + "Content-Type": "application/json; charset=utf-8" + } + }, options || {})); + if (response.status === 304 || response.status === 409) { + return null; + } + if (!response.ok) { + throw new Error("aw-api-" + response.status); + } + if (response.status === 204) return null; + const text = await response.text(); + return text ? JSON.parse(text) : null; + } + + async function ensureAwBucket(bucketId, clientName, bucketType, hostname) { + await awApiJson("/api/0/buckets/" + encodeURIComponent(bucketId), { + method: "POST", + body: JSON.stringify({ + client: clientName, + type: bucketType, + hostname: hostname + }) + }); + } + + async function saveAwHeartbeat(bucketId, payload, pulsetimeSeconds) { + const pulsetime = pulsetimeSeconds || 1; + await awApiJson("/api/0/buckets/" + encodeURIComponent(bucketId) + "/heartbeat?pulsetime=" + pulsetime, { + method: "POST", + body: JSON.stringify(payload) + }); + } + + async function loadBucketEvents(bucketId, limit) { + const data = await awApiJson("/api/0/buckets/" + encodeURIComponent(bucketId) + "/events?limit=" + (limit || 100), { + method: "GET", + headers: {} + }); + return Array.isArray(data) ? data : []; + } + + function getRuleMatchFields(event) { + const data = event && event.data ? event.data : {}; + return { + signalType: normalizeText(data.signalType), + username: normalizeText(data.username), + owner: normalizeText(data.owner), + documentName: normalizeText(data.documentName), + printerName: normalizeText(data.printerName), + hostname: normalizeText(data.hostname) + }; + } + + function serializeRuleMatch(match) { + const normalized = match || {}; + return [ + normalized.signalType || "", + normalized.username || "", + normalized.owner || "", + normalized.documentName || "", + normalized.printerName || "", + normalized.hostname || "" + ].join("|"); + } + + function getRuleId(ruleEvent) { + const data = ruleEvent && ruleEvent.data ? ruleEvent.data : {}; + return normalizeText(data.ruleId) || [ + serializeRuleMatch(data.match || {}), + normalizeText(data.category), + normalizeText(data.action) + ].join("|"); + } + + function getReviewId(reviewEvent) { + const data = reviewEvent && reviewEvent.data ? reviewEvent.data : {}; + const review = data.review || {}; + return normalizeText(review.reviewId) || [ + data.sourceEvent && data.sourceEvent.timestamp || "", + data.sourceEvent && data.sourceEvent.data ? buildDlpKey({ timestamp: data.sourceEvent.timestamp, data: data.sourceEvent.data }) : "", + normalizeText(review.verdict), + normalizeText(review.category) + ].join("|"); + } + + function collapseRuleEvents(events) { + const ordered = (events || []).slice().sort(function (a, b) { + return String(a.timestamp).localeCompare(String(b.timestamp)); + }); + const map = new Map(); + ordered.forEach(function (event) { + map.set(getRuleId(event), event); + }); + return Array.from(map.values()).sort(function (a, b) { + return String(b.timestamp).localeCompare(String(a.timestamp)); + }); + } + + function collapseReviewEvents(events) { + const ordered = (events || []).slice().sort(function (a, b) { + return String(a.timestamp).localeCompare(String(b.timestamp)); + }); + const map = new Map(); + ordered.forEach(function (event) { + map.set(getReviewId(event), event); + }); + return Array.from(map.values()).sort(function (a, b) { + return String(b.timestamp).localeCompare(String(a.timestamp)); + }); + } + + function ruleMatchesEvent(rule, event) { + const eventFields = getRuleMatchFields(event); + const match = rule && rule.data && rule.data.match ? rule.data.match : {}; + return Object.keys(eventFields).every(function (key) { + const ruleValue = normalizeText(match[key]); + return !ruleValue || ruleValue === eventFields[key]; + }); + } + + function getSuppressionState() { + if (!window.__awRuDlpState) { + window.__awRuDlpState = { + rules: [], + activeRules: [], + reviews: [], + events: [], + loading: false + }; + } + return window.__awRuDlpState; + } + + function removeBadDlpLinks(root) { + const badLinks = root.querySelectorAll("a[href*='/view/DLP']"); + badLinks.forEach(function (link) { + const item = link.closest("li") || link; + item.remove(); + }); + } + + function updateDlpLinks(root, href) { + const links = root.querySelectorAll("a[data-aw-ru-dlp-link='1']"); + for (const link of links) { + if (link.getAttribute("href") !== href) { + link.setAttribute("href", href); + } + } + } + + function buildDlpNavItem(templateItem, href) { + const templateLink = templateItem.querySelector("a[href], [role='link']"); + if (!templateLink) return null; + + const item = document.createElement("li"); + item.setAttribute("data-aw-ru-dlp-item", "1"); + item.className = templateItem.className || ""; + + const link = document.createElement("a"); + link.setAttribute("href", href); + link.setAttribute("data-aw-ru-dlp-link", "1"); + link.textContent = "DLP"; + link.className = templateLink.className || ""; + + item.appendChild(link); + return item; + } + + function findPrimaryNavList(root) { + const navLists = Array.from(root.querySelectorAll("nav ul")); + return navLists.find(function (list) { + const labels = Array.from(list.querySelectorAll("a")) + .map(function (link) { return normalizeText(link.textContent); }) + .filter(Boolean); + return labels.includes("Главная") || + labels.includes("Home") || + labels.includes("Активность") || + labels.includes("Activity"); + }) || null; + } + + function injectDlpNavigation(root) { + const hostForDlp = window.__awRuPatchSettingsHost || getCurrentHostFromHash(); + if (hostForDlp && isPveLikeHost(hostForDlp)) { + removeBadDlpLinks(root); + const ownItem = root.querySelector("[data-aw-ru-dlp-item='1']"); + if (ownItem) ownItem.remove(); + return; + } + const href = getDlpHref(hostForDlp); + removeBadDlpLinks(root); + updateDlpLinks(root, href); + if (root.querySelector("[data-aw-ru-dlp-item='1']")) return; + + const primaryNav = findPrimaryNavList(root); + if (primaryNav) { + const templateItem = primaryNav.querySelector("li") || primaryNav.parentElement; + const dlpItem = templateItem ? buildDlpNavItem(templateItem, href) : null; + if (dlpItem) { + primaryNav.appendChild(dlpItem); + return; + } + const item = document.createElement("li"); + item.setAttribute("data-aw-ru-dlp-item", "1"); + const link = document.createElement("a"); + link.setAttribute("href", href); + link.setAttribute("data-aw-ru-dlp-link", "1"); + link.textContent = "DLP"; + item.appendChild(link); + primaryNav.appendChild(item); + } + } + + function isHomeRoute() { + const hash = window.location.hash || ""; + return !hash || /^#\/home(?:[/?#]|$)/i.test(hash); + } + + function getDefaultHostGroupsConfig() { + return { + groups: [ + { + id: "windows-rdp", + name: "Windows RDP", + description: "Пользовательские Windows/RDP хосты.", + patterns: ["^(SHARKON|WIN|RDP|TERM|TS-|WS-)"], + links: [ + { label: "Активность", type: "activity" }, + { label: "DLP", type: "bucket", bucket_prefix: "aw-dlp-endpoint-signals_" } + ] + }, + { + id: "virtual-infra", + name: "Virtual servers + Proxmox", + description: "Инфраструктурные VM, Proxmox и сетевые узлы.", + patterns: ["^(PFSENSE|PVE|PROXMOX|DEBIAN|UBUNTU|LINUX|VM-|SRV-|INFRA-)"], + links: [ + { label: "pfSense health", type: "bucket", bucket_prefix: "aw-pfsense-health_" }, + { label: "pfSense gateways", type: "bucket", bucket_prefix: "aw-pfsense-gateways_" }, + { label: "Все бакеты", type: "buckets" } + ] + } + ], + ungrouped_name: "Прочие хосты" + }; + } + + function getHostGroupsState() { + if (!window.__awRuHostGroupsState) { + window.__awRuHostGroupsState = { + config: null, + buckets: null, + loading: false + }; + } + return window.__awRuHostGroupsState; + } + + async function ensureHostGroupsData() { + const state = getHostGroupsState(); + if (state.loading) return state; + if (state.config && state.buckets) return state; + state.loading = true; + try { + if (!state.config) { + try { + state.config = await awApiJson("/js/aw-host-groups.json?v=" + encodeURIComponent(window.__awRuPatchVersion), { method: "GET", headers: {} }); + } catch (error) { + state.config = getDefaultHostGroupsConfig(); + } + } + if (!state.buckets) { + state.buckets = await awApiJson("/api/0/buckets/", { method: "GET", headers: {} }); + } + } finally { + state.loading = false; + } + return state; + } + + function isPveActivityRoute() { + const hash = window.location.hash || ""; + const match = hash.match(/^#\/activity\/([^/]+)/i); + return !!(match && isPveLikeHost(decodeURIComponent(match[1] || ""))); + } + + function extractHostFromBucket(bucketId, bucketMeta) { + if (bucketMeta && bucketMeta.hostname) return String(bucketMeta.hostname); + const prefixes = [ + "aw-watcher-window_", + "aw-watcher-afk_", + "aw-dlp-endpoint-signals_", + "aw-dlp-incidents_", + "aw-pfsense-health_", + "aw-pfsense-gateways_", + "aw-pfsense-interfaces_" + ]; + for (const prefix of prefixes) { + if (bucketId.indexOf(prefix) === 0) { + return bucketId.slice(prefix.length); + } + } + return ""; + } + + function buildHostBucketMap(rawBuckets) { + const result = new Map(); + const entries = Array.isArray(rawBuckets) + ? rawBuckets.map(function (item) { return [item.id || "", item]; }) + : Object.entries(rawBuckets || {}); + entries.forEach(function (entry) { + const bucketId = entry[0]; + const meta = entry[1] || {}; + const host = extractHostFromBucket(bucketId, meta); + if (!host) return; + if (!result.has(host)) result.set(host, []); + result.get(host).push(bucketId); + }); + return result; + } + + function matchHostGroup(host, groups) { + for (const group of groups) { + const patterns = Array.isArray(group.patterns) ? group.patterns : []; + for (const pattern of patterns) { + try { + if (new RegExp(pattern, "i").test(host)) { + return group.id; + } + } catch (error) { + } + } + } + return ""; + } + + function buildHostLink(host, hostBuckets, linkDef) { + if (!linkDef || !linkDef.type) return ""; + if (linkDef.type === "activity") { + const viewId = linkDef.view ? String(linkDef.view) : "summary"; + return '#/activity/' + encodeURIComponent(host) + '/day/' + encodeURIComponent(new Date().toISOString().slice(0, 10)) + '/view/' + encodeURIComponent(viewId); + } + if (linkDef.type === "buckets") { + return "#/buckets"; + } + if (linkDef.type === "bucket" && linkDef.bucket_prefix) { + const bucketId = String(linkDef.bucket_prefix) + host; + return hostBuckets.indexOf(bucketId) >= 0 ? '#/buckets/' + encodeURIComponent(bucketId) : ""; + } + return ""; + } + + function renderHostGroupCards(state) { + const config = state.config || getDefaultHostGroupsConfig(); + const groups = Array.isArray(config.groups) ? config.groups : []; + const hostBuckets = buildHostBucketMap(state.buckets); + const grouped = new Map(); + + groups.forEach(function (group) { + grouped.set(group.id, []); + }); + grouped.set("__ungrouped__", []); + + Array.from(hostBuckets.keys()).sort().forEach(function (host) { + const groupId = matchHostGroup(host, groups) || "__ungrouped__"; + grouped.get(groupId).push(host); + }); + + const cards = []; + groups.forEach(function (group) { + const hosts = grouped.get(group.id) || []; + const items = hosts.map(function (host) { + const links = (group.links || []).map(function (linkDef) { + const href = buildHostLink(host, hostBuckets.get(host) || [], linkDef); + return href ? '' + escapeHtml(linkDef.label || "Открыть") + '' : ""; + }).filter(Boolean).join(""); + return '
' + + '
' + escapeHtml(host) + '
' + + '' + + '
'; + }).join(""); + cards.push( + '
' + + '

' + escapeHtml(group.name || group.id) + '

' + + '

' + escapeHtml(group.description || "") + '

' + + '
' + (items || '
Хосты пока не обнаружены.
') + '
' + + '
' + ); + }); + + const ungroupedHosts = grouped.get("__ungrouped__") || []; + if (ungroupedHosts.length) { + cards.push( + '
' + + '

' + escapeHtml(config.ungrouped_name || "Прочие хосты") + '

' + + '

Хосты, которые пока не попали под шаблоны группировки.

' + + '
' + + ungroupedHosts.map(function (host) { + return '
' + escapeHtml(host) + '
'; + }).join("") + + '
' + + '
' + ); + } + + return cards.join(""); + } + + async function injectHostGroupsCenter(root) { + if (!isHomeRoute()) return; + const heading = root.querySelector("h3"); + if (!heading) return; + + let center = root.querySelector("[data-aw-ru-host-groups='1']"); + if (!center) { + center = document.createElement("section"); + center.className = "aw-ru-host-groups"; + center.setAttribute("data-aw-ru-host-groups", "1"); + center.innerHTML = + '

Разделы хостов

' + + '

Здесь хосты разделены на пользовательские Windows RDP и инфраструктурные виртуальные серверы/Proxmox.

' + + '

Загрузка...

'; + heading.parentElement.insertBefore(center, heading.nextSibling); + } + + const state = await ensureHostGroupsData(); + center.querySelector("[data-aw-ru-host-groups-grid]").innerHTML = renderHostGroupCards(state); + } + + function renderDlpTableRows(center, host) { + const state = getSuppressionState(); + const tbody = center.querySelector("[data-aw-ru-dlp-events]"); + if (!tbody) return; + const hideSuppressed = center.querySelector("[data-aw-ru-hide-suppressed]") && center.querySelector("[data-aw-ru-hide-suppressed]").checked; + const rows = []; + for (const event of state.events) { + const matchedRule = state.activeRules.find(function (rule) { return ruleMatchesEvent(rule, event); }) || null; + if (hideSuppressed && matchedRule) continue; + const data = event.data || {}; + const eventKey = buildDlpKey(event); + rows.push( + '' + + "" + escapeHtml(new Date(event.timestamp).toLocaleString()) + "" + + "" + escapeHtml(data.signalType || "") + "" + + "" + escapeHtml(data.username || data.owner || "") + "" + + "" + escapeHtml(data.documentName || "") + "" + + "" + escapeHtml(data.printerName || "") + "" + + "" + (matchedRule ? 'Подавлено правилом' : "") + "" + + '" + + '' + + '' + + '' + + '' + + '' + + "" + + "" + ); + } + tbody.innerHTML = rows.length ? rows.join("") : 'Нет DLP-событий в выборке.'; + center.querySelector("[data-aw-ru-dlp-status]").textContent = + "Событий: " + state.events.length + " · правил: " + state.activeRules.length + "/" + state.rules.length + " · review: " + state.reviews.filter(function (review) { return !(review.data && review.data.review && review.data.review.archived); }).length + "/" + state.reviews.length; + bindDlpRowActions(center, host); + renderDlpRuleManager(center, host); + renderDlpReviewManager(center, host); + } + + async function saveDlpReview(host, event, row) { + const bucketId = "aw-dlp-review_" + host; + await ensureAwBucket(bucketId, "aw-dlp-review", "aw.dlp.review", host); + const verdict = row.querySelector("[data-aw-ru-dlp-verdict]").value; + const category = row.querySelector("[data-aw-ru-dlp-category]").value.trim(); + const comment = row.querySelector("[data-aw-ru-dlp-comment]").value.trim(); + await saveAwHeartbeat(bucketId, { + timestamp: new Date().toISOString(), + duration: 0, + data: { + host: host, + sourceBucket: getDlpBucketIdFromHash(), + sourceEvent: { + timestamp: event.timestamp, + data: event.data || {} + }, + review: { + reviewId: generateDlpId("review"), + verdict: verdict, + category: category, + comment: comment, + archived: false + } + } + }, 1); + if (verdict === "incident") { + await saveDlpIncident(host, event, { + verdict: verdict, + category: category, + comment: comment + }); + } + } + + async function saveDlpIncident(host, event, review) { + const bucketId = "aw-dlp-incidents_" + host; + await ensureAwBucket(bucketId, "aw-dlp-incidents", "aw.dlp.incident", host); + await saveAwHeartbeat(bucketId, { + timestamp: new Date().toISOString(), + duration: 0, + data: { + host: host, + sourceBucket: getDlpBucketIdFromHash(), + sourceEvent: { + timestamp: event.timestamp, + data: event.data || {} + }, + incident: { + incidentId: generateDlpId("incident"), + verdict: review && review.verdict || "incident", + category: review && review.category || "", + comment: review && review.comment || "", + status: "open" + } + } + }, 1); + } + + async function saveDlpRule(host, event, row) { + const bucketId = "aw-dlp-rules_" + host; + await ensureAwBucket(bucketId, "aw-dlp-rules", "aw.dlp.rule", host); + const verdict = row.querySelector("[data-aw-ru-dlp-verdict]").value; + const category = row.querySelector("[data-aw-ru-dlp-category]").value.trim(); + const comment = row.querySelector("[data-aw-ru-dlp-comment]").value.trim(); + await saveAwHeartbeat(bucketId, { + timestamp: new Date().toISOString(), + duration: 0, + data: { + host: host, + ruleId: generateDlpId("rule"), + enabled: true, + action: verdict, + category: category, + comment: comment, + match: getRuleMatchFields(event) + } + }, 1); + } + + function bindDlpRowActions(center, host) { + const state = getSuppressionState(); + const rows = center.querySelectorAll("[data-aw-ru-dlp-key]"); + rows.forEach(function (row) { + if (row.getAttribute("data-aw-ru-bound") === "1") return; + row.setAttribute("data-aw-ru-bound", "1"); + const eventKey = row.getAttribute("data-aw-ru-dlp-key"); + const event = state.events.find(function (item) { return buildDlpKey(item) === eventKey; }); + if (!event) return; + row.querySelector("[data-aw-ru-save-review]").addEventListener("click", async function () { + const message = center.querySelector("[data-aw-ru-dlp-message]"); + try { + await saveDlpReview(host, event, row); + state.reviews = collapseReviewEvents(await loadBucketEvents("aw-dlp-review_" + host, 200)); + renderDlpTableRows(center, host); + message.textContent = "Review сохранен."; + } catch (error) { + message.textContent = "Ошибка сохранения review: " + error.message; + } + }); + row.querySelector("[data-aw-ru-save-rule]").addEventListener("click", async function () { + const message = center.querySelector("[data-aw-ru-dlp-message]"); + try { + await saveDlpRule(host, event, row); + state.rules = collapseRuleEvents(await loadBucketEvents("aw-dlp-rules_" + host, 200)); + state.activeRules = state.rules.filter(function (rule) { return !(rule.data && rule.data.enabled === false); }); + message.textContent = "Правило сохранено."; + renderDlpTableRows(center, host); + } catch (error) { + message.textContent = "Ошибка сохранения правила: " + error.message; + } + }); + }); + } + + async function setDlpRuleEnabled(host, ruleEvent, enabled) { + const bucketId = "aw-dlp-rules_" + host; + await ensureAwBucket(bucketId, "aw-dlp-rules", "aw.dlp.rule", host); + const data = ruleEvent.data || {}; + await saveAwHeartbeat(bucketId, { + timestamp: new Date().toISOString(), + duration: 0, + data: { + host: host, + ruleId: getRuleId(ruleEvent), + enabled: enabled, + action: data.action || "", + category: data.category || "", + comment: data.comment || "", + match: data.match || {} + } + }, 1); + } + + async function setDlpReviewArchived(host, reviewEvent, archived) { + const bucketId = "aw-dlp-review_" + host; + await ensureAwBucket(bucketId, "aw-dlp-review", "aw.dlp.review", host); + const data = reviewEvent.data || {}; + const review = data.review || {}; + await saveAwHeartbeat(bucketId, { + timestamp: new Date().toISOString(), + duration: 0, + data: { + host: host, + sourceBucket: data.sourceBucket || getDlpBucketIdFromHash(), + sourceEvent: data.sourceEvent || {}, + review: { + reviewId: getReviewId(reviewEvent), + verdict: review.verdict || "", + category: review.category || "", + comment: review.comment || "", + archived: archived + } + } + }, 1); + } + + function renderDlpRuleManager(center, host) { + const state = getSuppressionState(); + const tbody = center.querySelector("[data-aw-ru-dlp-rules]"); + if (!tbody) return; + const showDisabled = center.querySelector("[data-aw-ru-show-disabled-rules]") && center.querySelector("[data-aw-ru-show-disabled-rules]").checked; + const rows = []; + state.rules.forEach(function (ruleEvent) { + const data = ruleEvent.data || {}; + const isEnabled = data.enabled !== false; + if (!isEnabled && !showDisabled) return; + rows.push( + '' + + '' + escapeHtml(new Date(ruleEvent.timestamp).toLocaleString()) + '' + + '' + (isEnabled ? 'Активно' : 'Отключено') + '' + + '' + escapeHtml(data.action || "") + '' + + '' + escapeHtml(data.category || "") + '' + + '' + escapeHtml(serializeRuleMatch(data.match || {})) + '' + + '' + escapeHtml(data.comment || "") + '' + + '' + + '' + + '' + + '' + ); + }); + tbody.innerHTML = rows.length ? rows.join("") : 'Сохраненных правил нет.'; + tbody.querySelectorAll("[data-aw-ru-toggle-rule]").forEach(function (button) { + button.addEventListener("click", async function () { + const row = button.closest("[data-aw-ru-rule-id]"); + const ruleId = row && row.getAttribute("data-aw-ru-rule-id"); + const ruleEvent = state.rules.find(function (item) { return getRuleId(item) === ruleId; }); + if (!ruleEvent) return; + const message = center.querySelector("[data-aw-ru-dlp-message]"); + try { + await setDlpRuleEnabled(host, ruleEvent, ruleEvent.data && ruleEvent.data.enabled === false); + state.rules = collapseRuleEvents(await loadBucketEvents("aw-dlp-rules_" + host, 200)); + state.activeRules = state.rules.filter(function (rule) { return !(rule.data && rule.data.enabled === false); }); + renderDlpTableRows(center, host); + message.textContent = "Статус правила обновлен."; + } catch (error) { + message.textContent = "Ошибка обновления правила: " + error.message; + } + }); + }); + } + + function renderDlpReviewManager(center, host) { + const state = getSuppressionState(); + const tbody = center.querySelector("[data-aw-ru-dlp-reviews]"); + if (!tbody) return; + const showArchived = center.querySelector("[data-aw-ru-show-archived-reviews]") && center.querySelector("[data-aw-ru-show-archived-reviews]").checked; + const rows = []; + state.reviews.forEach(function (reviewEvent) { + const data = reviewEvent.data || {}; + const review = data.review || {}; + const archived = review.archived === true; + if (archived && !showArchived) return; + const sourceData = data.sourceEvent && data.sourceEvent.data ? data.sourceEvent.data : {}; + rows.push( + '' + + '' + escapeHtml(new Date(reviewEvent.timestamp).toLocaleString()) + '' + + '' + (archived ? 'Архив' : 'Активно') + '' + + '' + escapeHtml(review.verdict || "") + '' + + '' + escapeHtml(review.category || "") + '' + + '' + escapeHtml(review.comment || "") + '' + + '' + escapeHtml(sourceData.signalType || "") + ' · ' + escapeHtml(sourceData.documentName || sourceData.printerName || sourceData.username || "") + '' + + '' + + '' + + '' + + '' + ); + }); + tbody.innerHTML = rows.length ? rows.join("") : 'Сохраненных review нет.'; + tbody.querySelectorAll("[data-aw-ru-toggle-review]").forEach(function (button) { + button.addEventListener("click", async function () { + const row = button.closest("[data-aw-ru-review-id]"); + const reviewId = row && row.getAttribute("data-aw-ru-review-id"); + const reviewEvent = state.reviews.find(function (item) { return getReviewId(item) === reviewId; }); + if (!reviewEvent) return; + const message = center.querySelector("[data-aw-ru-dlp-message]"); + try { + await setDlpReviewArchived(host, reviewEvent, !(reviewEvent.data && reviewEvent.data.review && reviewEvent.data.review.archived === true)); + state.reviews = collapseReviewEvents(await loadBucketEvents("aw-dlp-review_" + host, 200)); + renderDlpTableRows(center, host); + message.textContent = "Статус review обновлен."; + } catch (error) { + message.textContent = "Ошибка обновления review: " + error.message; + } + }); + }); + } + + async function refreshDlpCenter(center, host) { + const state = getSuppressionState(); + if (state.loading) return; + state.loading = true; + center.querySelector("[data-aw-ru-dlp-message]").textContent = "Загрузка DLP-событий..."; + try { + state.events = (await loadBucketEvents(getDlpBucketIdFromHash(), 200)) + .sort(function (a, b) { return String(b.timestamp).localeCompare(String(a.timestamp)); }); + try { + state.rules = collapseRuleEvents(await loadBucketEvents("aw-dlp-rules_" + host, 200)); + state.activeRules = state.rules.filter(function (rule) { return !(rule.data && rule.data.enabled === false); }); + } catch (error) { + state.rules = []; + state.activeRules = []; + } + try { + state.reviews = collapseReviewEvents(await loadBucketEvents("aw-dlp-review_" + host, 200)); + } catch (error) { + state.reviews = []; + } + renderDlpTableRows(center, host); + center.querySelector("[data-aw-ru-dlp-message]").textContent = "DLP review центр обновлен."; + } catch (error) { + center.querySelector("[data-aw-ru-dlp-message]").textContent = "Ошибка загрузки DLP-событий: " + error.message; + } finally { + state.loading = false; + } + } + + function injectDlpReviewCenter(root) { + if (!isDlpSignalBucketRoute()) return; + const bucketId = getDlpBucketIdFromHash(); + const host = getDlpHostFromBucketId(bucketId); + if (!host) return; + + const heading = root.querySelector("h3"); + if (!heading) return; + + let center = root.querySelector("[data-aw-ru-dlp-center='1']"); + if (!center) { + center = document.createElement("section"); + center.className = "aw-ru-dlp-center"; + center.setAttribute("data-aw-ru-dlp-center", "1"); + center.innerHTML = + '

DLP review и правила

' + + '
' + + 'bucket: ' + escapeHtml(bucketId) + '' + + '' + + '' + + '
Событий: 0 · правил: 0 · review: 0
' + + '
' + + '

Здесь можно категорировать DLP-события и сохранять suppress/rule записи прямо в AW. Это снижает ложные сработки на уровне review-потока.

' + + '' + + '' + + '' + + '
ВремяТипПользовательДокументПринтер/каналСтатусВердиктКатегорияКомментарийДействия
Загрузка...
' + + '
' + + '
' + + '
DLP Rules
' + + '' + + '
' + + '' + + '' + + '' + + '
ВремяСтатусДействиеКатегорияMatchКомментарийУправление
Загрузка...
' + + '
' + + '
' + + '
' + + '
DLP Review
' + + '' + + '
' + + '' + + '' + + '' + + '
ВремяСтатусВердиктКатегорияКомментарийИсточникУправление
Загрузка...
' + + '
' + + '
'; + heading.parentElement.insertBefore(center, heading.nextSibling); + center.querySelector("[data-aw-ru-refresh-dlp]").addEventListener("click", function () { + refreshDlpCenter(center, host); + }); + center.querySelector("[data-aw-ru-hide-suppressed]").addEventListener("change", function () { + renderDlpTableRows(center, host); + }); + center.querySelector("[data-aw-ru-show-disabled-rules]").addEventListener("change", function () { + renderDlpRuleManager(center, host); + }); + center.querySelector("[data-aw-ru-show-archived-reviews]").addEventListener("change", function () { + renderDlpReviewManager(center, host); + }); + } + + if (center.getAttribute("data-aw-ru-loaded") !== "1") { + center.setAttribute("data-aw-ru-loaded", "1"); + refreshDlpCenter(center, host); + } + } + + async function refreshDlpAlertsCenter(center, host) { + center.querySelector("[data-aw-ru-dlp-alerts-message]").textContent = "Загрузка DLP-инцидентов..."; + try { + const events = (await loadBucketEvents("aw-dlp-incidents_" + host, 100)) + .sort(function (a, b) { return String(b.timestamp).localeCompare(String(a.timestamp)); }); + const rows = events.map(function (event) { + const data = event.data || {}; + const incident = data.incident || {}; + const sourceData = data.sourceEvent && data.sourceEvent.data ? data.sourceEvent.data : {}; + return '' + + '' + escapeHtml(new Date(event.timestamp).toLocaleString()) + '' + + '' + escapeHtml(incident.status || "open") + '' + + '' + escapeHtml(incident.category || "") + '' + + '' + escapeHtml(incident.comment || "") + '' + + '' + escapeHtml(sourceData.username || sourceData.owner || "") + '' + + '' + escapeHtml(sourceData.signalType || "") + '' + + '' + escapeHtml(sourceData.documentName || sourceData.printerName || "") + '' + + ''; + }); + center.querySelector("[data-aw-ru-dlp-alerts-events]").innerHTML = rows.length + ? rows.join("") + : 'Операторских DLP-инцидентов пока нет.'; + center.querySelector("[data-aw-ru-dlp-alerts-status]").textContent = "Инцидентов: " + events.length; + center.querySelector("[data-aw-ru-dlp-alerts-message]").textContent = "Список DLP-инцидентов обновлен."; + } catch (error) { + center.querySelector("[data-aw-ru-dlp-alerts-events]").innerHTML = 'Не удалось загрузить DLP-инциденты.'; + center.querySelector("[data-aw-ru-dlp-alerts-message]").textContent = "Ошибка загрузки DLP-инцидентов: " + error.message; + } + } + + async function refreshPveAuditCenter(center, host) { + const message = center.querySelector("[data-aw-ru-pve-audit-message]"); + const recentBody = center.querySelector("[data-aw-ru-pve-audit-events]"); + message.textContent = "Загрузка audit-событий..."; + try { + const [webEvents, taskEvents, sshEvents, cmdEvents] = await Promise.all([ + loadBucketEvents("aw-pve-webadmin-events_" + host, 50).catch(function () { return []; }), + loadBucketEvents("aw-pve-task-events_" + host, 50).catch(function () { return []; }), + loadBucketEvents("aw-ssh-sessions_" + host, 50).catch(function () { return []; }), + loadBucketEvents("aw-console-commands_" + host, 50).catch(function () { return []; }) + ]); + const data = { + web: webEvents || [], + tasks: taskEvents || [], + ssh: sshEvents || [], + cmd: cmdEvents || [] + }; + center.querySelector("[data-aw-ru-pve-web-count]").textContent = String(data.web.length); + center.querySelector("[data-aw-ru-pve-task-count]").textContent = String(data.tasks.length); + center.querySelector("[data-aw-ru-pve-ssh-count]").textContent = String(data.ssh.length); + center.querySelector("[data-aw-ru-pve-cmd-count]").textContent = String(data.cmd.length); + const recent = [] + .concat(data.web.map(function (event) { return { kind: "Web-admin", event: event, text: (event.data && (event.data.method || "") + " " + (event.data.path || "")) || "" }; })) + .concat(data.tasks.map(function (event) { return { kind: "PVE task", event: event, text: (event.data && ((event.data.action || "") + " " + (event.data.target || ""))) || "" }; })) + .concat(data.ssh.map(function (event) { return { kind: "SSH", event: event, text: (event.data && ((event.data.event || "") + " " + (event.data.tty || ""))) || "" }; })) + .concat(data.cmd.slice(0, 25).map(function (event) { return { kind: "Shell", event: event, text: (event.data && (event.data.command || "")) || "" }; })) + .sort(function (a, b) { return String(b.event && b.event.timestamp || "").localeCompare(String(a.event && a.event.timestamp || "")); }) + .slice(0, 25); + recentBody.innerHTML = recent.length ? recent.map(function (item) { + const ev = item.event || {}; + const d = ev.data || {}; + return "" + + "" + escapeHtml(new Date(ev.timestamp).toLocaleString()) + "" + + "" + escapeHtml(item.kind) + "" + + "" + escapeHtml(d.user || d.username || "-") + "" + + "" + escapeHtml(d.remote_ip || d.tty || d.host || "-") + "" + + "" + escapeHtml(item.text) + "" + + ""; + }).join("") : 'Пока нет audit-событий.'; + message.textContent = "Audit-панель обновлена."; + } catch (error) { + recentBody.innerHTML = 'Не удалось загрузить audit-события.'; + message.textContent = "Ошибка загрузки audit-событий: " + error.message; + } + } + + function injectPveAuditCenter(root) { + if (!isPveActivityRoute()) return; + const host = getCurrentHostFromHash(); + if (!host) return; + const heading = root.querySelector("h3"); + if (!heading || !heading.parentElement) return; + let center = root.querySelector("[data-aw-ru-pve-audit='1']"); + if (!center) { + center = document.createElement("section"); + center.className = "aw-ru-pve-audit"; + center.setAttribute("data-aw-ru-pve-audit", "1"); + center.innerHTML = + "

PVE Audit

" + + '

Для Proxmox-хоста показывается audit-панель вместо desktop-виджетов ActivityWatch, так как у этого хоста нет window/afk watcher данных.

' + + '
' + + '
Web-admin
0
' + + '
PVE tasks
0
' + + '
SSH events
0
' + + '
Shell commands
0
' + + "
" + + '' + + "" + + '' + + "
ВремяТипПользовательИсточникДетали
Загрузка...
" + + '
'; + heading.parentElement.insertBefore(center, heading.nextSibling); + } + Array.from(heading.parentElement.children).forEach(function (child) { + if (child === heading || child === center) return; + child.style.display = "none"; + }); + const routeKey = host + "|" + (window.location.hash || ""); + if (center.getAttribute("data-aw-ru-pve-route") !== routeKey) { + center.setAttribute("data-aw-ru-pve-route", routeKey); + refreshPveAuditCenter(center, host); + } + } + + function injectDlpAlertsCenter(root) { + if (!isAlertsRoute()) return; + const host = window.__awRuPatchSettingsHost || getCurrentHostFromHash(); + if (!host) return; + + const heading = root.querySelector("h3"); + if (!heading) return; + + let center = root.querySelector("[data-aw-ru-dlp-alerts='1']"); + if (!center) { + center = document.createElement("section"); + center.className = "aw-ru-dlp-center"; + center.setAttribute("data-aw-ru-dlp-alerts", "1"); + center.innerHTML = + '

DLP-инциденты оператора

' + + '
' + + 'bucket: ' + escapeHtml("aw-dlp-incidents_" + host) + '' + + '' + + '
Инцидентов: 0
' + + '
' + + '

Здесь выводятся DLP-события, которые оператор вручную признал инцидентами через review-центр.

' + + '' + + '' + + '' + + '
ВремяСтатусКатегорияКомментарийПользовательТипДокумент/канал
Загрузка...
' + + '
'; + heading.parentElement.insertBefore(center, heading.nextSibling); + center.querySelector("[data-aw-ru-refresh-dlp-alerts]").addEventListener("click", function () { + refreshDlpAlertsCenter(center, host); + }); + } + + if (center.getAttribute("data-aw-ru-loaded") !== "1") { + center.setAttribute("data-aw-ru-loaded", "1"); + refreshDlpAlertsCenter(center, host); + } + } + + let trendsRedirectInFlight = false; + let settingsHostFetchInFlight = false; + let applyPatchScheduled = false; + + function getTrendsHostFromSettings(settings) { + if (!settings || typeof settings !== "object") return ""; + const landingpage = typeof settings.landingpage === "string" ? settings.landingpage : ""; + const match = landingpage.match(/\/activity\/([^/]+)/); + return match && match[1] ? match[1] : ""; + } + + function getTrendsPath(hash) { + if (!hash) return ""; + const normalized = hash.startsWith("#") ? hash.slice(1) : hash; + return normalized.split("?")[0]; + } + + function shouldRedirectTrends(hash) { + const path = getTrendsPath(hash); + return path === "/trends" || path === "/trends/"; + } + + function redirectBareTrendsRoute() { + if (trendsRedirectInFlight || !shouldRedirectTrends(window.location.hash)) return; + trendsRedirectInFlight = true; + fetch("/api/0/settings/", { credentials: "same-origin" }) + .then(function (response) { + if (!response.ok) throw new Error("settings-fetch-failed"); + return response.json(); + }) + .then(function (settings) { + window.__awRuPatchSettingsHost = getDlpHostFromSettings(settings); + const host = getTrendsHostFromSettings(settings); + if (!host || !shouldRedirectTrends(window.location.hash)) return; + const target = "#/trends/" + encodeURIComponent(host); + if (window.location.hash !== target) { + window.location.replace(target); + } + }) + .catch(function () {}) + .finally(function () { + trendsRedirectInFlight = false; + }); + } + + function ensureSettingsHost() { + if (window.__awRuPatchSettingsHost || settingsHostFetchInFlight) return; + settingsHostFetchInFlight = true; + fetch("/api/0/settings/", { credentials: "same-origin" }) + .then(function (response) { + if (!response.ok) throw new Error("settings-fetch-failed"); + return response.json(); + }) + .then(function (settings) { + window.__awRuPatchSettingsHost = getDlpHostFromSettings(settings); + }) + .catch(function () {}) + .finally(function () { + settingsHostFetchInFlight = false; + injectDlpNavigation(document.body); + }); + } + + function applyPatch() { + enforceSafeActivityViewForPveHost(); + ensureSettingsHost(); + injectStyles(); + walk(document.body); + translateAttributes(document.body); + hideNoiseNavigation(document.body); + injectPveAuditCenter(document.body); + injectDlpNavigation(document.body); + injectDlpReviewCenter(document.body); + injectDlpAlertsCenter(document.body); + injectHostGroupsCenter(document.body).catch(function () {}); + redirectBareTrendsRoute(); + } + + function scheduleApplyPatch() { + if (applyPatchScheduled) return; + applyPatchScheduled = true; + window.setTimeout(function () { + applyPatchScheduled = false; + applyPatch(); + }, 50); + } + + const observer = new MutationObserver(function () { + scheduleApplyPatch(); + }); + + window.addEventListener("load", function () { + applyPatch(); + observer.observe(document.body, { childList: true, subtree: true }); + }); + window.addEventListener("hashchange", function () { + redirectBareTrendsRoute(); + scheduleApplyPatch(); + }); +})(); diff --git a/install-kit-awindows-20260427-211240/aw-server/aw-server.env.example b/install-kit-awindows-20260427-211240/aw-server/aw-server.env.example new file mode 100755 index 0000000..93ef939 --- /dev/null +++ b/install-kit-awindows-20260427-211240/aw-server/aw-server.env.example @@ -0,0 +1,11 @@ +# Copy to /etc/activitywatch/aw-server.env and fill with real values. + +AW_SERVER_VERSION=0.13.2 +AW_SERVER_DOWNLOAD_URL=https://github.com/ActivityWatch/aw-server-rust/releases/download/v0.13.2/aw-server-rust-linux-x86_64.zip +AW_SERVER_BIND_HOST=0.0.0.0 +AW_SERVER_PORT=5600 +AW_SERVER_WEBUI_DIR=/opt/activitywatch/webui-ru +AW_SERVER_DATA_DIR=/var/lib/activitywatch +AW_SERVER_LOG_DIR=/var/log/activitywatch +AW_SERVER_USER=activitywatch +AW_SERVER_GROUP=activitywatch diff --git a/install-kit-awindows-20260427-211240/aw-server/aw-sw-cleanup.js b/install-kit-awindows-20260427-211240/aw-server/aw-sw-cleanup.js new file mode 100755 index 0000000..b7aa742 --- /dev/null +++ b/install-kit-awindows-20260427-211240/aw-server/aw-sw-cleanup.js @@ -0,0 +1,18 @@ +self.addEventListener("install", function (event) { + self.skipWaiting(); + event.waitUntil((async function () { + const keys = await caches.keys(); + await Promise.all(keys.map(function (key) { return caches.delete(key); })); + })()); +}); + +self.addEventListener("activate", function (event) { + event.waitUntil((async function () { + const keys = await caches.keys(); + await Promise.all(keys.map(function (key) { return caches.delete(key); })); + await self.clients.claim(); + await self.registration.unregister(); + })()); +}); + +self.addEventListener("fetch", function () {}); diff --git a/install-kit-awindows-20260427-211240/aw-server/install_aw_server.sh b/install-kit-awindows-20260427-211240/aw-server/install_aw_server.sh new file mode 100755 index 0000000..4d6262e --- /dev/null +++ b/install-kit-awindows-20260427-211240/aw-server/install_aw_server.sh @@ -0,0 +1,117 @@ +#!/bin/bash +set -euo pipefail + +ENV_FILE="/etc/activitywatch/aw-server.env" +if [[ ! -f "$ENV_FILE" ]]; then + echo "missing env file: $ENV_FILE" >&2 + exit 1 +fi + +source "$ENV_FILE" + +required_vars=( + AW_SERVER_VERSION + AW_SERVER_DOWNLOAD_URL + AW_SERVER_BIND_HOST + AW_SERVER_PORT + AW_SERVER_WEBUI_DIR + AW_SERVER_DATA_DIR + AW_SERVER_LOG_DIR + AW_SERVER_USER + AW_SERVER_GROUP +) + +BOOTSTRAP_DIR="/root/bootstrap" +VIEWS_JSON="$BOOTSTRAP_DIR/settings/views-default.json" +CLASSES_JSON="$BOOTSTRAP_DIR/settings/classes-worktime.json" + +for var_name in "${required_vars[@]}"; do + if [[ -z "${!var_name:-}" ]]; then + echo "missing required variable: $var_name" >&2 + exit 1 + fi +done + +export DEBIAN_FRONTEND=noninteractive +apt-get update +apt-get install -y curl ca-certificates unzip jq + +if ! getent group "$AW_SERVER_GROUP" >/dev/null; then + groupadd --system "$AW_SERVER_GROUP" +fi + +if ! id "$AW_SERVER_USER" >/dev/null 2>&1; then + useradd --system --gid "$AW_SERVER_GROUP" --home-dir "$AW_SERVER_DATA_DIR" --shell /usr/sbin/nologin "$AW_SERVER_USER" +fi + +install -d -o "$AW_SERVER_USER" -g "$AW_SERVER_GROUP" /opt/activitywatch/bin +install -d -o "$AW_SERVER_USER" -g "$AW_SERVER_GROUP" /opt/activitywatch/releases +install -d -o "$AW_SERVER_USER" -g "$AW_SERVER_GROUP" "$AW_SERVER_WEBUI_DIR" +install -d -o "$AW_SERVER_USER" -g "$AW_SERVER_GROUP" "$AW_SERVER_DATA_DIR" +install -d -o "$AW_SERVER_USER" -g "$AW_SERVER_GROUP" "$AW_SERVER_LOG_DIR" + +tmp_dir=$(mktemp -d) +trap 'rm -rf "$tmp_dir"' EXIT + +curl -fL "$AW_SERVER_DOWNLOAD_URL" -o "$tmp_dir/aw-server.zip" +unzip -q "$tmp_dir/aw-server.zip" -d "$tmp_dir/unpacked" + +server_bin=$(find "$tmp_dir/unpacked" -type f \( -name 'aw-server-rust' -o -name 'aw-server' \) | head -n 1) +webui_dir=$(find "$tmp_dir/unpacked" -type d \( -name 'webui' -o -name 'aw-webui' \) | head -n 1 || true) + +if [[ -z "$server_bin" || ! -f "$server_bin" ]]; then + echo "aw-server binary not found in archive" >&2 + exit 1 +fi + +release_dir="/opt/activitywatch/releases/aw-server-rust-v${AW_SERVER_VERSION}" +rm -rf "$release_dir" +install -d -o "$AW_SERVER_USER" -g "$AW_SERVER_GROUP" "$release_dir" +install -m 0755 -o "$AW_SERVER_USER" -g "$AW_SERVER_GROUP" "$server_bin" "$release_dir/aw-server-rust" +ln -sfn "$release_dir/aw-server-rust" /opt/activitywatch/bin/aw-server-rust + +if [[ -n "$webui_dir" && -d "$webui_dir" ]]; then + rm -rf "$AW_SERVER_WEBUI_DIR" + mkdir -p "$AW_SERVER_WEBUI_DIR" + cp -a "$webui_dir"/. "$AW_SERVER_WEBUI_DIR"/ + chown -R "$AW_SERVER_USER:$AW_SERVER_GROUP" "$AW_SERVER_WEBUI_DIR" +fi + +sed \ + -e "s#__AW_SERVER_USER__#$AW_SERVER_USER#g" \ + -e "s#__AW_SERVER_GROUP__#$AW_SERVER_GROUP#g" \ + -e "s#__AW_SERVER_DATA_DIR__#$AW_SERVER_DATA_DIR#g" \ + /root/bootstrap/activitywatch-server.service > /etc/systemd/system/activitywatch-server.service +chmod 0644 /etc/systemd/system/activitywatch-server.service + +systemctl daemon-reload +systemctl enable activitywatch-server.service +systemctl restart activitywatch-server.service +systemctl --no-pager --full status activitywatch-server.service || true + +for _ in $(seq 1 20); do + if curl -fsS "http://127.0.0.1:${AW_SERVER_PORT}/api/0/info" >/dev/null 2>&1; then + break + fi + sleep 2 +done + +if [[ -f "$CLASSES_JSON" ]]; then + curl -fsS -X POST \ + -H 'Content-Type: application/json' \ + --data-binary @"$CLASSES_JSON" \ + "http://127.0.0.1:${AW_SERVER_PORT}/api/0/settings/classes" >/dev/null + echo "Applied worktime classes from $CLASSES_JSON" +else + echo "Worktime classes bootstrap not found, skipped: $CLASSES_JSON" +fi + +if [[ -f "$VIEWS_JSON" ]]; then + curl -fsS -X POST \ + -H 'Content-Type: application/json' \ + --data-binary @"$VIEWS_JSON" \ + "http://127.0.0.1:${AW_SERVER_PORT}/api/0/settings/views" >/dev/null + echo "Applied baseline views from $VIEWS_JSON" +else + echo "Views bootstrap not found, skipped: $VIEWS_JSON" +fi diff --git a/install-kit-awindows-20260427-211240/aw-server/settings/classes-worktime.json b/install-kit-awindows-20260427-211240/aw-server/settings/classes-worktime.json new file mode 100644 index 0000000..b6361f4 --- /dev/null +++ b/install-kit-awindows-20260427-211240/aw-server/settings/classes-worktime.json @@ -0,0 +1,90 @@ +[ + { + "id": 0, + "name": ["Работа"], + "rule": { "type": "none" }, + "data": {} + }, + { + "id": 1, + "name": ["Работа", "1С"], + "rule": { + "type": "regex", + "regex": "\\b(1cv8s?|1cv8c|1cestart)\\.exe\\b|1С:Предприятие|Запуск 1С:Предприятия|Загрузка конфигурационной информации|Доступ к информационной базе", + "ignore_case": true + }, + "data": { "color": "#194D33" } + }, + { + "id": 2, + "name": ["Работа", "Документы"], + "rule": { + "type": "regex", + "regex": "\\b(winword|excel|powerpnt|outlook|acrord32|acrord64)\\.exe\\b|Adobe Reader|Acrobat", + "ignore_case": true + }, + "data": { "color": "#2E7D32" } + }, + { + "id": 3, + "name": ["Работа", "Коммуникации"], + "rule": { + "type": "regex", + "regex": "\\b(teams|telegram|slack|thunderbird|zoom|skype|whatsapp|viber|discord)\\.exe\\b|Mattermost|Element|Riot", + "ignore_case": true + }, + "data": { "color": "#1E88E5" } + }, + { + "id": 4, + "name": ["Работа", "Администрирование"], + "rule": { + "type": "regex", + "regex": "\\b(mstsc|putty|kitty|winscp|anydesk|teamviewer|vncviewer|mmc|regedit|services|control|powershell|cmd)\\.exe\\b", + "ignore_case": true + }, + "data": { "color": "#6D4C41" } + }, + { + "id": 5, + "name": ["Интернет"], + "rule": { "type": "none" }, + "data": {} + }, + { + "id": 6, + "name": ["Интернет", "Браузер"], + "rule": { + "type": "regex", + "regex": "\\b(chrome|msedge|firefox|opera|brave|vivaldi|browser)\\.exe\\b", + "ignore_case": true + }, + "data": { "color": "#00897B" } + }, + { + "id": 7, + "name": ["Система"], + "rule": { "type": "none" }, + "data": {} + }, + { + "id": 8, + "name": ["Система", "Windows"], + "rule": { + "type": "regex", + "regex": "\\b(SearchHost|explorer|ShellExperienceHost|ApplicationFrameHost|RuntimeBroker|sihost|dwm|svchost|fontdrvhost|userinit)\\.exe\\b|\\\\Windows\\\\System32", + "ignore_case": true + }, + "data": { "color": "#607D8B" } + }, + { + "id": 9, + "name": ["ActivityWatch"], + "rule": { + "type": "regex", + "regex": "ActivityWatch|\\baw-(watcher|qt)\\.exe\\b", + "ignore_case": true + }, + "data": {} + } +] diff --git a/install-kit-awindows-20260427-211240/aw-server/settings/views-default.json b/install-kit-awindows-20260427-211240/aw-server/settings/views-default.json new file mode 100644 index 0000000..439fce6 --- /dev/null +++ b/install-kit-awindows-20260427-211240/aw-server/settings/views-default.json @@ -0,0 +1,39 @@ +[ + { + "id": "summary", + "name": "Summary", + "elements": [ + { "type": "top_titles", "size": 3 }, + { "type": "timeline_barchart", "size": 3 }, + { "type": "top_categories", "size": 3 }, + { "type": "category_tree", "size": 3 } + ] + }, + { + "id": "window", + "name": "Window", + "elements": [ + { "type": "top_apps", "size": 3, "props": {} } + ] + }, + { + "id": "DLP", + "name": "DLP", + "elements": [] + }, + { + "id": "worktime", + "name": "Worktime", + "elements": [ + { "type": "top_categories", "size": 3, "props": {} }, + { "type": "timeline_barchart", "size": 3, "props": {} }, + { "type": "category_tree", "size": 3, "props": {} }, + { "type": "top_apps", "size": 3, "props": {} } + ] + }, + { + "id": "pve_audit", + "name": "PVE Audit", + "elements": [] + } +] diff --git a/proxmox/push-aw-artifacts.sh b/proxmox/push-aw-artifacts.sh index da46de2..d3b2d91 100755 --- a/proxmox/push-aw-artifacts.sh +++ b/proxmox/push-aw-artifacts.sh @@ -25,11 +25,16 @@ for file_name in \ activitywatch-server.service \ aw-server.env.example \ aw-ru-patch.js \ - aw-sw-cleanup.js + aw-sw-cleanup.js \ + aw-host-groups.json do pct push "$CT_ID" "$PROJECT_ROOT/aw-server/$file_name" "/root/bootstrap/$file_name" done +pct exec "$CT_ID" -- mkdir -p /root/bootstrap/settings +pct push "$CT_ID" "$PROJECT_ROOT/aw-server/settings/classes-worktime.json" "/root/bootstrap/settings/classes-worktime.json" +pct push "$CT_ID" "$PROJECT_ROOT/aw-server/settings/views-default.json" "/root/bootstrap/settings/views-default.json" + if [ -n "${AW_SERVER_VERSION:-}" ] && [ -n "${AW_SERVER_DOWNLOAD_URL:-}" ] && [ -n "${AW_SERVER_BIND_HOST:-}" ] &&