From 0eabc954affee4f520adcb2faf410a8e04fa6b6a Mon Sep 17 00:00:00 2001 From: Johannes Date: Mon, 22 Jun 2026 18:39:17 -0400 Subject: [PATCH 01/12] =?UTF-8?q?ci:=20rewrite=20cd-main.yml=20=E2=80=94?= =?UTF-8?q?=20production=20runner,=20pre-installed=20toolchain,=20no=20dep?= =?UTF-8?q?loy=20step?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitea/workflows/cd-main.yml | 27 +++++++-------------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/.gitea/workflows/cd-main.yml b/.gitea/workflows/cd-main.yml index 3d0d108..b5d9825 100644 --- a/.gitea/workflows/cd-main.yml +++ b/.gitea/workflows/cd-main.yml @@ -7,7 +7,7 @@ on: jobs: build-and-deploy: - runs-on: ubuntu-latest + runs-on: production env: GIT_SSL_NO_VERIFY: "true" MAVEN_OPTS: "-Dmaven.wagon.http.ssl.insecure=true -Dmaven.wagon.http.ssl.allowall=true" @@ -16,20 +16,12 @@ jobs: - name: Checkout uses: actions/checkout@v4 + - name: Verify toolchain + run: java -version && mvn -version && trivy --version + - name: Clean runner disk run: docker system prune -f 2>/dev/null || true - - name: Install Java 21 and Maven - run: | - apt-get update -qq - apt-get install -y -qq wget apt-transport-https gnupg - wget -qO - https://packages.adoptium.net/artifactory/api/gpg/key/public | gpg --dearmor -o /usr/share/keyrings/adoptium.gpg - echo "deb [signed-by=/usr/share/keyrings/adoptium.gpg] https://packages.adoptium.net/artifactory/deb $(. /etc/os-release && echo $VERSION_CODENAME) main" | tee /etc/apt/sources.list.d/adoptium.list - apt-get update -qq - apt-get install -y -qq temurin-21-jdk maven - java -version - mvn -version - - name: Configure Maven settings run: | mkdir -p ~/.m2 @@ -73,9 +65,6 @@ jobs: - name: Build JAR run: mvn clean package -DskipTests -B - - name: Install Trivy - run: curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin - - name: Trivy vulnerability scan run: | trivy fs \ @@ -83,6 +72,7 @@ jobs: --severity HIGH,CRITICAL \ --ignore-unfixed \ --scanners vuln \ + --skip-db-update \ --format table \ target/ @@ -98,15 +88,12 @@ jobs: -DautoUpdate=false \ -DdataDirectory=/tmp/owasp-data \ -DsuppressionFile=deployment/owasp-suppressions.xml \ + -DassemblyAnalyzerEnabled=false \ -Dformats=HTML,JSON \ -B - name: Build and push Docker image run: | - if ! command -v docker &> /dev/null; then - apt-get update -qq && apt-get install -y -qq docker.io - fi - DOCKER_BUILDKIT=0 docker system prune -f 2>/dev/null || true export DOCKER_BUILDKIT=0 echo "${{ secrets.REGISTRY_PASSWORD }}" | \ docker login ${{ secrets.REGISTRY_URL }} \ @@ -127,4 +114,4 @@ jobs: RUN_URL="https://hiveiq-gitea.directlx.dev/hiveiq-src/hiveops-aria/actions/runs/${{ github.run_id }}" curl -s -X POST "${{ secrets.SLACK_PROD_WEBHOOK_URL }}" \ -H "Content-Type: application/json" \ - -d "{\"attachments\":[{\"color\":\"$COLOR\",\"text\":\"*<$RUN_URL|hiveops-aria CD Production — $STATUS>*\n${{ github.ref_name }} · $SHORT_SHA · ${{ github.actor }}\",\"mrkdwn_in\":[\"text\"],\"footer\":\"Gitea Actions\"}]}" + -d "{\"attachments\":[{\"color\":\"$COLOR\",\"text\":\"*<$RUN_URL|hiveops-aria CD Production — $STATUS>*\\n${{ github.ref_name }} · $SHORT_SHA · ${{ github.actor }}\",\"mrkdwn_in\":[\"text\"],\"footer\":\"Gitea Actions\"}]}" -- 2.43.0 From 40de32e48be2b2ec8b3840f67841661616b629c3 Mon Sep 17 00:00:00 2001 From: Johannes Date: Mon, 22 Jun 2026 19:01:47 -0400 Subject: [PATCH 02/12] ci: add workflow_dispatch to cd-main for test runs --- .gitea/workflows/cd-main.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitea/workflows/cd-main.yml b/.gitea/workflows/cd-main.yml index b5d9825..1636c70 100644 --- a/.gitea/workflows/cd-main.yml +++ b/.gitea/workflows/cd-main.yml @@ -4,6 +4,7 @@ on: push: branches: - main + workflow_dispatch: jobs: build-and-deploy: -- 2.43.0 From 26ca47f1118dfe6f0fdb1c6ca2e4fa5d85bb6933 Mon Sep 17 00:00:00 2001 From: Johannes Date: Mon, 22 Jun 2026 21:58:19 -0400 Subject: [PATCH 03/12] =?UTF-8?q?ci:=20remove=20workflow=5Fdispatch=20from?= =?UTF-8?q?=20cd-main=20=E2=80=94=20prod=20Slack=20only=20fires=20on=20mai?= =?UTF-8?q?n=20pushes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitea/workflows/cd-main.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitea/workflows/cd-main.yml b/.gitea/workflows/cd-main.yml index 1636c70..b5d9825 100644 --- a/.gitea/workflows/cd-main.yml +++ b/.gitea/workflows/cd-main.yml @@ -4,7 +4,6 @@ on: push: branches: - main - workflow_dispatch: jobs: build-and-deploy: -- 2.43.0 From 14233425373cbf01e044ebe20668016bd6dc0648 Mon Sep 17 00:00:00 2001 From: Johannes Date: Mon, 22 Jun 2026 22:13:47 -0400 Subject: [PATCH 04/12] ci: remove DOCKER_BUILDKIT=0, restore workflow_dispatch (Slack gated to main only) --- .gitea/workflows/cd-main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/workflows/cd-main.yml b/.gitea/workflows/cd-main.yml index b5d9825..b560369 100644 --- a/.gitea/workflows/cd-main.yml +++ b/.gitea/workflows/cd-main.yml @@ -4,6 +4,7 @@ on: push: branches: - main + workflow_dispatch: jobs: build-and-deploy: @@ -94,7 +95,6 @@ jobs: - name: Build and push Docker image run: | - export DOCKER_BUILDKIT=0 echo "${{ secrets.REGISTRY_PASSWORD }}" | \ docker login ${{ secrets.REGISTRY_URL }} \ -u ${{ secrets.REGISTRY_USERNAME }} --password-stdin -- 2.43.0 From 6eecbf7494bbbebf89012fa7ad2d4440918b961d Mon Sep 17 00:00:00 2001 From: Johannes Date: Mon, 22 Jun 2026 22:14:52 -0400 Subject: [PATCH 05/12] ci: gate prod Slack notification to main branch only --- .gitea/workflows/cd-main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/workflows/cd-main.yml b/.gitea/workflows/cd-main.yml index b560369..d70a048 100644 --- a/.gitea/workflows/cd-main.yml +++ b/.gitea/workflows/cd-main.yml @@ -106,7 +106,7 @@ jobs: docker push ${{ secrets.REGISTRY_URL }}/hiveiq-aria:${SHORT_SHA} - name: Notify Slack - if: always() + if: always() && github.ref_name == \'main\' run: | STATUS="${{ job.status }}" [ "$STATUS" = "success" ] && COLOR="good" || COLOR="danger" -- 2.43.0 From 69eb86ea51960ec357c5b7a1334b6ae82175581a Mon Sep 17 00:00:00 2001 From: Johannes Date: Mon, 22 Jun 2026 22:19:25 -0400 Subject: [PATCH 06/12] ci: fix Slack gate condition syntax (remove escaped quotes) --- .gitea/workflows/cd-main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/workflows/cd-main.yml b/.gitea/workflows/cd-main.yml index d70a048..128f119 100644 --- a/.gitea/workflows/cd-main.yml +++ b/.gitea/workflows/cd-main.yml @@ -106,7 +106,7 @@ jobs: docker push ${{ secrets.REGISTRY_URL }}/hiveiq-aria:${SHORT_SHA} - name: Notify Slack - if: always() && github.ref_name == \'main\' + if: always() && github.ref_name == 'main' run: | STATUS="${{ job.status }}" [ "$STATUS" = "success" ] && COLOR="good" || COLOR="danger" -- 2.43.0 From 59a7e1187e9c3ab1476b008b575ed45268217541 Mon Sep 17 00:00:00 2001 From: Johannes Date: Tue, 23 Jun 2026 06:35:58 -0400 Subject: [PATCH 07/12] chore: remove Slack notification from CI workflows --- .gitea/workflows/cd-develop.yml | 12 ------------ .gitea/workflows/cd-main.yml | 11 ----------- 2 files changed, 23 deletions(-) diff --git a/.gitea/workflows/cd-develop.yml b/.gitea/workflows/cd-develop.yml index adedc3a..a6058f6 100644 --- a/.gitea/workflows/cd-develop.yml +++ b/.gitea/workflows/cd-develop.yml @@ -130,18 +130,6 @@ jobs: docker compose pull hiveiq-aria && \ REGISTRY_URL=${{ secrets.DEV_REGISTRY_URL }} ARIA_VERSION=dev \ docker compose up -d hiveiq-aria" - - - name: Notify Slack - if: always() - run: | - STATUS="${{ job.status }}" - [ "$STATUS" = "success" ] && COLOR="good" || COLOR="danger" - SHORT_SHA=$(echo "${{ github.sha }}" | cut -c1-7) - RUN_URL="https://hiveiq-gitea.directlx.dev/hiveiq-src/hiveops-aria/actions/runs/${{ github.run_id }}" - curl -s -X POST "${{ secrets.SLACK_WEBHOOK_URL }}" \ - -H "Content-Type: application/json" \ - -d "{\"attachments\":[{\"color\":\"$COLOR\",\"text\":\"*<$RUN_URL|hiveops-aria CD Develop — $STATUS>*\n${{ github.ref_name }} · $SHORT_SHA · ${{ github.actor }}\",\"mrkdwn_in\":[\"text\"],\"footer\":\"Gitea Actions\"}]}" - - name: Cleanup if: always() run: rm -f ~/.ssh/deploy_key diff --git a/.gitea/workflows/cd-main.yml b/.gitea/workflows/cd-main.yml index 128f119..17c57a8 100644 --- a/.gitea/workflows/cd-main.yml +++ b/.gitea/workflows/cd-main.yml @@ -104,14 +104,3 @@ jobs: -t ${{ secrets.REGISTRY_URL }}/hiveiq-aria:${SHORT_SHA} . docker push ${{ secrets.REGISTRY_URL }}/hiveiq-aria:latest docker push ${{ secrets.REGISTRY_URL }}/hiveiq-aria:${SHORT_SHA} - - - name: Notify Slack - if: always() && github.ref_name == 'main' - run: | - STATUS="${{ job.status }}" - [ "$STATUS" = "success" ] && COLOR="good" || COLOR="danger" - SHORT_SHA=$(echo "${{ github.sha }}" | cut -c1-7) - RUN_URL="https://hiveiq-gitea.directlx.dev/hiveiq-src/hiveops-aria/actions/runs/${{ github.run_id }}" - curl -s -X POST "${{ secrets.SLACK_PROD_WEBHOOK_URL }}" \ - -H "Content-Type: application/json" \ - -d "{\"attachments\":[{\"color\":\"$COLOR\",\"text\":\"*<$RUN_URL|hiveops-aria CD Production — $STATUS>*\\n${{ github.ref_name }} · $SHORT_SHA · ${{ github.actor }}\",\"mrkdwn_in\":[\"text\"],\"footer\":\"Gitea Actions\"}]}" -- 2.43.0 From 9a0d1a6ff2df44599bb7df4351a7b1f11361f60f Mon Sep 17 00:00:00 2001 From: Johannes Date: Tue, 23 Jun 2026 08:24:35 -0400 Subject: [PATCH 08/12] chore(build): use :dev tag for bcos.dev registry, :latest for prod Co-Authored-By: Claude Sonnet 4.6 --- build.sh | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/build.sh b/build.sh index 10cd8c2..4f9e62b 100755 --- a/build.sh +++ b/build.sh @@ -12,7 +12,14 @@ while [[ "$#" -gt 0 ]]; do shift done -IMAGE="$REGISTRY/hiveiq-aria:latest" +# Use :dev tag for the dev registry, :latest for everything else +if [[ "$REGISTRY" == *"bcos.dev"* ]]; then + TAG="dev" +else + TAG="latest" +fi + +IMAGE="$REGISTRY/hiveiq-aria:$TAG" echo "Building JAR..." mvn clean package -DskipTests -B -- 2.43.0 From d8619c4ecba58c531b6ca7187777f0f6938f5962 Mon Sep 17 00:00:00 2001 From: Johannes Date: Tue, 23 Jun 2026 15:20:26 -0400 Subject: [PATCH 09/12] =?UTF-8?q?fix:=20remove=20duplicate=20CORS=20header?= =?UTF-8?q?s=20=E2=80=94=20disable=20Spring=20CORS,=20delegate=20to=20NGIN?= =?UTF-8?q?X?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 4.6 --- .../hiveops/aria/config/SecurityConfig.java | 33 +------------------ 1 file changed, 1 insertion(+), 32 deletions(-) diff --git a/src/main/java/com/hiveops/aria/config/SecurityConfig.java b/src/main/java/com/hiveops/aria/config/SecurityConfig.java index 799e953..1229225 100644 --- a/src/main/java/com/hiveops/aria/config/SecurityConfig.java +++ b/src/main/java/com/hiveops/aria/config/SecurityConfig.java @@ -10,12 +10,6 @@ import org.springframework.security.config.annotation.web.configurers.AbstractHt import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; -import org.springframework.web.cors.CorsConfiguration; -import org.springframework.web.cors.CorsConfigurationSource; -import org.springframework.web.cors.UrlBasedCorsConfigurationSource; - -import java.util.Arrays; -import java.util.List; @Configuration @EnableWebSecurity @@ -31,7 +25,7 @@ public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http - .cors(cors -> cors.configurationSource(corsConfigurationSource())) + .cors(AbstractHttpConfigurer::disable) .csrf(AbstractHttpConfigurer::disable) .sessionManagement(s -> s.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authorizeHttpRequests(auth -> auth @@ -46,29 +40,4 @@ public class SecurityConfig { return http.build(); } - - @Bean - public CorsConfigurationSource corsConfigurationSource() { - CorsConfiguration config = new CorsConfiguration(); - - String originsEnv = System.getenv("CORS_ALLOWED_ORIGINS"); - if (originsEnv != null && !originsEnv.isBlank()) { - config.setAllowedOrigins(Arrays.asList(originsEnv.split(","))); - } else { - config.setAllowedOrigins(List.of( - "https://aria.bcos.cloud", - "https://portal.bcos.cloud", - "http://localhost:5181" - )); - } - - config.setAllowedMethods(List.of("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS")); - config.setAllowedHeaders(List.of("Authorization", "Content-Type", "X-Requested-With", "X-Service-Secret")); - config.setAllowCredentials(true); - config.setMaxAge(3600L); - - UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); - source.registerCorsConfiguration("/**", config); - return source; - } } -- 2.43.0 From dd10b8754aa761299140f4c335b2baaac94a2bce Mon Sep 17 00:00:00 2001 From: Johannes Date: Wed, 24 Jun 2026 10:03:16 -0400 Subject: [PATCH 10/12] feat(aria): replace countdown toggle with labeled auto-refresh switch Replace cryptic SVG countdown circle with a clear labeled toggle switch in ThreatEvents, DevicePosture, and PrecursorAlerts. State is persisted to localStorage per-component (aria_threats_autoRefresh, aria_posture_autoRefresh, aria_alerts_autoRefresh). Co-Authored-By: Claude Sonnet 4.6 --- frontend/src/components/DevicePosture.svelte | 81 ++++++++++--------- .../src/components/PrecursorAlerts.svelte | 81 ++++++++++--------- frontend/src/components/ThreatEvents.svelte | 81 ++++++++++--------- 3 files changed, 129 insertions(+), 114 deletions(-) diff --git a/frontend/src/components/DevicePosture.svelte b/frontend/src/components/DevicePosture.svelte index 1a79bc7..3873071 100644 --- a/frontend/src/components/DevicePosture.svelte +++ b/frontend/src/components/DevicePosture.svelte @@ -122,16 +122,17 @@ // Auto-refresh const REFRESH_SECONDS = 60; - const CIRCUMFERENCE = 2 * Math.PI * 14; let secondsLeft = REFRESH_SECONDS; let refreshing = false; - let autoRefreshEnabled = true; + const _AR_KEY = 'aria_posture_autoRefresh'; + let autoRefreshEnabled: boolean = typeof localStorage !== 'undefined' + ? (localStorage.getItem(_AR_KEY) ?? 'true') === 'true' + : true; let tickInterval: ReturnType; - $: dashOffset = CIRCUMFERENCE * (1 - secondsLeft / REFRESH_SECONDS); - function toggleAutoRefresh() { autoRefreshEnabled = !autoRefreshEnabled; + if (typeof localStorage !== 'undefined') localStorage.setItem(_AR_KEY, String(autoRefreshEnabled)); if (autoRefreshEnabled) secondsLeft = REFRESH_SECONDS; } @@ -166,32 +167,13 @@

Per-device security compliance — disk encryption, image integrity, OS currency

+ -
@@ -570,6 +552,39 @@ .toolbar { display: flex; align-items: center; justify-content: space-between; flex-shrink: 0; } .toolbar-count { font-size: var(--font-size-body-sm); color: #6b7280; } .header-controls { display: flex; align-items: center; gap: 0.5rem; flex-shrink: 0; } + .auto-refresh-toggle { + display: flex; + align-items: center; + gap: 0.5rem; + cursor: pointer; + user-select: none; + flex-shrink: 0; + } + .auto-refresh-toggle input { display: none; } + .toggle-track { + width: 36px; height: 20px; + background: rgba(255,255,255,0.25); + border-radius: 10px; + position: relative; + transition: background 0.2s; + flex-shrink: 0; + } + .auto-refresh-toggle input:checked + .toggle-track { background: #34d399; } + .toggle-thumb { + position: absolute; + top: 3px; left: 3px; + width: 14px; height: 14px; + background: white; + border-radius: 50%; + transition: transform 0.2s; + } + .auto-refresh-toggle input:checked + .toggle-track .toggle-thumb { transform: translateX(16px); } + .toggle-label { + color: rgba(255,255,255,0.9); + font-size: 0.8rem; + white-space: nowrap; + min-width: 110px; + } .btn-manual-refresh { background: rgba(255,255,255,0.15); border: 1px solid rgba(255,255,255,0.3); color: white; border-radius: 50%; width: 36px; height: 36px; @@ -578,16 +593,6 @@ } .btn-manual-refresh:hover:not(:disabled) { background: rgba(255,255,255,0.28); } .btn-manual-refresh:disabled { opacity: 0.5; cursor: not-allowed; } - .refresh-countdown { - flex-shrink: 0; background: none; border: none; cursor: pointer; padding: 0; - border-radius: 50%; display: flex; align-items: center; justify-content: center; - opacity: 0.9; transition: opacity 0.15s, transform 0.15s; - } - .refresh-countdown:hover { opacity: 1; transform: scale(1.08); } - .refresh-countdown.paused { opacity: 0.5; } - .refresh-countdown.paused:hover { opacity: 0.8; } - @keyframes countdown-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.4; } } - :global(.pulsing) { animation: countdown-pulse 0.8s ease-in-out infinite; } /* Table card — canonical */ .table-card { diff --git a/frontend/src/components/PrecursorAlerts.svelte b/frontend/src/components/PrecursorAlerts.svelte index f5d99a3..7643799 100644 --- a/frontend/src/components/PrecursorAlerts.svelte +++ b/frontend/src/components/PrecursorAlerts.svelte @@ -110,16 +110,17 @@ // Auto-refresh const REFRESH_SECONDS = 60; - const CIRCUMFERENCE = 2 * Math.PI * 14; let secondsLeft = REFRESH_SECONDS; let refreshing = false; - let autoRefreshEnabled = true; + const _AR_KEY = 'aria_alerts_autoRefresh'; + let autoRefreshEnabled: boolean = typeof localStorage !== 'undefined' + ? (localStorage.getItem(_AR_KEY) ?? 'true') === 'true' + : true; let tickInterval: ReturnType; - $: dashOffset = CIRCUMFERENCE * (1 - secondsLeft / REFRESH_SECONDS); - function toggleAutoRefresh() { autoRefreshEnabled = !autoRefreshEnabled; + if (typeof localStorage !== 'undefined') localStorage.setItem(_AR_KEY, String(autoRefreshEnabled)); if (autoRefreshEnabled) secondsLeft = REFRESH_SECONDS; } @@ -162,32 +163,13 @@

Multi-phase attack sequence detection — jackpotting and skimming precursors

+ -
@@ -551,6 +533,39 @@ .toolbar-count { font-size: var(--font-size-body-sm); color: #6b7280; } .header-controls { display: flex; align-items: center; gap: 0.5rem; flex-shrink: 0; } + .auto-refresh-toggle { + display: flex; + align-items: center; + gap: 0.5rem; + cursor: pointer; + user-select: none; + flex-shrink: 0; + } + .auto-refresh-toggle input { display: none; } + .toggle-track { + width: 36px; height: 20px; + background: rgba(255,255,255,0.25); + border-radius: 10px; + position: relative; + transition: background 0.2s; + flex-shrink: 0; + } + .auto-refresh-toggle input:checked + .toggle-track { background: #34d399; } + .toggle-thumb { + position: absolute; + top: 3px; left: 3px; + width: 14px; height: 14px; + background: white; + border-radius: 50%; + transition: transform 0.2s; + } + .auto-refresh-toggle input:checked + .toggle-track .toggle-thumb { transform: translateX(16px); } + .toggle-label { + color: rgba(255,255,255,0.9); + font-size: 0.8rem; + white-space: nowrap; + min-width: 110px; + } .btn-manual-refresh { background: rgba(255,255,255,0.15); border: 1px solid rgba(255,255,255,0.3); color: white; border-radius: 50%; width: 36px; height: 36px; @@ -559,16 +574,6 @@ } .btn-manual-refresh:hover:not(:disabled) { background: rgba(255,255,255,0.28); } .btn-manual-refresh:disabled { opacity: 0.5; cursor: not-allowed; } - .refresh-countdown { - flex-shrink: 0; background: none; border: none; cursor: pointer; padding: 0; - border-radius: 50%; display: flex; align-items: center; justify-content: center; - opacity: 0.9; transition: opacity 0.15s, transform 0.15s; - } - .refresh-countdown:hover { opacity: 1; transform: scale(1.08); } - .refresh-countdown.paused { opacity: 0.5; } - .refresh-countdown.paused:hover { opacity: 0.8; } - @keyframes countdown-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.4; } } - :global(.pulsing) { animation: countdown-pulse 0.8s ease-in-out infinite; } /* Table card — canonical */ .table-card { diff --git a/frontend/src/components/ThreatEvents.svelte b/frontend/src/components/ThreatEvents.svelte index 7bc0cfb..d558598 100644 --- a/frontend/src/components/ThreatEvents.svelte +++ b/frontend/src/components/ThreatEvents.svelte @@ -85,16 +85,17 @@ // Auto-refresh const REFRESH_SECONDS = 60; - const CIRCUMFERENCE = 2 * Math.PI * 14; let secondsLeft = REFRESH_SECONDS; let refreshing = false; - let autoRefreshEnabled = true; + const _AR_KEY = 'aria_threats_autoRefresh'; + let autoRefreshEnabled: boolean = typeof localStorage !== 'undefined' + ? (localStorage.getItem(_AR_KEY) ?? 'true') === 'true' + : true; let tickInterval: ReturnType; - $: dashOffset = CIRCUMFERENCE * (1 - secondsLeft / REFRESH_SECONDS); - function toggleAutoRefresh() { autoRefreshEnabled = !autoRefreshEnabled; + if (typeof localStorage !== 'undefined') localStorage.setItem(_AR_KEY, String(autoRefreshEnabled)); if (autoRefreshEnabled) secondsLeft = REFRESH_SECONDS; } @@ -128,32 +129,13 @@

IOC match events detected across the fleet

+ -
@@ -290,6 +272,39 @@ } .header-controls { display: flex; align-items: center; gap: 0.5rem; flex-shrink: 0; } + .auto-refresh-toggle { + display: flex; + align-items: center; + gap: 0.5rem; + cursor: pointer; + user-select: none; + flex-shrink: 0; + } + .auto-refresh-toggle input { display: none; } + .toggle-track { + width: 36px; height: 20px; + background: rgba(255,255,255,0.25); + border-radius: 10px; + position: relative; + transition: background 0.2s; + flex-shrink: 0; + } + .auto-refresh-toggle input:checked + .toggle-track { background: #34d399; } + .toggle-thumb { + position: absolute; + top: 3px; left: 3px; + width: 14px; height: 14px; + background: white; + border-radius: 50%; + transition: transform 0.2s; + } + .auto-refresh-toggle input:checked + .toggle-track .toggle-thumb { transform: translateX(16px); } + .toggle-label { + color: rgba(255,255,255,0.9); + font-size: 0.8rem; + white-space: nowrap; + min-width: 110px; + } .btn-manual-refresh { background: rgba(255,255,255,0.15); border: 1px solid rgba(255,255,255,0.3); color: white; border-radius: 50%; width: 36px; height: 36px; @@ -298,16 +313,6 @@ } .btn-manual-refresh:hover:not(:disabled) { background: rgba(255,255,255,0.28); } .btn-manual-refresh:disabled { opacity: 0.5; cursor: not-allowed; } - .refresh-countdown { - flex-shrink: 0; background: none; border: none; cursor: pointer; padding: 0; - border-radius: 50%; display: flex; align-items: center; justify-content: center; - opacity: 0.9; transition: opacity 0.15s, transform 0.15s; - } - .refresh-countdown:hover { opacity: 1; transform: scale(1.08); } - .refresh-countdown.paused { opacity: 0.5; } - .refresh-countdown.paused:hover { opacity: 0.8; } - @keyframes countdown-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.4; } } - :global(.pulsing) { animation: countdown-pulse 0.8s ease-in-out infinite; } .page-body { display: flex; flex: 1; overflow: hidden; min-height: 0; margin: 0 0 16px; -- 2.43.0 From 2c957fb07212674ab3d8d6b8c68972af8d07e4c7 Mon Sep 17 00:00:00 2001 From: Johannes Date: Wed, 24 Jun 2026 20:23:12 -0400 Subject: [PATCH 11/12] fix(aria): correct auth users/me endpoint path Path was /api/auth/users/me but the auth controller is at /auth/api/users/me. Caused a CORS-then-404 error on every page load. Co-Authored-By: Claude Sonnet 4.6 --- frontend/src/App.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/App.svelte b/frontend/src/App.svelte index edc650d..1c7f820 100644 --- a/frontend/src/App.svelte +++ b/frontend/src/App.svelte @@ -38,7 +38,7 @@ onMount(() => { document.documentElement.classList.add('dark-mode'); - authApi.get('/api/auth/users/me').then((res: any) => { + authApi.get('/auth/api/users/me').then((res: any) => { userInfo = { name: res.data.name, email: res.data.email, role: res.data.role }; }).catch(() => {}); }); -- 2.43.0 From b0121c9f4436c1a13e783bad8a474da17b18c57c Mon Sep 17 00:00:00 2001 From: Johannes Date: Thu, 25 Jun 2026 14:40:10 -0400 Subject: [PATCH 12/12] =?UTF-8?q?refactor(aria):=20standardize=20API=20URL?= =?UTF-8?q?=20=E2=80=94=20VITE=5FAPI=5FURL=3Dapi.bcos.dev/aria?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Single VITE_API_URL replaces apiUrl+authUrl. Gateway URL for auth calls is derived at runtime via apiUrl.replace(/\/aria$/, ''). Co-Authored-By: Claude Sonnet 4.6 --- frontend/entrypoint.sh | 11 ++--------- frontend/src/lib/api.ts | 9 ++++----- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/frontend/entrypoint.sh b/frontend/entrypoint.sh index e76c3bb..b368b7c 100644 --- a/frontend/entrypoint.sh +++ b/frontend/entrypoint.sh @@ -1,19 +1,12 @@ #!/bin/sh set -e -# Inject runtime config from environment variables into the built SPA. -# This file is written to /app/dist/config.js and loaded by index.html -# BEFORE the Vite bundle — so window.__APP_CONFIG__ is available at startup. -# -# Add new config values here and in src/lib/api.ts Window interface. - cat > /app/dist/config.js <