Web/elixir phoenix poc #2578

Merged
mfreeman451 merged 87 commits from refs/pull/2578/head into staging 2025-12-16 05:11:48 +00:00
mfreeman451 commented 2025-12-15 17:15:37 +00:00 (Migrated from github.com)
Owner

Imported from GitHub pull request.

Original GitHub pull request: #2139
Original author: @mfreeman451
Original URL: https://github.com/carverauto/serviceradar/pull/2139
Original created: 2025-12-15T17:15:37Z
Original updated: 2025-12-16T05:11:52Z
Original head: carverauto/serviceradar:web/elixir_phoenix_poc
Original base: staging
Original merged: 2025-12-16T05:11:48Z by @mfreeman451

User description

IMPORTANT: Please sign the Developer Certificate of Origin

Thank you for your contribution to ServiceRadar. Please note, when contributing, the developer must include
a DCO sign-off statement indicating the DCO acceptance in one commit message. Here
is an example DCO Signed-off-by line in a commit message:

Signed-off-by: J. Doe <j.doe@domain.com>

Describe your changes

Code checklist before requesting a review

  • I have signed the DCO?
  • The build completes without errors?
  • All tests are passing when running make test?

PR Type

Enhancement, Tests


Description

  • SRQL Query Engine Refactoring: Comprehensive refactor of the query engine to support typed SQL parameters (BindParam enum) and visualization metadata generation, replacing the previous debug SQL pattern

  • New Query Capabilities: Added support for time-series downsampling with configurable bucket sizes and aggregation functions (avg, min, max, sum, count), and read-only Cypher graph database queries with security validation

  • Visualization Metadata: Implemented VizMeta system providing column type information and visualization hints (timeseries, table) for query results across all entity types

  • Parameter Binding Across All Entities: Refactored all entity-specific query modules (logs, OTEL metrics, interfaces, traces, CPU/memory/disk/process metrics, devices, services, events, etc.) to use typed parameter binding with validation

  • PostgreSQL TLS Authentication: Added client certificate and key support for mutual TLS authentication with PostgreSQL

  • Elixir Phoenix Integration: Created new Elixir NIF (Native Implemented Function) for SRQL query translation, behaviour module definition, and comprehensive Phoenix v1.8 web framework with LiveView components

  • Web UI Components: Implemented SRQL query interface with interactive builder, results table, auto-visualization, and query state management

  • User Authentication & Email: Added user authentication system with magic link login, email notifications via Swoosh, and user account management

  • Configuration & Infrastructure: Added embedded SRQL mode, PostgreSQL SSL certificate configuration, dynamic certificate SAN support, and Docker Compose updates for external database access

  • UI Framework: Integrated Tailwind CSS v4 with daisyUI themes, Heroicons, and topbar progress indicators

  • Documentation: Comprehensive Phoenix development guidelines covering authentication, JavaScript, CSS, testing, and best practices


Diagram Walkthrough

flowchart LR
  A["SRQL Query Engine"] -->|"Typed Parameters"| B["BindParam Enum"]
  A -->|"Visualization"| C["VizMeta System"]
  A -->|"Downsampling"| D["Time Bucketing"]
  A -->|"Graph Queries"| E["Cypher Support"]
  F["Elixir NIF"] -->|"Translates"| A
  G["Phoenix Web"] -->|"Uses"| F
  G -->|"Components"| H["Query Builder UI"]
  G -->|"Auth"| I["User System"]
  J["PostgreSQL"] -->|"TLS"| K["Client Certs"]
  A -->|"Connects"| J

File Walkthrough

Relevant files
Enhancement
34 files
mod.rs
Refactor query engine to support typed parameters and visualization
metadata

rust/srql/src/query/mod.rs

  • Added BindParam enum for typed SQL parameter binding with support for
    text, arrays, integers, booleans, floats, and timestamps
  • Introduced downsample module and graph_cypher module for new query
    capabilities
  • Refactored query execution to check for downsampling before routing to
    entity-specific handlers
  • Replaced to_debug_sql pattern with to_sql_and_params to return both
    SQL and typed parameters
  • Added translate_request public function that returns SQL, parameters,
    pagination metadata, and visualization hints
  • Implemented helper functions for SQL placeholder detection, bind
    parameter reconciliation, and Diesel query introspection
  • Added comprehensive tests for parameter arity matching, visualization
    metadata, downsampling, and graph cypher queries
+506/-54
viz.rs
Add visualization metadata generation for query results   

rust/srql/src/query/viz.rs

  • Created new module providing visualization metadata for query results
  • Defined VizMeta struct containing column metadata and visualization
    suggestions
  • Implemented ColumnType and ColumnSemantic enums to describe result
    columns
  • Added VizKind enum supporting timeseries and table visualizations
  • Implemented meta_for_plan function that generates appropriate
    visualization hints for each entity type
  • Provided column definitions with semantic information and units for
    all supported entities
+605/-0 
downsample.rs
Add time-series downsampling with bucketing and aggregation

rust/srql/src/query/downsample.rs

  • Implemented downsampling module for time-bucketed metric aggregation
  • Added support for multiple aggregation functions (avg, min, max, sum,
    count) with configurable bucket sizes
  • Implemented series grouping with validation for supported fields per
    metric type
  • Added comprehensive filter support for all metric entities with
    type-safe parameter binding
  • Implemented SQL generation with PostgreSQL time_bucket function and
    proper placeholder rewriting
+521/-0 
graph_cypher.rs
Add read-only Cypher graph query execution support             

rust/srql/src/query/graph_cypher.rs

  • Created new module for executing read-only Cypher queries against
    graph database
  • Implemented security validation to reject write operations (CREATE,
    MERGE, SET, DELETE, etc.)
  • Added SQL generation that wraps Cypher results in topology payload
    format with nodes and edges
  • Implemented dollar-quoting for safe Cypher query embedding in SQL
  • Added placeholder rewriting with proper handling of SQL string
    literals and dollar-delimited strings
+208/-0 
parser.rs
Add downsampling and graph cypher query parsing support   

rust/srql/src/parser.rs

  • Added GraphCypher entity variant for graph database queries
  • Introduced DownsampleSpec struct with bucket duration, aggregation
    function, and series field
  • Added DownsampleAgg enum supporting avg, min, max, sum, and count
    operations
  • Implemented parsing for bucket, agg, and series query parameters with
    validation
  • Added parse_bucket_seconds function supporting duration suffixes (s,
    m, h, d) with maximum bounds
  • Added helper functions for parsing aggregation types and normalizing
    optional strings
+103/-0 
logs.rs
Refactor logs query to return typed SQL parameters             

rust/srql/src/query/logs.rs

  • Added to_sql_and_params function returning typed BindParam vectors
    instead of debug SQL
  • Implemented collect_filter_params to extract parameters from filter
    conditions
  • Added support for text, text array, and integer array parameter types
  • Implemented bind_param_from_stats conversion for statistics query
    results
+107/-1 
otel_metrics.rs
Refactor OTEL metrics query to return typed SQL parameters

rust/srql/src/query/otel_metrics.rs

  • Added to_sql_and_params function with typed parameter collection
  • Implemented collect_filter_params for extracting parameters from
    filter conditions
  • Added support for text, text array, and boolean parameter types
  • Implemented bind parameter reconciliation and validation with diesel
    bind count checking
+91/-1   
interfaces.rs
Refactor interfaces query to return typed SQL parameters 

rust/srql/src/query/interfaces.rs

  • Added to_sql_and_params function with comprehensive parameter
    collection
  • Implemented collect_filter_params supporting text, integer, and text
    array parameters
  • Added validation for IP address filter list size limits
  • Implemented bind parameter reconciliation with diesel bind count
    verification
+118/-1 
traces.rs
Refactor traces query to return typed SQL parameters         

rust/srql/src/query/traces.rs

  • Added to_sql_and_params function with typed parameter collection
  • Implemented collect_filter_params supporting text and integer list
    parameters
  • Added helper function collect_i32_list for parsing integer arrays from
    filters
  • Implemented bind parameter reconciliation and diesel bind count
    validation
+112/-1 
cpu_metrics.rs
Refactor CPU metrics query to return typed SQL parameters

rust/srql/src/query/cpu_metrics.rs

  • Added to_sql_and_params function with typed parameter collection
  • Implemented collect_filter_params supporting text, integer, and float
    parameters
  • Added bind_param_from_stats conversion for statistics query results
  • Implemented bind parameter reconciliation and diesel bind count
    validation
+93/-1   
pollers.rs
Refactor pollers query to return typed SQL parameters       

rust/srql/src/query/pollers.rs

  • Added to_sql_and_params function with typed parameter collection
  • Implemented collect_filter_params supporting text and boolean
    parameters
  • Moved parse_bool function to module level for reusability
  • Implemented bind parameter reconciliation and diesel bind count
    validation
+84/-11 
db.rs
Add client certificate TLS authentication support               

rust/srql/src/db.rs

  • Added support for client certificate and key TLS authentication
  • Implemented load_client_certs function to parse certificate chains
    from PEM files
  • Implemented load_client_key function to parse private keys from PEM
    files
  • Added build_client_config function to construct Rustls client
    configuration with optional mutual TLS
  • Updated PgConnectionManager::new to accept client certificate and key
    paths
+77/-10 
process_metrics.rs
Refactor process metrics query to return typed SQL parameters

rust/srql/src/query/process_metrics.rs

  • Added to_sql_and_params function with typed parameter collection
  • Implemented collect_filter_params supporting text, integer, and float
    parameters
  • Implemented bind parameter reconciliation and diesel bind count
    validation
+80/-1   
timeseries_metrics.rs
Refactor timeseries metrics query to return typed SQL parameters

rust/srql/src/query/timeseries_metrics.rs

  • Added to_sql_and_params function with typed parameter collection
  • Implemented collect_filter_params supporting text, integer, and float
    parameters
  • Added metric scope handling for forced metric type parameters
  • Implemented bind parameter reconciliation and diesel bind count
    validation
+85/-1   
behaviour.ex
Add Elixir SRQL behaviour module definition                           

web-ng/lib/serviceradar_web_ng/srql/behaviour.ex

  • Created new Elixir behaviour module defining SRQL callback interface
  • Defined srql_response type as a map
  • Specified query_request callback accepting a map and returning success
    or error tuple
+7/-0     
devices.rs
Add parameterized SQL generation for device queries           

rust/srql/src/query/devices.rs

  • Added BindParam import to support parameterized query generation
  • Implemented to_sql_and_params() function to generate SQL with bind
    parameters
  • Added collect_text_params() and collect_filter_params() helper
    functions for parameter collection
  • Includes validation to ensure bind count matches between diesel and
    collected parameters
+90/-1   
disk_metrics.rs
Add parameterized SQL generation for disk metrics               

rust/srql/src/query/disk_metrics.rs

  • Added BindParam import for parameterized queries
  • Implemented to_sql_and_params() function with time range and filter
    parameter handling
  • Added collect_text_params() and collect_filter_params() for parameter
    extraction
  • Updated test configuration to include downsample field
+75/-1   
memory_metrics.rs
Add parameterized SQL generation for memory metrics           

rust/srql/src/query/memory_metrics.rs

  • Added BindParam import for parameterized query support
  • Implemented to_sql_and_params() function with parameter collection
    logic
  • Added collect_text_params() and collect_filter_params() helper
    functions
  • Updated test configuration to include downsample field
+76/-1   
device_updates.rs
Add parameterized SQL generation for device updates           

rust/srql/src/query/device_updates.rs

  • Added BindParam import for parameterized query generation
  • Implemented to_sql_and_params() function with filter and time range
    handling
  • Added collect_text_params() and collect_filter_params() for parameter
    collection
  • Supports both scalar and list filter values with validation
+81/-1   
services.rs
Add parameterized SQL generation for services queries       

rust/srql/src/query/services.rs

  • Added BindParam import for parameterized query support
  • Implemented to_sql_and_params() function with parameter collection
  • Added collect_text_params() and collect_filter_params() helper
    functions
  • Updated test configuration to include downsample field
+68/-1   
events.rs
Add parameterized SQL generation for events queries           

rust/srql/src/query/events.rs

  • Added BindParam import for parameterized queries
  • Implemented to_sql_and_params() function with parameter extraction
    logic
  • Added collect_text_params() and collect_filter_params() for parameter
    handling
  • Supports text and integer parameter types for various event fields
+74/-1   
trace_summaries.rs
Add parameterized SQL generation for trace summaries         

rust/srql/src/query/trace_summaries.rs

  • Added BindParam import for parameterized query support
  • Implemented to_sql_and_params() function that converts query binds to
    BindParam
  • Added bind_param_from_query() helper function to convert SqlBindValue
    to BindParam
  • Handles multiple bind value types including text, arrays, integers,
    and timestamps
+24/-1   
config.rs
Add PostgreSQL SSL certificate configuration and embedded mode

rust/srql/src/config.rs

  • Added pg_ssl_cert and pg_ssl_key configuration fields for PostgreSQL
    client certificates
  • Updated configuration loading to read PGSSLCERT and PGSSLKEY
    environment variables
  • Added embedded() constructor method for creating in-memory
    configurations
  • Embedded configuration supports all standard settings with sensible
    defaults
+23/-0   
lib.rs
Add Elixir NIF for SRQL query translation                               

web-ng/native/srql_nif/src/lib.rs

  • New Rust NIF (Native Implemented Function) module for Elixir
    integration
  • Implements translate() function to convert SRQL queries to SQL with
    parameters
  • Supports query direction (next/prev) and optional cursor/limit
    parameters
  • Returns JSON-encoded response or error message as Elixir tuple
+49/-0   
device_graph.rs
Add parameterized SQL generation for device graph               

rust/srql/src/query/device_graph.rs

  • Added BindParam import for parameterized query support
  • Implemented to_sql_and_params() function that extracts parameters from
    query plan
  • Returns SQL string with three bind parameters: device_id,
    collector_owned_only, include_topology
+14/-1   
lib.rs
Export public API and add embedded mode support                   

rust/srql/src/lib.rs

  • Exported public API types: QueryDirection, QueryEngine, QueryRequest,
    QueryResponse, TranslateRequest, TranslateResponse
  • Added EmbeddedSrql struct for embedded mode usage
  • Implemented EmbeddedSrql::new() constructor that initializes database
    pool and query engine
  • Enables use of SRQL as a library within other Rust applications
+19/-0   
app.js
Add Phoenix LiveView application initialization                   

web-ng/assets/js/app.js

  • New Phoenix LiveView application entry point
  • Configures LiveSocket with CSRF token and colocated hooks
  • Integrates topbar for visual page loading feedback
  • Includes development mode features for live reload and editor
    integration
+83/-0   
heroicons.js
Add Heroicons Tailwind CSS plugin                                               

web-ng/assets/vendor/heroicons.js

  • New Tailwind CSS plugin for Heroicons SVG icon integration
  • Supports multiple icon sizes: outline (24px), solid (24px), mini
    (20px), micro (16px)
  • Dynamically loads SVG files and converts them to CSS mask utilities
  • Enables icon usage via Tailwind classes like hero-*
+43/-0   
page_controller.ex
Add page controller with authentication redirect                 

web-ng/lib/serviceradar_web_ng_web/controllers/page_controller.ex

  • New Elixir controller module for page routing
  • Implements home() action that redirects authenticated users to
    dashboard
  • Renders home page for unauthenticated users
  • Uses scope and user information from connection assigns
+11/-0   
home.html.heex
Phoenix framework landing page with community links           

web-ng/lib/serviceradar_web_ng_web/controllers/page_html/home.html.heex

  • Creates a landing page template with Phoenix branding and decorative
    SVG background
  • Displays Phoenix framework version badge and theme toggle component
  • Provides navigation links to documentation, GitHub repository, and
    changelog
  • Includes community engagement links for Elixir Forum, Discord, Slack,
    and Fly.io deployment
+202/-0 
srql_components.ex
SRQL query interface components with builder and visualization

web-ng/lib/serviceradar_web_ng_web/components/srql_components.ex

  • Implements SRQL query bar component with text input and builder toggle
    functionality
  • Creates results table component with dynamic column detection and cell
    value formatting
  • Provides auto-visualization component supporting timeseries and
    categories chart types
  • Builds comprehensive query builder UI with entity selection, time
    filters, downsampling, and filter management
+551/-0 
builder.ex
SRQL query builder with parsing and state management         

web-ng/lib/serviceradar_web_ng_web/srql/builder.ex

  • Implements SRQL query builder state management with parsing and
    validation logic
  • Provides query construction from builder state and parsing of SRQL
    query strings
  • Handles normalization of query parameters including entity, time,
    bucket, aggregation, and filters
  • Validates downsample operations, filter fields, and sort
    configurations against entity catalog
+596/-0 
page.ex
SRQL page state and event handling logic                                 

web-ng/lib/serviceradar_web_ng_web/srql/page.ex

  • Manages SRQL page state initialization and query list loading with
    result handling
  • Implements event handlers for query submission, builder toggle, and
    filter management
  • Provides visualization metadata extraction and error formatting for
    SRQL responses
  • Handles builder state synchronization with query draft and parameter
    normalization
+470/-0 
user_notifier.ex
User email notification system with Swoosh integration     

web-ng/lib/serviceradar_web_ng/accounts/user_notifier.ex

  • Implements email delivery functions using Swoosh for user
    notifications
  • Provides email templates for magic link login, account confirmation,
    and email update instructions
  • Handles conditional logic to send confirmation instructions for
    unconfirmed users
  • Integrates with application mailer for reliable email delivery
+84/-0   
Tests
1 files
harness.rs
Update test harness for SSL certificate configuration       

rust/srql/tests/support/harness.rs

  • Updated test configuration to include new pg_ssl_cert and pg_ssl_key
    fields
  • Both fields set to None for test environment
+2/-0     
Configuration changes
3 files
generate-certs.sh
Add dynamic certificate SAN support and workstation cert 

docker/compose/generate-certs.sh

  • Added logic to check for missing SANs in existing CNPG certificates
    when CNPG_CERT_EXTRA_IPS is set
  • Regenerates CNPG certificate if required SANs are missing
  • Supports dynamic SAN configuration via environment variable
  • Added generation of workstation certificate for external developer
    connections
+28/-6   
docker-compose.yml
Add CNPG public port and certificate configuration             

docker-compose.yml

  • Added CNPG_CERT_EXTRA_IPS environment variable to cert-generator
    service
  • Added public port binding for CNPG database service (default
    127.0.0.1:5455)
  • Allows external connections to PostgreSQL when configured
  • Supports dynamic certificate generation for additional IP addresses
+6/-0     
app.css
Tailwind CSS v4 configuration with daisyUI themes               

web-ng/assets/css/app.css

  • Configures Tailwind CSS v4 with new import syntax and source
    directives
  • Integrates daisyUI plugin with custom light and dark theme
    configurations using OKLCH color values
  • Adds custom Tailwind variants for Phoenix LiveView classes
    (phx-click-loading, phx-submit-loading, phx-change-loading)
  • Implements transparent display for LiveView wrapper divs to support
    proper layout composition
+105/-0 
Dependencies
2 files
daisyui-theme.js
Add daisyUI theme vendor library                                                 

web-ng/assets/vendor/daisyui-theme.js

  • New vendor file containing daisyUI theme plugin and color definitions
  • Includes 40+ pre-built color themes (cyberpunk, acid, dark, light,
    luxury, etc.)
  • Provides Tailwind CSS plugin for theme customization and application
  • MIT licensed library for UI theming
+124/-0 
topbar.js
Add topbar progress bar vendor library                                     

web-ng/assets/vendor/topbar.js

  • New vendor file containing topbar progress bar library
  • Implements progress bar visualization with configurable colors and
    styling
  • Supports show/hide/progress control methods for page loading
    indicators
  • MIT licensed library for visual feedback during navigation
+138/-0 
Documentation
1 files
AGENTS.md
Complete Phoenix v1.8 development guidelines and best practices

web-ng/AGENTS.md

  • Comprehensive project guidelines for Phoenix v1.8 web framework
    development
  • Detailed authentication flow documentation with router scope and
    live_session patterns
  • JavaScript, CSS, and UI/UX design guidelines emphasizing Tailwind CSS
    and custom components
  • Extensive Elixir, Mix, testing, Phoenix, Ecto, HTML, and LiveView best
    practices and patterns
+510/-0 
Additional files
101 files
.env.example +18/-1   
.tool-versions +2/-0     
AGENTS.md +46/-0   
design.md +151/-0 
proposal.md +48/-0   
tasks.md +109/-0 
.formatter.exs +6/-0     
README.md +56/-0   
tsconfig.json +32/-0   
daisyui.js +1031/-0
config.exs +83/-0   
dev.exs +151/-0 
prod.exs +31/-0   
runtime.exs +190/-0 
test.exs +105/-0 
ready.ex +33/-0   
serviceradar_web_ng.ex +9/-0     
accounts.ex +297/-0 
scope.ex +33/-0   
user.ex +136/-0 
user_token.ex +156/-0 
device_controller.ex +282/-0 
query_controller.ex +19/-0   
application.ex +35/-0   
edge.ex +11/-0   
onboarding_package.ex +41/-0   
onboarding_token.ex +82/-0   
graph.ex +104/-0 
infrastructure.ex +19/-0   
poller.ex +21/-0   
service.ex +15/-0   
inventory.ex +19/-0   
device.ex +25/-0   
mailer.ex +3/-0     
repo.ex +5/-0     
srql.ex +219/-0 
native.ex +8/-0     
serviceradar_web_ng_web.ex +116/-0 
core_components.ex +498/-0 
layouts.ex +318/-0 
root.html.heex +36/-0   
ui_components.ex +302/-0 
error_html.ex +24/-0   
error_json.ex +21/-0   
page_html.ex +10/-0   
user_session_controller.ex +67/-0   
engine.ex +70/-0   
plugin.ex +10/-0   
categories.ex +53/-0   
graph_result.ex +106/-0 
table.ex +63/-0   
timeseries.ex +226/-0 
topology.ex +372/-0 
registry.ex +15/-0   
endpoint.ex +55/-0   
gettext.ex +25/-0   
index.ex +113/-0 
index.ex +80/-0   
index.ex +79/-0   
index.ex +80/-0   
index.ex +79/-0   
index.ex +80/-0   
index.ex +80/-0   
confirmation.ex +94/-0   
login.ex +132/-0 
registration.ex +88/-0   
settings.ex +157/-0 
safe_parsers.ex +34/-0   
router.ex +94/-0   
catalog.ex +308/-0 
viz.ex +173/-0 
telemetry.ex +93/-0   
user_auth.ex +287/-0 
mix.exs +97/-0   
Cargo.toml +16/-0   
errors.po +112/-0 
errors.pot +109/-0 
.formatter.exs +4/-0     
20251215020449_create_ng_users_auth_tables.exs +28/-0   
seeds.exs +11/-0   
robots.txt +5/-0     
graph_cypher_integration_test.exs +153/-0 
srql_nif_integration_test.exs +193/-0 
api_device_controller_property_test.exs +43/-0   
api_query_controller_property_test.exs +52/-0   
edge_onboarding_token_property_test.exs +46/-0   
srql_property_test.exs +27/-0   
srql_query_input_property_test.exs +90/-0   
accounts_test.exs +421/-0 
infrastructure_test.exs +28/-0   
inventory_test.exs +30/-0   
error_html_test.exs +15/-0   
error_json_test.exs +14/-0   
page_controller_test.exs +8/-0     
user_session_controller_test.exs +147/-0 
dashboard_engine_test.exs +41/-0   
dashboard_topology_plugin_test.exs +31/-0   
device_live_test.exs +27/-0   
poller_live_test.exs +25/-0   
confirmation_test.exs +118/-0 
Additional files not shown

Imported from GitHub pull request. Original GitHub pull request: #2139 Original author: @mfreeman451 Original URL: https://github.com/carverauto/serviceradar/pull/2139 Original created: 2025-12-15T17:15:37Z Original updated: 2025-12-16T05:11:52Z Original head: carverauto/serviceradar:web/elixir_phoenix_poc Original base: staging Original merged: 2025-12-16T05:11:48Z by @mfreeman451 --- ### **User description** ## IMPORTANT: Please sign the Developer Certificate of Origin Thank you for your contribution to ServiceRadar. Please note, when contributing, the developer must include a [DCO sign-off statement]( https://developercertificate.org/) indicating the DCO acceptance in one commit message. Here is an example DCO Signed-off-by line in a commit message: ``` Signed-off-by: J. Doe <j.doe@domain.com> ``` ## Describe your changes ## Issue ticket number and link ## Code checklist before requesting a review - [ ] I have signed the DCO? - [ ] The build completes without errors? - [ ] All tests are passing when running make test? ___ ### **PR Type** Enhancement, Tests ___ ### **Description** - **SRQL Query Engine Refactoring**: Comprehensive refactor of the query engine to support typed SQL parameters (`BindParam` enum) and visualization metadata generation, replacing the previous debug SQL pattern - **New Query Capabilities**: Added support for time-series downsampling with configurable bucket sizes and aggregation functions (avg, min, max, sum, count), and read-only Cypher graph database queries with security validation - **Visualization Metadata**: Implemented `VizMeta` system providing column type information and visualization hints (timeseries, table) for query results across all entity types - **Parameter Binding Across All Entities**: Refactored all entity-specific query modules (logs, OTEL metrics, interfaces, traces, CPU/memory/disk/process metrics, devices, services, events, etc.) to use typed parameter binding with validation - **PostgreSQL TLS Authentication**: Added client certificate and key support for mutual TLS authentication with PostgreSQL - **Elixir Phoenix Integration**: Created new Elixir NIF (Native Implemented Function) for SRQL query translation, behaviour module definition, and comprehensive Phoenix v1.8 web framework with LiveView components - **Web UI Components**: Implemented SRQL query interface with interactive builder, results table, auto-visualization, and query state management - **User Authentication & Email**: Added user authentication system with magic link login, email notifications via Swoosh, and user account management - **Configuration & Infrastructure**: Added embedded SRQL mode, PostgreSQL SSL certificate configuration, dynamic certificate SAN support, and Docker Compose updates for external database access - **UI Framework**: Integrated Tailwind CSS v4 with daisyUI themes, Heroicons, and topbar progress indicators - **Documentation**: Comprehensive Phoenix development guidelines covering authentication, JavaScript, CSS, testing, and best practices ___ ### Diagram Walkthrough ```mermaid flowchart LR A["SRQL Query Engine"] -->|"Typed Parameters"| B["BindParam Enum"] A -->|"Visualization"| C["VizMeta System"] A -->|"Downsampling"| D["Time Bucketing"] A -->|"Graph Queries"| E["Cypher Support"] F["Elixir NIF"] -->|"Translates"| A G["Phoenix Web"] -->|"Uses"| F G -->|"Components"| H["Query Builder UI"] G -->|"Auth"| I["User System"] J["PostgreSQL"] -->|"TLS"| K["Client Certs"] A -->|"Connects"| J ``` <details><summary><h3>File Walkthrough</h3></summary> <table><thead><tr><th></th><th align="left">Relevant files</th></tr></thead><tbody><tr><td><strong>Enhancement</strong></td><td><details><summary>34 files</summary><table> <tr> <td> <details> <summary><strong>mod.rs</strong><dd><code>Refactor query engine to support typed parameters and visualization </code><br><code>metadata</code></dd></summary> <hr> rust/srql/src/query/mod.rs <ul><li>Added <code>BindParam</code> enum for typed SQL parameter binding with support for <br>text, arrays, integers, booleans, floats, and timestamps<br> <li> Introduced <code>downsample</code> module and <code>graph_cypher</code> module for new query <br>capabilities<br> <li> Refactored query execution to check for downsampling before routing to <br>entity-specific handlers<br> <li> Replaced <code>to_debug_sql</code> pattern with <code>to_sql_and_params</code> to return both <br>SQL and typed parameters<br> <li> Added <code>translate_request</code> public function that returns SQL, parameters, <br>pagination metadata, and visualization hints<br> <li> Implemented helper functions for SQL placeholder detection, bind <br>parameter reconciliation, and Diesel query introspection<br> <li> Added comprehensive tests for parameter arity matching, visualization <br>metadata, downsampling, and graph cypher queries</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-393e3fa3d0e41741834cd7cd398a06111ab7b141ae6caca7a5dcc0e036172491">+506/-54</a></td> </tr> <tr> <td> <details> <summary><strong>viz.rs</strong><dd><code>Add visualization metadata generation for query results</code>&nbsp; &nbsp; </dd></summary> <hr> rust/srql/src/query/viz.rs <ul><li>Created new module providing visualization metadata for query results<br> <li> Defined <code>VizMeta</code> struct containing column metadata and visualization <br>suggestions<br> <li> Implemented <code>ColumnType</code> and <code>ColumnSemantic</code> enums to describe result <br>columns<br> <li> Added <code>VizKind</code> enum supporting timeseries and table visualizations<br> <li> Implemented <code>meta_for_plan</code> function that generates appropriate <br>visualization hints for each entity type<br> <li> Provided column definitions with semantic information and units for <br>all supported entities</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-70c860470ae125afe6d7e4a2f9cf9eb36f8967ae773c3a47804329bbf56a2d8b">+605/-0</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>downsample.rs</strong><dd><code>Add time-series downsampling with bucketing and aggregation</code></dd></summary> <hr> rust/srql/src/query/downsample.rs <ul><li>Implemented downsampling module for time-bucketed metric aggregation<br> <li> Added support for multiple aggregation functions (avg, min, max, sum, <br>count) with configurable bucket sizes<br> <li> Implemented series grouping with validation for supported fields per <br>metric type<br> <li> Added comprehensive filter support for all metric entities with <br>type-safe parameter binding<br> <li> Implemented SQL generation with PostgreSQL <code>time_bucket</code> function and <br>proper placeholder rewriting</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-94f68b4684578afa112ab05ff903667b6cd902ad276e17c12af350078b300a6a">+521/-0</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>graph_cypher.rs</strong><dd><code>Add read-only Cypher graph query execution support</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> rust/srql/src/query/graph_cypher.rs <ul><li>Created new module for executing read-only Cypher queries against <br>graph database<br> <li> Implemented security validation to reject write operations (CREATE, <br>MERGE, SET, DELETE, etc.)<br> <li> Added SQL generation that wraps Cypher results in topology payload <br>format with nodes and edges<br> <li> Implemented dollar-quoting for safe Cypher query embedding in SQL<br> <li> Added placeholder rewriting with proper handling of SQL string <br>literals and dollar-delimited strings</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-378b0ec80e1b1221c347160a0dac36c82d082fa06766d47d18cf069220166309">+208/-0</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>parser.rs</strong><dd><code>Add downsampling and graph cypher query parsing support</code>&nbsp; &nbsp; </dd></summary> <hr> rust/srql/src/parser.rs <ul><li>Added <code>GraphCypher</code> entity variant for graph database queries<br> <li> Introduced <code>DownsampleSpec</code> struct with bucket duration, aggregation <br>function, and series field<br> <li> Added <code>DownsampleAgg</code> enum supporting avg, min, max, sum, and count <br>operations<br> <li> Implemented parsing for <code>bucket</code>, <code>agg</code>, and <code>series</code> query parameters with <br>validation<br> <li> Added <code>parse_bucket_seconds</code> function supporting duration suffixes (s, <br>m, h, d) with maximum bounds<br> <li> Added helper functions for parsing aggregation types and normalizing <br>optional strings</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-b2edf55d1721185349ecddb2f4eacc42e0dfcae19b6c2bc638602f187da67e66">+103/-0</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>logs.rs</strong><dd><code>Refactor logs query to return typed SQL parameters</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> rust/srql/src/query/logs.rs <ul><li>Added <code>to_sql_and_params</code> function returning typed <code>BindParam</code> vectors <br>instead of debug SQL<br> <li> Implemented <code>collect_filter_params</code> to extract parameters from filter <br>conditions<br> <li> Added support for text, text array, and integer array parameter types<br> <li> Implemented <code>bind_param_from_stats</code> conversion for statistics query <br>results</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-f4d3b33667f2e79a6ebe4cfff931f93c728d9a81c305ac13586e623850a504db">+107/-1</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>otel_metrics.rs</strong><dd><code>Refactor OTEL metrics query to return typed SQL parameters</code></dd></summary> <hr> rust/srql/src/query/otel_metrics.rs <ul><li>Added <code>to_sql_and_params</code> function with typed parameter collection<br> <li> Implemented <code>collect_filter_params</code> for extracting parameters from <br>filter conditions<br> <li> Added support for text, text array, and boolean parameter types<br> <li> Implemented bind parameter reconciliation and validation with diesel <br>bind count checking</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-8106bfa2099b8a6d945b338532f8b1108467e2f0592ddbd64d546fcbce3e3613">+91/-1</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>interfaces.rs</strong><dd><code>Refactor interfaces query to return typed SQL parameters</code>&nbsp; </dd></summary> <hr> rust/srql/src/query/interfaces.rs <ul><li>Added <code>to_sql_and_params</code> function with comprehensive parameter <br>collection<br> <li> Implemented <code>collect_filter_params</code> supporting text, integer, and text <br>array parameters<br> <li> Added validation for IP address filter list size limits<br> <li> Implemented bind parameter reconciliation with diesel bind count <br>verification</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-1ec833d4525fb2806523888b8c57b76ca8dd2ab70539368ccecc4d262769c8c5">+118/-1</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>traces.rs</strong><dd><code>Refactor traces query to return typed SQL parameters</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> rust/srql/src/query/traces.rs <ul><li>Added <code>to_sql_and_params</code> function with typed parameter collection<br> <li> Implemented <code>collect_filter_params</code> supporting text and integer list <br>parameters<br> <li> Added helper function <code>collect_i32_list</code> for parsing integer arrays from <br>filters<br> <li> Implemented bind parameter reconciliation and diesel bind count <br>validation</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-f390b0aef82baa9c3438719276dca70be826318d7446074a83b4527558528e19">+112/-1</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>cpu_metrics.rs</strong><dd><code>Refactor CPU metrics query to return typed SQL parameters</code></dd></summary> <hr> rust/srql/src/query/cpu_metrics.rs <ul><li>Added <code>to_sql_and_params</code> function with typed parameter collection<br> <li> Implemented <code>collect_filter_params</code> supporting text, integer, and float <br>parameters<br> <li> Added <code>bind_param_from_stats</code> conversion for statistics query results<br> <li> Implemented bind parameter reconciliation and diesel bind count <br>validation</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-400c860c805cd3e1a5e7f7c42cece63cce3e62c1f3fd8b49a5803e26b40fe31e">+93/-1</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>pollers.rs</strong><dd><code>Refactor pollers query to return typed SQL parameters</code>&nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> rust/srql/src/query/pollers.rs <ul><li>Added <code>to_sql_and_params</code> function with typed parameter collection<br> <li> Implemented <code>collect_filter_params</code> supporting text and boolean <br>parameters<br> <li> Moved <code>parse_bool</code> function to module level for reusability<br> <li> Implemented bind parameter reconciliation and diesel bind count <br>validation</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-e2192a3ba3041ed995e0abf9973489b7dff550949ff75da41d31bf514e765cc4">+84/-11</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>db.rs</strong><dd><code>Add client certificate TLS authentication support</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> rust/srql/src/db.rs <ul><li>Added support for client certificate and key TLS authentication<br> <li> Implemented <code>load_client_certs</code> function to parse certificate chains <br>from PEM files<br> <li> Implemented <code>load_client_key</code> function to parse private keys from PEM <br>files<br> <li> Added <code>build_client_config</code> function to construct Rustls client <br>configuration with optional mutual TLS<br> <li> Updated <code>PgConnectionManager::new</code> to accept client certificate and key <br>paths</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-8d5a0c48024aa12a3e06de065ca71e940d2b5007fbc34ef87471b90d7937891c">+77/-10</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>process_metrics.rs</strong><dd><code>Refactor process metrics query to return typed SQL parameters</code></dd></summary> <hr> rust/srql/src/query/process_metrics.rs <ul><li>Added <code>to_sql_and_params</code> function with typed parameter collection<br> <li> Implemented <code>collect_filter_params</code> supporting text, integer, and float <br>parameters<br> <li> Implemented bind parameter reconciliation and diesel bind count <br>validation</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-c70bb600f5fba7199d80930fdc735cfc38371ef2d4ff3c821af55100e4cb0bac">+80/-1</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>timeseries_metrics.rs</strong><dd><code>Refactor timeseries metrics query to return typed SQL parameters</code></dd></summary> <hr> rust/srql/src/query/timeseries_metrics.rs <ul><li>Added <code>to_sql_and_params</code> function with typed parameter collection<br> <li> Implemented <code>collect_filter_params</code> supporting text, integer, and float <br>parameters<br> <li> Added metric scope handling for forced metric type parameters<br> <li> Implemented bind parameter reconciliation and diesel bind count <br>validation</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-8450bff3aef4c1271bbf9b96809b4a5ac7b218e3f98c8b60387e35dbf4845bb0">+85/-1</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>behaviour.ex</strong><dd><code>Add Elixir SRQL behaviour module definition</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> web-ng/lib/serviceradar_web_ng/srql/behaviour.ex <ul><li>Created new Elixir behaviour module defining SRQL callback interface<br> <li> Defined <code>srql_response</code> type as a map<br> <li> Specified <code>query_request</code> callback accepting a map and returning success <br>or error tuple</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-fac2f59bc5bfeb6802901d6ea2260d4c2c0a8fb52772052109c4b83e8534fbba">+7/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>devices.rs</strong><dd><code>Add parameterized SQL generation for device queries</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> rust/srql/src/query/devices.rs <ul><li>Added <code>BindParam</code> import to support parameterized query generation<br> <li> Implemented <code>to_sql_and_params()</code> function to generate SQL with bind <br>parameters<br> <li> Added <code>collect_text_params()</code> and <code>collect_filter_params()</code> helper <br>functions for parameter collection<br> <li> Includes validation to ensure bind count matches between diesel and <br>collected parameters</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-3202f22fff6863ed7848a129c49e2323322462b379d896d3fca2e59aa6f7b4c5">+90/-1</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>disk_metrics.rs</strong><dd><code>Add parameterized SQL generation for disk metrics</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> rust/srql/src/query/disk_metrics.rs <ul><li>Added <code>BindParam</code> import for parameterized queries<br> <li> Implemented <code>to_sql_and_params()</code> function with time range and filter <br>parameter handling<br> <li> Added <code>collect_text_params()</code> and <code>collect_filter_params()</code> for parameter <br>extraction<br> <li> Updated test configuration to include <code>downsample</code> field</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-b5a536baa37176f971e96c789991202b483314e2a85d4556489b4568a5675162">+75/-1</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>memory_metrics.rs</strong><dd><code>Add parameterized SQL generation for memory metrics</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> rust/srql/src/query/memory_metrics.rs <ul><li>Added <code>BindParam</code> import for parameterized query support<br> <li> Implemented <code>to_sql_and_params()</code> function with parameter collection <br>logic<br> <li> Added <code>collect_text_params()</code> and <code>collect_filter_params()</code> helper <br>functions<br> <li> Updated test configuration to include <code>downsample</code> field</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-a12a227c4b5c224359799853f5fd7bbf626ec14b1e7eae40e4c65b1db6699417">+76/-1</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>device_updates.rs</strong><dd><code>Add parameterized SQL generation for device updates</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> rust/srql/src/query/device_updates.rs <ul><li>Added <code>BindParam</code> import for parameterized query generation<br> <li> Implemented <code>to_sql_and_params()</code> function with filter and time range <br>handling<br> <li> Added <code>collect_text_params()</code> and <code>collect_filter_params()</code> for parameter <br>collection<br> <li> Supports both scalar and list filter values with validation</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-77b99f0ef409a6a3e4ecc65800ae841231a82b27a64e8dc1d4667db6698a539d">+81/-1</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>services.rs</strong><dd><code>Add parameterized SQL generation for services queries</code>&nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> rust/srql/src/query/services.rs <ul><li>Added <code>BindParam</code> import for parameterized query support<br> <li> Implemented <code>to_sql_and_params()</code> function with parameter collection<br> <li> Added <code>collect_text_params()</code> and <code>collect_filter_params()</code> helper <br>functions<br> <li> Updated test configuration to include <code>downsample</code> field</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-ad5e7e1a352406ed473a765de4856625f1bede590e7e2c724163bcd1e7b313e9">+68/-1</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>events.rs</strong><dd><code>Add parameterized SQL generation for events queries</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> rust/srql/src/query/events.rs <ul><li>Added <code>BindParam</code> import for parameterized queries<br> <li> Implemented <code>to_sql_and_params()</code> function with parameter extraction <br>logic<br> <li> Added <code>collect_text_params()</code> and <code>collect_filter_params()</code> for parameter <br>handling<br> <li> Supports text and integer parameter types for various event fields</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-ad603163a35223c63c68279e53a1a4c9219b9096042a168d8a87443abaa29a58">+74/-1</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>trace_summaries.rs</strong><dd><code>Add parameterized SQL generation for trace summaries</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> rust/srql/src/query/trace_summaries.rs <ul><li>Added <code>BindParam</code> import for parameterized query support<br> <li> Implemented <code>to_sql_and_params()</code> function that converts query binds to <br><code>BindParam</code><br> <li> Added <code>bind_param_from_query()</code> helper function to convert <code>SqlBindValue</code> <br>to <code>BindParam</code><br> <li> Handles multiple bind value types including text, arrays, integers, <br>and timestamps</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-ff98619eb5dc4d75205d1797477e9e2880f42081e8d0c61d6fd1d6e5926b72fa">+24/-1</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>config.rs</strong><dd><code>Add PostgreSQL SSL certificate configuration and embedded mode</code></dd></summary> <hr> rust/srql/src/config.rs <ul><li>Added <code>pg_ssl_cert</code> and <code>pg_ssl_key</code> configuration fields for PostgreSQL <br>client certificates<br> <li> Updated configuration loading to read <code>PGSSLCERT</code> and <code>PGSSLKEY</code> <br>environment variables<br> <li> Added <code>embedded()</code> constructor method for creating in-memory <br>configurations<br> <li> Embedded configuration supports all standard settings with sensible <br>defaults</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-8a094b8b89c66adf329551634fa2c9c0e2ad9a1043f42012ad11cd3bf3f8ab6a">+23/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>lib.rs</strong><dd><code>Add Elixir NIF for SRQL query translation</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> web-ng/native/srql_nif/src/lib.rs <ul><li>New Rust NIF (Native Implemented Function) module for Elixir <br>integration<br> <li> Implements <code>translate()</code> function to convert SRQL queries to SQL with <br>parameters<br> <li> Supports query direction (next/prev) and optional cursor/limit <br>parameters<br> <li> Returns JSON-encoded response or error message as Elixir tuple</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-74a225cdf31e64db49fb42c0419c524068150b07528e8d722a75e3ec8034c297">+49/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>device_graph.rs</strong><dd><code>Add parameterized SQL generation for device graph</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> rust/srql/src/query/device_graph.rs <ul><li>Added <code>BindParam</code> import for parameterized query support<br> <li> Implemented <code>to_sql_and_params()</code> function that extracts parameters from <br>query plan<br> <li> Returns SQL string with three bind parameters: device_id, <br>collector_owned_only, include_topology</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-a6e5019c94fdef4f11f3c834e5e6ecac78efd5ce40f34b93bb64fe8076db9011">+14/-1</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>lib.rs</strong><dd><code>Export public API and add embedded mode support</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> rust/srql/src/lib.rs <ul><li>Exported public API types: <code>QueryDirection</code>, <code>QueryEngine</code>, <code>QueryRequest</code>, <br><code>QueryResponse</code>, <code>TranslateRequest</code>, <code>TranslateResponse</code><br> <li> Added <code>EmbeddedSrql</code> struct for embedded mode usage<br> <li> Implemented <code>EmbeddedSrql::new()</code> constructor that initializes database <br>pool and query engine<br> <li> Enables use of SRQL as a library within other Rust applications</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-8c0401c0d0bb9761ed66ff5328703755132a87d625f77878f53037e2329644b8">+19/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>app.js</strong><dd><code>Add Phoenix LiveView application initialization</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> web-ng/assets/js/app.js <ul><li>New Phoenix LiveView application entry point<br> <li> Configures LiveSocket with CSRF token and colocated hooks<br> <li> Integrates topbar for visual page loading feedback<br> <li> Includes development mode features for live reload and editor <br>integration</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-16bc6f19f34cfdd66ac36b4ef2dd66d34e3470c4eb324ebc3e4bb8e58a91360c">+83/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>heroicons.js</strong><dd><code>Add Heroicons Tailwind CSS plugin</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> web-ng/assets/vendor/heroicons.js <ul><li>New Tailwind CSS plugin for Heroicons SVG icon integration<br> <li> Supports multiple icon sizes: outline (24px), solid (24px), mini <br>(20px), micro (16px)<br> <li> Dynamically loads SVG files and converts them to CSS mask utilities<br> <li> Enables icon usage via Tailwind classes like <code>hero-*</code></ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-435329705234b4577a06b84302017b73aaf264e7af376e6bfd5abbf71611d9bd">+43/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>page_controller.ex</strong><dd><code>Add page controller with authentication redirect</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> web-ng/lib/serviceradar_web_ng_web/controllers/page_controller.ex <ul><li>New Elixir controller module for page routing<br> <li> Implements <code>home()</code> action that redirects authenticated users to <br>dashboard<br> <li> Renders home page for unauthenticated users<br> <li> Uses scope and user information from connection assigns</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-191be2756e89e5e7533d99c008d252d053347ae5b9585cb1a74acec36ceefb4e">+11/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>home.html.heex</strong><dd><code>Phoenix framework landing page with community links</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> web-ng/lib/serviceradar_web_ng_web/controllers/page_html/home.html.heex <ul><li>Creates a landing page template with Phoenix branding and decorative <br>SVG background<br> <li> Displays Phoenix framework version badge and theme toggle component<br> <li> Provides navigation links to documentation, GitHub repository, and <br>changelog<br> <li> Includes community engagement links for Elixir Forum, Discord, Slack, <br>and Fly.io deployment</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-0ce3797073d29e377a1dfc2a0fee0dcc83a0f3ade3fe835a3684d06d5badc59c">+202/-0</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>srql_components.ex</strong><dd><code>SRQL query interface components with builder and visualization</code></dd></summary> <hr> web-ng/lib/serviceradar_web_ng_web/components/srql_components.ex <ul><li>Implements SRQL query bar component with text input and builder toggle <br>functionality<br> <li> Creates results table component with dynamic column detection and cell <br>value formatting<br> <li> Provides auto-visualization component supporting timeseries and <br>categories chart types<br> <li> Builds comprehensive query builder UI with entity selection, time <br>filters, downsampling, and filter management</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-aeeb844af4bc736fac30dab33b73c6d9023ffa5c75b79dacfbc287b61524ec59">+551/-0</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>builder.ex</strong><dd><code>SRQL query builder with parsing and state management</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> web-ng/lib/serviceradar_web_ng_web/srql/builder.ex <ul><li>Implements SRQL query builder state management with parsing and <br>validation logic<br> <li> Provides query construction from builder state and parsing of SRQL <br>query strings<br> <li> Handles normalization of query parameters including entity, time, <br>bucket, aggregation, and filters<br> <li> Validates downsample operations, filter fields, and sort <br>configurations against entity catalog</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-0eed4282479f6f8c4f3ed9ae60280a2acdd5ff9d52db06dde050ed49d3b6da20">+596/-0</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>page.ex</strong><dd><code>SRQL page state and event handling logic</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> web-ng/lib/serviceradar_web_ng_web/srql/page.ex <ul><li>Manages SRQL page state initialization and query list loading with <br>result handling<br> <li> Implements event handlers for query submission, builder toggle, and <br>filter management<br> <li> Provides visualization metadata extraction and error formatting for <br>SRQL responses<br> <li> Handles builder state synchronization with query draft and parameter <br>normalization</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-0c9304de34d47d5cb556153e217371ff7865e9bab17a4bd1d7c626815d6246f1">+470/-0</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>user_notifier.ex</strong><dd><code>User email notification system with Swoosh integration</code>&nbsp; &nbsp; &nbsp; </dd></summary> <hr> web-ng/lib/serviceradar_web_ng/accounts/user_notifier.ex <ul><li>Implements email delivery functions using Swoosh for user <br>notifications<br> <li> Provides email templates for magic link login, account confirmation, <br>and email update instructions<br> <li> Handles conditional logic to send confirmation instructions for <br>unconfirmed users<br> <li> Integrates with application mailer for reliable email delivery</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-b3d07040520183bd78e0863da19728a3af1885cda55569f50c232ad086fae24b">+84/-0</a>&nbsp; &nbsp; </td> </tr> </table></details></td></tr><tr><td><strong>Tests</strong></td><td><details><summary>1 files</summary><table> <tr> <td> <details> <summary><strong>harness.rs</strong><dd><code>Update test harness for SSL certificate configuration</code>&nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> rust/srql/tests/support/harness.rs <ul><li>Updated test configuration to include new <code>pg_ssl_cert</code> and <code>pg_ssl_key</code> <br>fields<br> <li> Both fields set to <code>None</code> for test environment</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-f15ba86798901e9218b7310fbeae763be61ab6ccf3fe592a79d63eda02eea8b4">+2/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> </table></details></td></tr><tr><td><strong>Configuration changes</strong></td><td><details><summary>3 files</summary><table> <tr> <td> <details> <summary><strong>generate-certs.sh</strong><dd><code>Add dynamic certificate SAN support and workstation cert</code>&nbsp; </dd></summary> <hr> docker/compose/generate-certs.sh <ul><li>Added logic to check for missing SANs in existing CNPG certificates <br>when <code>CNPG_CERT_EXTRA_IPS</code> is set<br> <li> Regenerates CNPG certificate if required SANs are missing<br> <li> Supports dynamic SAN configuration via environment variable<br> <li> Added generation of <code>workstation</code> certificate for external developer <br>connections</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-8298241543b4744a6ac7780c760ac5b5a0a87ba62de19c8612ebe1aba0996ebd">+28/-6</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>docker-compose.yml</strong><dd><code>Add CNPG public port and certificate configuration</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> docker-compose.yml <ul><li>Added <code>CNPG_CERT_EXTRA_IPS</code> environment variable to cert-generator <br>service<br> <li> Added public port binding for CNPG database service (default <br>127.0.0.1:5455)<br> <li> Allows external connections to PostgreSQL when configured<br> <li> Supports dynamic certificate generation for additional IP addresses</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-e45e45baeda1c1e73482975a664062aa56f20c03dd9d64a827aba57775bed0d3">+6/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>app.css</strong><dd><code>Tailwind CSS v4 configuration with daisyUI themes</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> web-ng/assets/css/app.css <ul><li>Configures Tailwind CSS v4 with new import syntax and source <br>directives<br> <li> Integrates daisyUI plugin with custom light and dark theme <br>configurations using OKLCH color values<br> <li> Adds custom Tailwind variants for Phoenix LiveView classes <br>(<code>phx-click-loading</code>, <code>phx-submit-loading</code>, <code>phx-change-loading</code>)<br> <li> Implements transparent display for LiveView wrapper divs to support <br>proper layout composition</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-e2f1698b818ca68e34f2f330886b5dc3621fb6033b4414869c7e3d677aa4c4d3">+105/-0</a>&nbsp; </td> </tr> </table></details></td></tr><tr><td><strong>Dependencies</strong></td><td><details><summary>2 files</summary><table> <tr> <td> <details> <summary><strong>daisyui-theme.js</strong><dd><code>Add daisyUI theme vendor library</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> web-ng/assets/vendor/daisyui-theme.js <ul><li>New vendor file containing daisyUI theme plugin and color definitions<br> <li> Includes 40+ pre-built color themes (cyberpunk, acid, dark, light, <br>luxury, etc.)<br> <li> Provides Tailwind CSS plugin for theme customization and application<br> <li> MIT licensed library for UI theming</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-1f651678246fddd353d5b865da740f0fd7f0a16d5e252a378d791ae43136c803">+124/-0</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>topbar.js</strong><dd><code>Add topbar progress bar vendor library</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> web-ng/assets/vendor/topbar.js <ul><li>New vendor file containing topbar progress bar library<br> <li> Implements progress bar visualization with configurable colors and <br>styling<br> <li> Supports show/hide/progress control methods for page loading <br>indicators<br> <li> MIT licensed library for visual feedback during navigation</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-b33ec2270b5ebb791a0c96c3ff024f82d66d346be6d542374e5885273f7bc004">+138/-0</a>&nbsp; </td> </tr> </table></details></td></tr><tr><td><strong>Documentation</strong></td><td><details><summary>1 files</summary><table> <tr> <td> <details> <summary><strong>AGENTS.md</strong><dd><code>Complete Phoenix v1.8 development guidelines and best practices</code></dd></summary> <hr> web-ng/AGENTS.md <ul><li>Comprehensive project guidelines for Phoenix v1.8 web framework <br>development<br> <li> Detailed authentication flow documentation with router scope and <br><code>live_session</code> patterns<br> <li> JavaScript, CSS, and UI/UX design guidelines emphasizing Tailwind CSS <br>and custom components<br> <li> Extensive Elixir, Mix, testing, Phoenix, Ecto, HTML, and LiveView best <br>practices and patterns</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-33d10e9419f85fc4e84bee48a6c3d32c47213354d4e32a0d37f3714ef0f71857">+510/-0</a>&nbsp; </td> </tr> </table></details></td></tr><tr><td><strong>Additional files</strong></td><td><details><summary>101 files</summary><table> <tr> <td><strong>.env.example</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-a3046da0d15a27e89f2afe639b25748a7ad4d9290af3e7b1b6c1a5533c8f0a8c">+18/-1</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>.tool-versions</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-751af1a340658c7b8176fe32d7db9fadbe15d1d075daba1919a91df04155bc70">+2/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>AGENTS.md</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-a54ff182c7e8acf56acfd6e4b9c3ff41e2c41a31c9b211b2deb9df75d9a478f9">+46/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>design.md</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-cb59e5a2e863f7206ac3be889b3cc23b7eef933fbc33909b0d4f19235eeacbd6">+151/-0</a>&nbsp; </td> </tr> <tr> <td><strong>proposal.md</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-381b711be98b0c138b5513beec105376f4b63e46d82bfd75e7d22e4b0eb3e6ff">+48/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>tasks.md</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-52b58e4010c2df75f7534dc76212ed1c275e462f5f4be90fa0b947a5bfd407ce">+109/-0</a>&nbsp; </td> </tr> <tr> <td><strong>.formatter.exs</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-09a9d30041f1ef21d73f728097fc1001abe492ddde2fd795d34ea33fa45d0a9d">+6/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>README.md</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-f0d5289ea28edbf0f030f4f08f31e8c05e701730d23d2aa4ff4ed784a401e797">+56/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>tsconfig.json</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-0e5ef6ab27f78a41f637db298f4c3423ace89ceb7ac107666531adaad99497cf">+32/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>daisyui.js</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-1b1d093f688f9b2cd8fd59953adaa20204e62bc26aeda7146579735ff47e7124">+1031/-0</a></td> </tr> <tr> <td><strong>config.exs</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-da0b524f083d350207155c247526098fdd68866d64e7e4eae3d96fdae31bcfb6">+83/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>dev.exs</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-6cc2919e487233a5ea42f09327f73cd1ef2ef6250301c4641d43e0f4a8528131">+151/-0</a>&nbsp; </td> </tr> <tr> <td><strong>prod.exs</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-3fab1a301b2b81310beca7d09cd817a9b0eeb89fbcc59e7095ca428110f10eab">+31/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>runtime.exs</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-f1093c1a9d74ba3cd35f31e2c50dec60b4fb9c241fc5ff9f62bcbcc0559691ae">+190/-0</a>&nbsp; </td> </tr> <tr> <td><strong>test.exs</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-1bcfbd23e6504ddc21fc728c1a7fb46c7bbd62a94b5c4e95726b29c982604a50">+105/-0</a>&nbsp; </td> </tr> <tr> <td><strong>ready.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-190d1b3e459357bf8443f7fba35a94b591c60b99330d855d5b79501157b1243e">+33/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>serviceradar_web_ng.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-d5469296deac2d697bc17553fb4b312c38ae3b4ca70424c3f1d1dcb479457f35">+9/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>accounts.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-39756bf90cbfe63ce06a756bd44c5b1ea25fb66f95002f9029f72ecc52b0b69e">+297/-0</a>&nbsp; </td> </tr> <tr> <td><strong>scope.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-8142f5d0719ccc41761eee96fa01036051e73b305766b3bb09e0c36945ef84dd">+33/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>user.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-b72d009cde455d21c00fea9b54279782128449a0ce9b8080384502654e0ffba2">+136/-0</a>&nbsp; </td> </tr> <tr> <td><strong>user_token.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-aa226d125993cef89d3b80a8b5bce8bc1693993a3bde94c49aaebdb295b65f6f">+156/-0</a>&nbsp; </td> </tr> <tr> <td><strong>device_controller.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-ac55b43afa10dd331fa712def4e0af4608417e58727fbde08a6702ef180e46d4">+282/-0</a>&nbsp; </td> </tr> <tr> <td><strong>query_controller.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-ddf39e3a7443a5647673a1303b4df01a5212e82aa84ac9f810be358fcf84769e">+19/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>application.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-c7f656ca70ecb16c87c6c152dc6777117876b7885ee30deeb630a12cbf734ac4">+35/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>edge.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-89bfbe26459dbf864f9aac049d00441dd399990b165b2bbc2f69001de55c53f9">+11/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>onboarding_package.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-32cea4276fd8de69d82d39ff1f19a2fa90feedc64fb855bb32dba0bd0de9b5fd">+41/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>onboarding_token.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-e2e1ed43a06834d4af7dfb07b38dd2f67d4c801ec8c7a1eea40759b59de83010">+82/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>graph.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-da24d024a22b05733a6c825a90128a904a1f460b98de82be8f62d0c380c3e6ae">+104/-0</a>&nbsp; </td> </tr> <tr> <td><strong>infrastructure.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-eb9ad11c35a6b0afdb349dc9e4ad826dc4da2f355b0345d40e3ade6dfaa9b131">+19/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>poller.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-380d6b443258253409acec0b48d23bab28cc4f9cbf6e0c17d00e46414653a7cc">+21/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>service.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-51dd4344d68674335b888e0b9227b2e9076bc5630c119d442e94a7015d34a18d">+15/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>inventory.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-a3a4fd8769b4d293500c8ae003aca2417bc7c239ff7b51f601214f589d43cace">+19/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>device.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-51b6e305c83ac39015fe5ead7db8933d5965b118f1671c8b3a26ce52ac5e32c7">+25/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>mailer.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-c4acbfc8da4fbb95c671d9a7f0a53ce4e4fc9cad46148bdfc2eba277973be106">+3/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>repo.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-0a23adc979e73d58eabf1ce3017652150c4ad4219909e3af171a61119fef977f">+5/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>srql.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-dffa2e7586a9d96fb5dcadde453dae7f3b09624e84b0878da10f57706781aba1">+219/-0</a>&nbsp; </td> </tr> <tr> <td><strong>native.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-194d2427c0dc1f26ae2c2b75a23336a60159bbc6b70625ec49afe98d0a1003bf">+8/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>serviceradar_web_ng_web.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-bfeb4e8164b81e0ff846081b11224a63282c549425d41e300932d79d164155ae">+116/-0</a>&nbsp; </td> </tr> <tr> <td><strong>core_components.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-462cb1dff5e3616d06e8858bdce90c2e284b8dc67e191dfec41ee73daecf4101">+498/-0</a>&nbsp; </td> </tr> <tr> <td><strong>layouts.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-75eaced063e3e7325588594e742492bab789a979319a34f2f623f6d161cfffd2">+318/-0</a>&nbsp; </td> </tr> <tr> <td><strong>root.html.heex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-88684210500a6ce74e883c4fa1c496f6ffc540e4bfd2ea1015f61e5f930b24d1">+36/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>ui_components.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-ea4114e247de47a7a6261975047564b934b013e368aa2a7546a7853a9eca0f86">+302/-0</a>&nbsp; </td> </tr> <tr> <td><strong>error_html.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-1793d8bc6051b96653eea71beabb5ee2102045477bb78ddabd16d563d281ae4d">+24/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>error_json.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-6f65a24c96a162f18486908a6013653271eaa6d88b6d896d9dd0a7a24344da36">+21/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>page_html.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-6e56b53f79326199d7daea8ed02ef6c62eb6e6d7c9a1a1a94ef5228b0beca2a9">+10/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>user_session_controller.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-7d5f49c78692d273eea38a1d061d178f4b95a6ff2b6a878d70660d0cec1343fe">+67/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>engine.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-28a935d2080e0fbd4c4b4c65c360982028dc75c05e41735ef723ceece350a9f8">+70/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>plugin.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-ecb54984739784b94dcddc283ff6fc1b77f10d0734ba00213685ae445934c0b0">+10/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>categories.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-b8903956c6563e882819929f26babc14d3ce864108f1a90f1015c29868298a84">+53/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>graph_result.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-447ac4dd5de37c09b4b402b31f5793ac9b448be0fe7d0b766c3c6c246fb03dfc">+106/-0</a>&nbsp; </td> </tr> <tr> <td><strong>table.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-89eab1b66d0fa5509ade32a3caa785d45c715fd31fd715a6bb13fb7a6e5ae6c8">+63/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>timeseries.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-2dd46da2ac00979257fc2b79ad1c72441c867402317f0f903c6d76ae3c8faecd">+226/-0</a>&nbsp; </td> </tr> <tr> <td><strong>topology.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-2f44cf63d61f5412f7d9a08c4fb7e6cfdc52c66aae4986d490816586c4ebc32f">+372/-0</a>&nbsp; </td> </tr> <tr> <td><strong>registry.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-c66e2f611e24d71406e8b122da67a9bd68c15801f54fb8e5cf6866752359f4ad">+15/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>endpoint.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-926d7a0d0b480533e16e6cdf4aa701391c64669ad832938fe098e10f3fdadf56">+55/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>gettext.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-fe005cb379080edc3bea8bffc72e55e62ed7131009d4ac5f0929ab98b6f0d484">+25/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>index.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-3ba8661d7541fee8ae421fa038a2015dfad52a75c8de3412b1d2561f2617d585">+113/-0</a>&nbsp; </td> </tr> <tr> <td><strong>index.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-261a01f4876e5984e1d9e9b38a3540675dfb0272abc30e6bdb2a4fa610353cc7">+80/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>index.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-5baf4b9eb7b1983ce19f58bd0f2a6b8de518856ef3d8141585749f9108e038b5">+79/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>index.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-fdb324ee20880a7945d6314d2f31a4554d0ac0319c5ec14f24159e1c87ce2ac6">+80/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>index.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-5c469994d989d04955c9b8a31536ed518a182b3f46819ecee9c3c22f651a4321">+79/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>index.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-cad18ffe9881e0bd3dd9702704fa09e45548e0bc5c9f7465118d8e6631a436f2">+80/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>index.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-104816b5afaf56b1435b12b527de52858c144f5f67c8487404940a55d50e95f9">+80/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>confirmation.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-4300116173c1311d4cc830c80c95334aa7d9930714ba7c251735b8fe85e6f56c">+94/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>login.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-e8589f1f0def5440b7a3b23b8269775cfb8d1faf63200e9ad46c5c4aa25bb014">+132/-0</a>&nbsp; </td> </tr> <tr> <td><strong>registration.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-d2bccf0e3b3364f00208a882632418d57250cb0da23eba34f83eab6959413d0d">+88/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>settings.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-f9d2d04eddd93536ff7f531d1936ac5f21e7b9c67ad5ec792f1ff2c4de05ea12">+157/-0</a>&nbsp; </td> </tr> <tr> <td><strong>safe_parsers.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-e301468002e4bac3964f56b2b4883af915102592f5566a5850fb1bee4b0f2f55">+34/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>router.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-df516cd33165cd85914c1ccb3ff6511d3fe688d4a66498b99807958998c5d07c">+94/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>catalog.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-80c07c25c17e48bf86860ec91db10256b7700a863d5371798e1893e741dc0e15">+308/-0</a>&nbsp; </td> </tr> <tr> <td><strong>viz.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-68f3e8258d386d9ca41de0c116e90f39442253c7194ea177b3c842c184535218">+173/-0</a>&nbsp; </td> </tr> <tr> <td><strong>telemetry.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-c98169a140d7a6eb1945081fbc2f60918d460f50c2eae328c57f4f7b8da31c2c">+93/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>user_auth.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-eed904963eb2089bfce4e634d5229889213b12d02489e07af53f1b1982a42d78">+287/-0</a>&nbsp; </td> </tr> <tr> <td><strong>mix.exs</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-cd109ab3082c4d4fd2a746f47eb49bda3f5dfcaf0f440812594765299905bb2d">+97/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>Cargo.toml</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-2bd2401576861a4a4ceafeb1e9a739c83787bfa83be20d04747bae6381fafd49">+16/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>errors.po</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-5e2bd56e2b45ef1c0af0df17268150fc91e76c0a01703c1b0ec7048f2cc4f535">+112/-0</a>&nbsp; </td> </tr> <tr> <td><strong>errors.pot</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-4f664462ae8525cb7f8be151fce45fbc7211a6fa72ff8678508269ea773b23b9">+109/-0</a>&nbsp; </td> </tr> <tr> <td><strong>.formatter.exs</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-a93882a87950cbd97e1bddab83527c5c88af04347ba457933bff0ef2d843fe13">+4/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>20251215020449_create_ng_users_auth_tables.exs</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-74766ec054e04cc5ce719712dd67c7bb07b22de1a6d4544060f1a086f94436b9">+28/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>seeds.exs</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-2c1642b3f26c15199ad67b8cececc864bd43a1a47350ed97152700ea729a96a3">+11/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>robots.txt</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-533db5db95b7e5876f57dfcab70c6e8ab238310bece57c5b9e628784daf7ddea">+5/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>graph_cypher_integration_test.exs</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-62d708913a0c32472d10acb991c20e1513e159fc200bf9a7fb43f7c52d68bf02">+153/-0</a>&nbsp; </td> </tr> <tr> <td><strong>srql_nif_integration_test.exs</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-aa381b8fad4397392f1342f58d8ff7abe2147463a7f0de10f6b891a527227be8">+193/-0</a>&nbsp; </td> </tr> <tr> <td><strong>api_device_controller_property_test.exs</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-921f7550298c2b6f606fe10313a3f0a1487411eaf5648b84c63b7dc4a0ab009b">+43/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>api_query_controller_property_test.exs</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-bf27ce20246c51a047950ea64cda88dc82a2cbaa42d0e779f32256d09c31ecb4">+52/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>edge_onboarding_token_property_test.exs</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-b02fd0b47294d4532ba2e5ddf43a3d2fa53425c66ca6dbc0a613929dac959032">+46/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>srql_property_test.exs</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-da1559d3a036c7208201b3711ac5124744bf72a515918d8403522a4774d52fea">+27/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>srql_query_input_property_test.exs</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-2b2952d6474d80f46fa85539ddec7ebbfb79480491a412aa129f1985baa09a3c">+90/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>accounts_test.exs</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-4ac5a781347a5d4433b8c09efadfc51deb9b71cc1fbc298891cd2f6a702004d2">+421/-0</a>&nbsp; </td> </tr> <tr> <td><strong>infrastructure_test.exs</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-d31d547ce16907eb7343e42d477287bb5b114ff438c43cc2db64ade73be1c705">+28/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>inventory_test.exs</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-cdde3a4f7e98e7f22e697c4408a92cbe7e92bc450170b5721b2375630f50664f">+30/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>error_html_test.exs</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-8dde1d39a4a2992323b4c616069482bb514b28c39a924a24ca988de1fb258f6b">+15/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>error_json_test.exs</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-6177875640e2409a87896ef8952bf1f66f59c282bc8c1a421c610921ff0f8c7c">+14/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>page_controller_test.exs</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-57a42bbe63c19d09e2e810ef6f924ea07af552881bafb7c990a3a6042816ca54">+8/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>user_session_controller_test.exs</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-4348c958b471cb5dbb2da917f60b3c2c4276d39f28717d0092e2823bb6f0816e">+147/-0</a>&nbsp; </td> </tr> <tr> <td><strong>dashboard_engine_test.exs</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-97f5267521eed50701a3923390415cf711fd29b194dccd40b4e806fcc7fc59ba">+41/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>dashboard_topology_plugin_test.exs</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-893c3d3d0229d94390763f681b9f110910bdaf7766fc1e5b6795a14cb3c7f518">+31/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>device_live_test.exs</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-8130c6c6897063cc952a326ac4e9d26373cfabd3413d181574c0e2427aba533e">+27/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>poller_live_test.exs</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-c9a026be62441a1c463f41b207287aaad649a11228d9115a68bed15269cbd109">+25/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>confirmation_test.exs</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-796bd0e55e1f487ca730e57b2dfacb80847bdaf3f6230e2012b37bac525774ec">+118/-0</a>&nbsp; </td> </tr> <tr> <td><strong>Additional files not shown</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2139/files#diff-2f328e4cd8dbe3ad193e49d92bcf045f47a6b72b1e9487d366f6b8288589b4ca"></a></td> </tr> </table></details></td></tr></tbody></table> </details> ___
gitguardian[bot] commented 2025-12-15 17:15:51 +00:00 (Migrated from github.com)
Author
Owner

Imported GitHub PR comment.

Original author: @gitguardian[bot]
Original URL: https://github.com/carverauto/serviceradar/pull/2139#issuecomment-3656760663
Original created: 2025-12-15T17:15:51Z

⚠️ GitGuardian has uncovered 1 secret following the scan of your pull request.

Please consider investigating the findings and remediating the incidents. Failure to do so may lead to compromising the associated services or software components.

🔎 Detected hardcoded secret in your pull request
GitGuardian id GitGuardian status Secret Commit Filename
17619916 Triggered Generic Password d4ee01d332 web-ng/config/dev.exs View secret
🛠 Guidelines to remediate hardcoded secrets
  1. Understand the implications of revoking this secret by investigating where it is used in your code.
  2. Replace and store your secret safely. Learn here the best practices.
  3. Revoke and rotate this secret.
  4. If possible, rewrite git history. Rewriting git history is not a trivial act. You might completely break other contributing developers' workflow and you risk accidentally deleting legitimate data.

To avoid such incidents in the future consider


🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.

Imported GitHub PR comment. Original author: @gitguardian[bot] Original URL: https://github.com/carverauto/serviceradar/pull/2139#issuecomment-3656760663 Original created: 2025-12-15T17:15:51Z --- #### ⚠️ GitGuardian has uncovered 1 secret following the scan of your pull request. Please consider investigating the findings and remediating the incidents. Failure to do so may lead to compromising the associated services or software components. <details> <summary>🔎 Detected hardcoded secret in your pull request</summary> <br> | GitGuardian id | GitGuardian status | Secret | Commit | Filename | | | -------------- | ------------------ | ------------------------------ | ---------------- | --------------- | -------------------- | | [17619916](https://dashboard.gitguardian.com/workspace/312055/incidents/17619916?occurrence=229985582) | Triggered | Generic Password | d4ee01d33277d105cbd562ac801536ca5f399ddf | web-ng/config/dev.exs | [View secret](https://github.com/carverauto/serviceradar/commit/d4ee01d33277d105cbd562ac801536ca5f399ddf#diff-6cc2919e487233a5ea42f09327f73cd1ef2ef6250301c4641d43e0f4a8528131R63) | </details> <details> <summary>🛠 Guidelines to remediate hardcoded secrets</summary> <br> 1. Understand the implications of revoking this secret by investigating where it is used in your code. 2. Replace and store your secret safely. [Learn here](https://blog.gitguardian.com/secrets-api-management?utm_source=product&amp;utm_medium=GitHub_checks&amp;utm_campaign=check_run_comment) the best practices. 3. Revoke and [rotate this secret](https://docs.gitguardian.com/secrets-detection/secrets-detection-engine/detectors/generics/generic_password#revoke-the-secret?utm_source=product&amp;utm_medium=GitHub_checks&amp;utm_campaign=check_run_comment). 4. If possible, [rewrite git history](https://blog.gitguardian.com/rewriting-git-history-cheatsheet?utm_source=product&amp;utm_medium=GitHub_checks&amp;utm_campaign=check_run_comment). Rewriting git history is not a trivial act. You might completely break other contributing developers' workflow and you risk accidentally deleting legitimate data. To avoid such incidents in the future consider - following these [best practices](https://blog.gitguardian.com/secrets-api-management/?utm_source=product&amp;utm_medium=GitHub_checks&amp;utm_campaign=check_run_comment) for managing and storing secrets including API keys and other credentials - install [secret detection on pre-commit](https://docs.gitguardian.com/ggshield-docs/integrations/git-hooks/pre-commit?utm_source=product&amp;utm_medium=GitHub_checks&amp;utm_campaign=check_run_comment) to catch secret before it leaves your machine and ease remediation. </details> --- <sup>🦉 [GitGuardian](https://dashboard.gitguardian.com/auth/login/?utm_medium=checkruns&amp;utm_source=github&amp;utm_campaign=cr1) detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.<br/></sup>
qodo-code-review[bot] commented 2025-12-15 17:16:52 +00:00 (Migrated from github.com)
Author
Owner

Imported GitHub PR comment.

Original author: @qodo-code-review[bot]
Original URL: https://github.com/carverauto/serviceradar/pull/2139#issuecomment-3656764606
Original created: 2025-12-15T17:16:52Z

You are nearing your monthly Qodo Merge usage quota. For more information, please visit here.

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
Cypher validation bypass

Description: The read-only enforcement for user-supplied Cypher relies on a simple keyword token check
and may be bypassable with Cypher/AGE-specific syntax (e.g., comments, alternative
procedure/function invocations, or non-keyword mutation constructs), potentially allowing
unintended graph writes or other unsafe operations if the validator misses a mutation
pattern.
graph_cypher.rs [104-126]

Referred Code
fn ensure_read_only(raw: &str) -> Result<()> {
    let lower = raw.to_lowercase();
    if lower.contains(';') {
        return Err(ServiceError::InvalidRequest(
            "cypher queries must not contain ';'".into(),
        ));
    }

    for keyword in [
        "create", "merge", "set", "delete", "detach", "remove", "drop", "call",
    ] {
        if lower
            .split(|c: char| !c.is_ascii_alphanumeric() && c != '_')
            .any(|token| token == keyword)
        {
            return Err(ServiceError::InvalidRequest(format!(
                "cypher queries must be read-only (found '{keyword}')"
            )));
        }
    }



 ... (clipped 2 lines)
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status:
Missing audit context: The new query paths (downsample and graph_cypher) and translate_request return results/SQL
without any visible audit logging of who executed the action and the outcome, which may be
handled elsewhere but is not verifiable from this diff.

Referred Code
let results = if plan.downsample.is_some() {
    downsample::execute(&mut conn, &plan).await?
} else {
    match plan.entity {
        Entity::Devices => devices::execute(&mut conn, &plan).await?,
        Entity::DeviceUpdates => device_updates::execute(&mut conn, &plan).await?,
        Entity::DeviceGraph => device_graph::execute(&mut conn, &plan).await?,
        Entity::GraphCypher => graph_cypher::execute(&mut conn, &plan).await?,
        Entity::Events => events::execute(&mut conn, &plan).await?,
        Entity::Interfaces => interfaces::execute(&mut conn, &plan).await?,
        Entity::Logs => logs::execute(&mut conn, &plan).await?,
        Entity::Pollers => pollers::execute(&mut conn, &plan).await?,
        Entity::OtelMetrics => otel_metrics::execute(&mut conn, &plan).await?,
        Entity::RperfMetrics | Entity::TimeseriesMetrics | Entity::SnmpMetrics => {
            timeseries_metrics::execute(&mut conn, &plan).await?
        }
        Entity::CpuMetrics => cpu_metrics::execute(&mut conn, &plan).await?,
        Entity::MemoryMetrics => memory_metrics::execute(&mut conn, &plan).await?,
        Entity::DiskMetrics => disk_metrics::execute(&mut conn, &plan).await?,
        Entity::ProcessMetrics => process_metrics::execute(&mut conn, &plan).await?,
        Entity::Services => services::execute(&mut conn, &plan).await?,


 ... (clipped 4 lines)

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status:
User-facing errors: The new Cypher validation returns specific rejection reasons (e.g., which keyword was
found) that may be user-facing depending on API layers not shown, so exposure risk cannot
be confirmed from this diff alone.

Referred Code
fn ensure_read_only(raw: &str) -> Result<()> {
    let lower = raw.to_lowercase();
    if lower.contains(';') {
        return Err(ServiceError::InvalidRequest(
            "cypher queries must not contain ';'".into(),
        ));
    }

    for keyword in [
        "create", "merge", "set", "delete", "detach", "remove", "drop", "call",
    ] {
        if lower
            .split(|c: char| !c.is_ascii_alphanumeric() && c != '_')
            .any(|token| token == keyword)
        {
            return Err(ServiceError::InvalidRequest(format!(
                "cypher queries must be read-only (found '{keyword}')"
            )));
        }
    }



 ... (clipped 2 lines)

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status:
Logs may leak: The new diesel_sql helper logs error = ?err which could include SQL fragments or other
sensitive context depending on Diesel error content, requiring confirmation/redaction
review.

Referred Code
pub(super) fn diesel_sql<T>(query: &T) -> Result<String>
where
    T: diesel::query_builder::QueryFragment<diesel::pg::Pg>,
{
    use diesel::query_builder::QueryBuilder as _;

    let backend = diesel::pg::Pg::default();
    let mut query_builder = <diesel::pg::Pg as diesel::backend::Backend>::QueryBuilder::default();
    diesel::query_builder::QueryFragment::<diesel::pg::Pg>::to_sql(
        query,
        &mut query_builder,
        &backend,
    )
    .map_err(|err| {
        error!(error = ?err, "failed to serialize diesel SQL");
        ServiceError::Internal(anyhow::anyhow!("failed to serialize SQL"))
    })?;

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status:
Cypher safety scope: Although ensure_read_only blocks many mutation keywords and ;, confirming full protection
against Cypher/SQL injection and graph-side privilege escalation requires reviewing the
surrounding API/authz layers and the missing diffs.

Referred Code
fn extract_cypher(plan: &QueryPlan) -> Result<String> {
    let filter = plan
        .filters
        .iter()
        .find(|f| f.field == "cypher")
        .ok_or_else(|| {
            ServiceError::InvalidRequest("graph_cypher requires cypher:\"...\"".into())
        })?;

    if !matches!(filter.op, FilterOp::Eq) {
        return Err(ServiceError::InvalidRequest(
            "cypher filter only supports equality".into(),
        ));
    }

    let raw = filter.value.as_scalar()?.trim();
    if raw.is_empty() {
        return Err(ServiceError::InvalidRequest(
            "cypher query cannot be empty".into(),
        ));
    }


 ... (clipped 28 lines)

Learn more about managing compliance generic rules or creating your own custom rules

  • Update
Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
- Requires Further Human Verification
🏷️ - Compliance label
Imported GitHub PR comment. Original author: @qodo-code-review[bot] Original URL: https://github.com/carverauto/serviceradar/pull/2139#issuecomment-3656764606 Original created: 2025-12-15T17:16:52Z --- _You are nearing your monthly Qodo Merge usage quota. For more information, please visit [here](https://qodo-merge-docs.qodo.ai/installation/qodo_merge/#cloud-users)._ ## PR Compliance Guide 🔍 <!-- https://github.com/carverauto/serviceradar/commit/118baa2410e9aaed327bb1ba6652c21e430f33d3 --> Below is a summary of compliance checks for this PR:<br> <table><tbody><tr><td colspan='2'><strong>Security Compliance</strong></td></tr> <tr><td rowspan=1>⚪</td> <td><details><summary><strong>Cypher validation bypass </strong></summary><br> <b>Description:</b> The read-only enforcement for user-supplied Cypher relies on a simple keyword token check <br>and may be bypassable with Cypher/AGE-specific syntax (e.g., comments, alternative <br>procedure/function invocations, or non-keyword mutation constructs), potentially allowing <br>unintended graph writes or other unsafe operations if the validator misses a mutation <br>pattern.<br> <strong><a href='https://github.com/carverauto/serviceradar/pull/2139/files#diff-378b0ec80e1b1221c347160a0dac36c82d082fa06766d47d18cf069220166309R104-R126'>graph_cypher.rs [104-126]</a></strong><br> <details open><summary>Referred Code</summary> ```rust fn ensure_read_only(raw: &str) -> Result<()> { let lower = raw.to_lowercase(); if lower.contains(';') { return Err(ServiceError::InvalidRequest( "cypher queries must not contain ';'".into(), )); } for keyword in [ "create", "merge", "set", "delete", "detach", "remove", "drop", "call", ] { if lower .split(|c: char| !c.is_ascii_alphanumeric() && c != '_') .any(|token| token == keyword) { return Err(ServiceError::InvalidRequest(format!( "cypher queries must be read-only (found '{keyword}')" ))); } } ... (clipped 2 lines) ``` </details></details></td></tr> <tr><td colspan='2'><strong>Ticket Compliance</strong></td></tr> <tr><td>⚪</td><td><details><summary>🎫 <strong>No ticket provided </strong></summary> - [ ] Create ticket/issue <!-- /create_ticket --create_ticket=true --> </details></td></tr> <tr><td colspan='2'><strong>Codebase Duplication Compliance</strong></td></tr> <tr><td>⚪</td><td><details><summary><strong>Codebase context is not defined </strong></summary> Follow the <a href='https://qodo-merge-docs.qodo.ai/core-abilities/rag_context_enrichment/'>guide</a> to enable codebase context checks. </details></td></tr> <tr><td colspan='2'><strong>Custom Compliance</strong></td></tr> <tr><td rowspan=2>🟢</td><td> <details><summary><strong>Generic: Meaningful Naming and Self-Documenting Code</strong></summary><br> **Objective:** Ensure all identifiers clearly express their purpose and intent, making code <br>self-documenting<br> **Status:** Passed<br> > Learn more about managing compliance <a href='https://qodo-merge-docs.qodo.ai/tools/compliance/#configuration-options'>generic rules</a> or creating your own <a href='https://qodo-merge-docs.qodo.ai/tools/compliance/#custom-compliance'>custom rules</a> </details></td></tr> <tr><td> <details><summary><strong>Generic: Robust Error Handling and Edge Case Management</strong></summary><br> **Objective:** Ensure comprehensive error handling that provides meaningful context and graceful <br>degradation<br> **Status:** Passed<br> > Learn more about managing compliance <a href='https://qodo-merge-docs.qodo.ai/tools/compliance/#configuration-options'>generic rules</a> or creating your own <a href='https://qodo-merge-docs.qodo.ai/tools/compliance/#custom-compliance'>custom rules</a> </details></td></tr> <tr><td rowspan=4>⚪</td> <td><details> <summary><strong>Generic: Comprehensive Audit Trails</strong></summary><br> **Objective:** To create a detailed and reliable record of critical system actions for security analysis <br>and compliance.<br> **Status:** <br><a href='https://github.com/carverauto/serviceradar/pull/2139/files#diff-393e3fa3d0e41741834cd7cd398a06111ab7b141ae6caca7a5dcc0e036172491R169-R193'><strong>Missing audit context</strong></a>: The new query paths (<code>downsample</code> and <code>graph_cypher</code>) and <code>translate_request</code> return results/SQL <br>without any visible audit logging of who executed the action and the outcome, which may be <br>handled elsewhere but is not verifiable from this diff.<br> <details open><summary>Referred Code</summary> ```rust let results = if plan.downsample.is_some() { downsample::execute(&mut conn, &plan).await? } else { match plan.entity { Entity::Devices => devices::execute(&mut conn, &plan).await?, Entity::DeviceUpdates => device_updates::execute(&mut conn, &plan).await?, Entity::DeviceGraph => device_graph::execute(&mut conn, &plan).await?, Entity::GraphCypher => graph_cypher::execute(&mut conn, &plan).await?, Entity::Events => events::execute(&mut conn, &plan).await?, Entity::Interfaces => interfaces::execute(&mut conn, &plan).await?, Entity::Logs => logs::execute(&mut conn, &plan).await?, Entity::Pollers => pollers::execute(&mut conn, &plan).await?, Entity::OtelMetrics => otel_metrics::execute(&mut conn, &plan).await?, Entity::RperfMetrics | Entity::TimeseriesMetrics | Entity::SnmpMetrics => { timeseries_metrics::execute(&mut conn, &plan).await? } Entity::CpuMetrics => cpu_metrics::execute(&mut conn, &plan).await?, Entity::MemoryMetrics => memory_metrics::execute(&mut conn, &plan).await?, Entity::DiskMetrics => disk_metrics::execute(&mut conn, &plan).await?, Entity::ProcessMetrics => process_metrics::execute(&mut conn, &plan).await?, Entity::Services => services::execute(&mut conn, &plan).await?, ... (clipped 4 lines) ``` </details> > Learn more about managing compliance <a href='https://qodo-merge-docs.qodo.ai/tools/compliance/#configuration-options'>generic rules</a> or creating your own <a href='https://qodo-merge-docs.qodo.ai/tools/compliance/#custom-compliance'>custom rules</a> </details></td></tr> <tr><td><details> <summary><strong>Generic: Secure Error Handling</strong></summary><br> **Objective:** To prevent the leakage of sensitive system information through error messages while <br>providing sufficient detail for internal debugging.<br> **Status:** <br><a href='https://github.com/carverauto/serviceradar/pull/2139/files#diff-378b0ec80e1b1221c347160a0dac36c82d082fa06766d47d18cf069220166309R104-R126'><strong>User-facing errors</strong></a>: The new Cypher validation returns specific rejection reasons (e.g., which keyword was <br>found) that may be user-facing depending on API layers not shown, so exposure risk cannot <br>be confirmed from this diff alone.<br> <details open><summary>Referred Code</summary> ```rust fn ensure_read_only(raw: &str) -> Result<()> { let lower = raw.to_lowercase(); if lower.contains(';') { return Err(ServiceError::InvalidRequest( "cypher queries must not contain ';'".into(), )); } for keyword in [ "create", "merge", "set", "delete", "detach", "remove", "drop", "call", ] { if lower .split(|c: char| !c.is_ascii_alphanumeric() && c != '_') .any(|token| token == keyword) { return Err(ServiceError::InvalidRequest(format!( "cypher queries must be read-only (found '{keyword}')" ))); } } ... (clipped 2 lines) ``` </details> > Learn more about managing compliance <a href='https://qodo-merge-docs.qodo.ai/tools/compliance/#configuration-options'>generic rules</a> or creating your own <a href='https://qodo-merge-docs.qodo.ai/tools/compliance/#custom-compliance'>custom rules</a> </details></td></tr> <tr><td><details> <summary><strong>Generic: Secure Logging Practices</strong></summary><br> **Objective:** To ensure logs are useful for debugging and auditing without exposing sensitive <br>information like PII, PHI, or cardholder data.<br> **Status:** <br><a href='https://github.com/carverauto/serviceradar/pull/2139/files#diff-393e3fa3d0e41741834cd7cd398a06111ab7b141ae6caca7a5dcc0e036172491R324-R340'><strong>Logs may leak</strong></a>: The new <code>diesel_sql</code> helper logs <code>error = ?err</code> which could include SQL fragments or other <br>sensitive context depending on Diesel error content, requiring confirmation/redaction <br>review.<br> <details open><summary>Referred Code</summary> ```rust pub(super) fn diesel_sql<T>(query: &T) -> Result<String> where T: diesel::query_builder::QueryFragment<diesel::pg::Pg>, { use diesel::query_builder::QueryBuilder as _; let backend = diesel::pg::Pg::default(); let mut query_builder = <diesel::pg::Pg as diesel::backend::Backend>::QueryBuilder::default(); diesel::query_builder::QueryFragment::<diesel::pg::Pg>::to_sql( query, &mut query_builder, &backend, ) .map_err(|err| { error!(error = ?err, "failed to serialize diesel SQL"); ServiceError::Internal(anyhow::anyhow!("failed to serialize SQL")) })?; ``` </details> > Learn more about managing compliance <a href='https://qodo-merge-docs.qodo.ai/tools/compliance/#configuration-options'>generic rules</a> or creating your own <a href='https://qodo-merge-docs.qodo.ai/tools/compliance/#custom-compliance'>custom rules</a> </details></td></tr> <tr><td><details> <summary><strong>Generic: Security-First Input Validation and Data Handling</strong></summary><br> **Objective:** Ensure all data inputs are validated, sanitized, and handled securely to prevent <br>vulnerabilities<br> **Status:** <br><a href='https://github.com/carverauto/serviceradar/pull/2139/files#diff-378b0ec80e1b1221c347160a0dac36c82d082fa06766d47d18cf069220166309R78-R126'><strong>Cypher safety scope</strong></a>: Although <code>ensure_read_only</code> blocks many mutation keywords and <code>;</code>, confirming full protection <br>against Cypher/SQL injection and graph-side privilege escalation requires reviewing the <br>surrounding API/authz layers and the missing diffs.<br> <details open><summary>Referred Code</summary> ```rust fn extract_cypher(plan: &QueryPlan) -> Result<String> { let filter = plan .filters .iter() .find(|f| f.field == "cypher") .ok_or_else(|| { ServiceError::InvalidRequest("graph_cypher requires cypher:\"...\"".into()) })?; if !matches!(filter.op, FilterOp::Eq) { return Err(ServiceError::InvalidRequest( "cypher filter only supports equality".into(), )); } let raw = filter.value.as_scalar()?.trim(); if raw.is_empty() { return Err(ServiceError::InvalidRequest( "cypher query cannot be empty".into(), )); } ... (clipped 28 lines) ``` </details> > Learn more about managing compliance <a href='https://qodo-merge-docs.qodo.ai/tools/compliance/#configuration-options'>generic rules</a> or creating your own <a href='https://qodo-merge-docs.qodo.ai/tools/compliance/#custom-compliance'>custom rules</a> </details></td></tr> <tr><td align="center" colspan="2"> - [ ] Update <!-- /compliance --update_compliance=true --> </td></tr></tbody></table> <details><summary>Compliance status legend</summary> 🟢 - Fully Compliant<br> 🟡 - Partial Compliant<br> 🔴 - Not Compliant<br> ⚪ - Requires Further Human Verification<br> 🏷️ - Compliance label<br> </details>
qodo-code-review[bot] commented 2025-12-15 17:18:12 +00:00 (Migrated from github.com)
Author
Owner

Imported GitHub PR comment.

Original author: @qodo-code-review[bot]
Original URL: https://github.com/carverauto/serviceradar/pull/2139#issuecomment-3656769847
Original created: 2025-12-15T17:18:12Z

You are nearing your monthly Qodo Merge usage quota. For more information, please visit here.

PR Code Suggestions

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
General
Manage dependencies with a package manager

Instead of committing the daisyui.js file directly, manage it as a dependency
using a package manager like npm or yarn. This improves maintainability,
security, and reduces repository size.

web-ng/assets/vendor/daisyui.js [1-24]

-/** 🌼
- *  @license MIT
- *  daisyUI bundle
- *  https://daisyui.com/
- */
+/*
+  This file should be removed.
+  To add DaisyUI to the project, follow these steps:
+  1. Ensure you have Node.js and a package manager (npm/yarn) installed.
+  2. In your assets directory, run: `npm install daisyui`
+  3. Configure your `tailwind.config.js` to include DaisyUI as a plugin:
+     module.exports = {
+       //...
+       plugins: [require('daisyui')],
+     }
+  4. Import the base Tailwind styles in your main CSS file.
+*/
 
-var __defProp = Object.defineProperty;
-var __getOwnPropNames = Object.getOwnPropertyNames;
-var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
-var __hasOwnProp = Object.prototype.hasOwnProperty;
-var __moduleCache = /* @__PURE__ */ new WeakMap;
-var __toCommonJS = (from) => {
-  var entry = __moduleCache.get(from), desc;
-  if (entry)
-    return entry;
-  entry = __defProp({}, "__esModule", { value: true });
-  if (from && typeof from === "object" || typeof from === "function")
-    __getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, {
-      get: () => from[key],
-      enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
-    }));
-  __moduleCache.set(from, entry);
-  return entry;
-};
-...
-

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 9

__

Why: The suggestion correctly identifies that vendoring a large third-party library is an anti-pattern and proposes the standard best practice of using a package manager, which significantly improves project maintainability and security.

High
Improve TLS error message clarity

Improve the TLS client configuration error message to reference the client
certificate and key paths instead of the root certificate path, making it more
accurate for debugging.

rust/srql/src/db.rs [125-144]

 fn build_client_config(
     root_store: RootCertStore,
-    root_cert: &str,
+    _root_cert: &str,
     client_cert: Option<&str>,
     client_key: Option<&str>,
 ) -> Result<ClientConfig> {
     let builder = ClientConfig::builder().with_root_certificates(root_store);
 
     match (client_cert, client_key) {
         (None, None) => Ok(builder.with_no_client_auth()),
-        (Some(cert), Some(key)) => {
-            let certs = load_client_certs(cert)?;
-            let key = load_client_key(key)?;
-            builder
-                .with_client_auth_cert(certs, key)
-                .with_context(|| format!("failed to build client TLS config for {root_cert}"))
+        (Some(cert_path), Some(key_path)) => {
+            let certs = load_client_certs(cert_path)?;
+            let key = load_client_key(key_path)?;
+            builder.with_client_auth_cert(certs, key).with_context(|| {
+                format!("failed to build client TLS config using cert '{cert_path}' and key '{key_path}'")
+            })
         }
         _ => anyhow::bail!("PGSSLCERT and PGSSLKEY must both be set (or neither)"),
     }
 }
  • Apply / Chat
Suggestion importance[1-10]: 4

__

Why: The suggestion proposes a minor improvement to an error message for better debuggability. While the change is valid and makes the error context more precise, it has a low impact on functionality or correctness.

Low
Use a valid dummy database URL

Replace the hardcoded dummy database URL in the translate NIF with a
syntactically valid PostgreSQL connection string to improve robustness against
future changes in configuration parsing.

web-ng/native/srql_nif/src/lib.rs [32]

-let config = srql::config::AppConfig::embedded("postgres://unused/db".to_string());
+let config = srql::config::AppConfig::embedded(
+    "postgres://localhost:5432/serviceradar".to_string(),
+);

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 3

__

Why: The suggestion correctly points out that a hardcoded, invalid URL could be brittle. While the current code works, using a syntactically valid dummy URL is a good practice for future-proofing, though it has a low immediate impact.

Low
Security
Improve Cypher query security validation

Improve the Cypher query security validation to prevent false positives by using
a regular expression that correctly ignores keywords within string literals and
comments.

rust/srql/src/query/graph_cypher.rs [104-126]

 fn ensure_read_only(raw: &str) -> Result<()> {
     let lower = raw.to_lowercase();
     if lower.contains(';') {
         return Err(ServiceError::InvalidRequest(
             "cypher queries must not contain ';'".into(),
         ));
     }
 
-    for keyword in [
-        "create", "merge", "set", "delete", "detach", "remove", "drop", "call",
-    ] {
-        if lower
-            .split(|c: char| !c.is_ascii_alphanumeric() && c != '_')
-            .any(|token| token == keyword)
-        {
+    // This regex finds write-operation keywords that are not inside single or double quotes,
+    // and not inside single-line or multi-line comments.
+    let re = regex::Regex::new(r#"(?is)(?P<comment_sl>//.*)|(?P<comment_ml>/\*.*?\*/)|(?P<string_sq>'.*?')|(?P<string_dq>".*?")|(?P<keyword>\b(create|merge|set|delete|detach|remove|drop|call)\b)"#).unwrap();
+
+    for caps in re.captures_iter(&lower) {
+        if let Some(keyword_match) = caps.name("keyword") {
             return Err(ServiceError::InvalidRequest(format!(
-                "cypher queries must be read-only (found '{keyword}')"
+                "cypher queries must be read-only (found '{}')",
+                keyword_match.as_str()
             )));
         }
     }
 
     Ok(())
 }
  • Apply / Chat
Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies that the current validation logic can lead to false positives by incorrectly rejecting queries with forbidden keywords inside string literals. This is a security-related improvement that enhances the correctness and robustness of the Cypher query validation.

Medium
Possible issue
Improve duration parsing logic

Refactor the parse_bucket_seconds function to more robustly separate the numeric
value from the unit suffix, correctly handling inputs with missing or invalid
units.

rust/srql/src/parser.rs [245-285]

 fn parse_bucket_seconds(raw: &str) -> Result<i64> {
     let raw = raw.trim();
     if raw.is_empty() {
         return Err(ServiceError::InvalidRequest(
             "bucket requires a duration like 5m, 1h".into(),
         ));
     }
 
     let raw = raw.to_lowercase();
-    let (number_part, unit_part) = raw.split_at(raw.len().saturating_sub(1));
+    let split_at = raw.find(|c: char| !c.is_ascii_digit()).unwrap_or(raw.len());
+    let (number_part, unit_part) = raw.split_at(split_at);
+
+    if number_part.is_empty() {
+        return Err(ServiceError::InvalidRequest("bucket duration must have a numeric part".into()));
+    }
+
     let value = number_part
         .parse::<i64>()
         .map_err(|_| ServiceError::InvalidRequest("bucket duration must be an integer".into()))?;
 
     if value <= 0 {
         return Err(ServiceError::InvalidRequest(
             "bucket duration must be positive".into(),
         ));
     }
 
     let multiplier = match unit_part {
         "s" => 1,
         "m" => 60,
         "h" => 60 * 60,
         "d" => 24 * 60 * 60,
+        "" => return Err(ServiceError::InvalidRequest(
+            "bucket duration requires a unit suffix (s|m|h|d)".into(),
+        )),
         _ => {
             return Err(ServiceError::InvalidRequest(
                 "bucket supports only s|m|h|d suffixes".into(),
             ))
         }
     };
 
     let seconds = value.saturating_mul(multiplier);
     if seconds <= 0 || seconds > MAX_DOWNSAMPLE_BUCKET_SECS {
         return Err(ServiceError::InvalidRequest(format!(
             "bucket duration must be between 1s and {}d",
             MAX_DOWNSAMPLE_BUCKET_SECS / (24 * 60 * 60)
         )));
     }
     Ok(seconds)
 }
  • Apply / Chat
Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies a flaw in the parsing logic for bucket durations, where inputs without a unit suffix are mishandled. The proposed change makes the parsing more robust and provides clearer error messages for invalid or missing units, improving the function's correctness.

Medium
Fix incorrect filter parameter collection

Use the collect_text_params helper function to handle filter parameter
collection for text fields, ensuring correct behavior for various operators and
preventing panics with list-based filters.

rust/srql/src/query/devices.rs [249-252]

 "poller_id" | "agent_id" | "device_type" | "service_type" | "service_status" => {
-    params.push(BindParam::Text(filter.value.as_scalar()?.to_string()));
-    Ok(())
+    collect_text_params(params, filter, false)
 }
  • Apply / Chat
Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies that the current implementation would panic on list-based filters and proposes using the collect_text_params helper function, which provides proper error handling and improves code reuse.

Medium
Improve boolean parsing for filters

Refactor the boolean parsing for available and is_available filters to use the
parse_bool utility for better consistency and to support more truthy values.

rust/srql/src/query/device_updates.rs [198-203]

 "available" | "is_available" => {
-    let value = filter.value.as_scalar()?.to_lowercase();
-    let bool_val = value == "true" || value == "1";
-    params.push(BindParam::Bool(bool_val));
+    params.push(BindParam::Bool(parse_bool(filter.value.as_scalar()?)?));
     Ok(())
 }
  • Apply / Chat
Suggestion importance[1-10]: 6

__

Why: The suggestion correctly identifies inconsistent and limited boolean parsing, and its proposed change to use the parse_bool function aligns with other parts of the codebase, improving robustness and maintainability.

Low
  • Update
Imported GitHub PR comment. Original author: @qodo-code-review[bot] Original URL: https://github.com/carverauto/serviceradar/pull/2139#issuecomment-3656769847 Original created: 2025-12-15T17:18:12Z --- _You are nearing your monthly Qodo Merge usage quota. For more information, please visit [here](https://qodo-merge-docs.qodo.ai/installation/qodo_merge/#cloud-users)._ ## PR Code Suggestions ✨ <!-- 118baa2 --> Explore these optional code suggestions: <table><thead><tr><td><strong>Category</strong></td><td align=left><strong>Suggestion&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </strong></td><td align=center><strong>Impact</strong></td></tr><tbody><tr><td rowspan=3>General</td> <td> <details><summary>Manage dependencies with a package manager</summary> ___ **Instead of committing the <code>daisyui.js</code> file directly, manage it as a dependency <br>using a package manager like <code>npm</code> or <code>yarn</code>. This improves maintainability, <br>security, and reduces repository size.** [web-ng/assets/vendor/daisyui.js [1-24]](https://github.com/carverauto/serviceradar/pull/2139/files#diff-1b1d093f688f9b2cd8fd59953adaa20204e62bc26aeda7146579735ff47e7124R1-R24) ```diff -/** 🌼 - * @license MIT - * daisyUI bundle - * https://daisyui.com/ - */ +/* + This file should be removed. + To add DaisyUI to the project, follow these steps: + 1. Ensure you have Node.js and a package manager (npm/yarn) installed. + 2. In your assets directory, run: `npm install daisyui` + 3. Configure your `tailwind.config.js` to include DaisyUI as a plugin: + module.exports = { + //... + plugins: [require('daisyui')], + } + 4. Import the base Tailwind styles in your main CSS file. +*/ -var __defProp = Object.defineProperty; -var __getOwnPropNames = Object.getOwnPropertyNames; -var __getOwnPropDesc = Object.getOwnPropertyDescriptor; -var __hasOwnProp = Object.prototype.hasOwnProperty; -var __moduleCache = /* @__PURE__ */ new WeakMap; -var __toCommonJS = (from) => { - var entry = __moduleCache.get(from), desc; - if (entry) - return entry; - entry = __defProp({}, "__esModule", { value: true }); - if (from && typeof from === "object" || typeof from === "function") - __getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, { - get: () => from[key], - enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable - })); - __moduleCache.set(from, entry); - return entry; -}; -... - ``` `[To ensure code accuracy, apply this suggestion manually]` <details><summary>Suggestion importance[1-10]: 9</summary> __ Why: The suggestion correctly identifies that vendoring a large third-party library is an anti-pattern and proposes the standard best practice of using a package manager, which significantly improves project maintainability and security. </details></details></td><td align=center>High </td></tr><tr><td> <details><summary>Improve TLS error message clarity</summary> ___ **Improve the TLS client configuration error message to reference the client <br>certificate and key paths instead of the root certificate path, making it more <br>accurate for debugging.** [rust/srql/src/db.rs [125-144]](https://github.com/carverauto/serviceradar/pull/2139/files#diff-8d5a0c48024aa12a3e06de065ca71e940d2b5007fbc34ef87471b90d7937891cR125-R144) ```diff fn build_client_config( root_store: RootCertStore, - root_cert: &str, + _root_cert: &str, client_cert: Option<&str>, client_key: Option<&str>, ) -> Result<ClientConfig> { let builder = ClientConfig::builder().with_root_certificates(root_store); match (client_cert, client_key) { (None, None) => Ok(builder.with_no_client_auth()), - (Some(cert), Some(key)) => { - let certs = load_client_certs(cert)?; - let key = load_client_key(key)?; - builder - .with_client_auth_cert(certs, key) - .with_context(|| format!("failed to build client TLS config for {root_cert}")) + (Some(cert_path), Some(key_path)) => { + let certs = load_client_certs(cert_path)?; + let key = load_client_key(key_path)?; + builder.with_client_auth_cert(certs, key).with_context(|| { + format!("failed to build client TLS config using cert '{cert_path}' and key '{key_path}'") + }) } _ => anyhow::bail!("PGSSLCERT and PGSSLKEY must both be set (or neither)"), } } ``` - [ ] **Apply / Chat** <!-- /improve --apply_suggestion=1 --> <details><summary>Suggestion importance[1-10]: 4</summary> __ Why: The suggestion proposes a minor improvement to an error message for better debuggability. While the change is valid and makes the error context more precise, it has a low impact on functionality or correctness. </details></details></td><td align=center>Low </td></tr><tr><td> <details><summary>Use a valid dummy database URL</summary> ___ **Replace the hardcoded dummy database URL in the <code>translate</code> NIF with a <br>syntactically valid PostgreSQL connection string to improve robustness against <br>future changes in configuration parsing.** [web-ng/native/srql_nif/src/lib.rs [32]](https://github.com/carverauto/serviceradar/pull/2139/files#diff-74a225cdf31e64db49fb42c0419c524068150b07528e8d722a75e3ec8034c297R32-R32) ```diff -let config = srql::config::AppConfig::embedded("postgres://unused/db".to_string()); +let config = srql::config::AppConfig::embedded( + "postgres://localhost:5432/serviceradar".to_string(), +); ``` `[To ensure code accuracy, apply this suggestion manually]` <details><summary>Suggestion importance[1-10]: 3</summary> __ Why: The suggestion correctly points out that a hardcoded, invalid URL could be brittle. While the current code works, using a syntactically valid dummy URL is a good practice for future-proofing, though it has a low immediate impact. </details></details></td><td align=center>Low </td></tr><tr><td rowspan=1>Security</td> <td> <details><summary>Improve Cypher query security validation</summary> ___ **Improve the Cypher query security validation to prevent false positives by using <br>a regular expression that correctly ignores keywords within string literals and <br>comments.** [rust/srql/src/query/graph_cypher.rs [104-126]](https://github.com/carverauto/serviceradar/pull/2139/files#diff-378b0ec80e1b1221c347160a0dac36c82d082fa06766d47d18cf069220166309R104-R126) ```diff fn ensure_read_only(raw: &str) -> Result<()> { let lower = raw.to_lowercase(); if lower.contains(';') { return Err(ServiceError::InvalidRequest( "cypher queries must not contain ';'".into(), )); } - for keyword in [ - "create", "merge", "set", "delete", "detach", "remove", "drop", "call", - ] { - if lower - .split(|c: char| !c.is_ascii_alphanumeric() && c != '_') - .any(|token| token == keyword) - { + // This regex finds write-operation keywords that are not inside single or double quotes, + // and not inside single-line or multi-line comments. + let re = regex::Regex::new(r#"(?is)(?P<comment_sl>//.*)|(?P<comment_ml>/\*.*?\*/)|(?P<string_sq>'.*?')|(?P<string_dq>".*?")|(?P<keyword>\b(create|merge|set|delete|detach|remove|drop|call)\b)"#).unwrap(); + + for caps in re.captures_iter(&lower) { + if let Some(keyword_match) = caps.name("keyword") { return Err(ServiceError::InvalidRequest(format!( - "cypher queries must be read-only (found '{keyword}')" + "cypher queries must be read-only (found '{}')", + keyword_match.as_str() ))); } } Ok(()) } ``` - [ ] **Apply / Chat** <!-- /improve --apply_suggestion=3 --> <details><summary>Suggestion importance[1-10]: 8</summary> __ Why: The suggestion correctly identifies that the current validation logic can lead to false positives by incorrectly rejecting queries with forbidden keywords inside string literals. This is a security-related improvement that enhances the correctness and robustness of the Cypher query validation. </details></details></td><td align=center>Medium </td></tr><tr><td rowspan=3>Possible issue</td> <td> <details><summary>Improve duration parsing logic</summary> ___ **Refactor the <code>parse_bucket_seconds</code> function to more robustly separate the numeric <br>value from the unit suffix, correctly handling inputs with missing or invalid <br>units.** [rust/srql/src/parser.rs [245-285]](https://github.com/carverauto/serviceradar/pull/2139/files#diff-b2edf55d1721185349ecddb2f4eacc42e0dfcae19b6c2bc638602f187da67e66R245-R285) ```diff fn parse_bucket_seconds(raw: &str) -> Result<i64> { let raw = raw.trim(); if raw.is_empty() { return Err(ServiceError::InvalidRequest( "bucket requires a duration like 5m, 1h".into(), )); } let raw = raw.to_lowercase(); - let (number_part, unit_part) = raw.split_at(raw.len().saturating_sub(1)); + let split_at = raw.find(|c: char| !c.is_ascii_digit()).unwrap_or(raw.len()); + let (number_part, unit_part) = raw.split_at(split_at); + + if number_part.is_empty() { + return Err(ServiceError::InvalidRequest("bucket duration must have a numeric part".into())); + } + let value = number_part .parse::<i64>() .map_err(|_| ServiceError::InvalidRequest("bucket duration must be an integer".into()))?; if value <= 0 { return Err(ServiceError::InvalidRequest( "bucket duration must be positive".into(), )); } let multiplier = match unit_part { "s" => 1, "m" => 60, "h" => 60 * 60, "d" => 24 * 60 * 60, + "" => return Err(ServiceError::InvalidRequest( + "bucket duration requires a unit suffix (s|m|h|d)".into(), + )), _ => { return Err(ServiceError::InvalidRequest( "bucket supports only s|m|h|d suffixes".into(), )) } }; let seconds = value.saturating_mul(multiplier); if seconds <= 0 || seconds > MAX_DOWNSAMPLE_BUCKET_SECS { return Err(ServiceError::InvalidRequest(format!( "bucket duration must be between 1s and {}d", MAX_DOWNSAMPLE_BUCKET_SECS / (24 * 60 * 60) ))); } Ok(seconds) } ``` - [ ] **Apply / Chat** <!-- /improve --apply_suggestion=4 --> <details><summary>Suggestion importance[1-10]: 7</summary> __ Why: The suggestion correctly identifies a flaw in the parsing logic for bucket durations, where inputs without a unit suffix are mishandled. The proposed change makes the parsing more robust and provides clearer error messages for invalid or missing units, improving the function's correctness. </details></details></td><td align=center>Medium </td></tr><tr><td> <details><summary>Fix incorrect filter parameter collection</summary> ___ **Use the <code>collect_text_params</code> helper function to handle filter parameter <br>collection for text fields, ensuring correct behavior for various operators and <br>preventing panics with list-based filters.** [rust/srql/src/query/devices.rs [249-252]](https://github.com/carverauto/serviceradar/pull/2139/files#diff-3202f22fff6863ed7848a129c49e2323322462b379d896d3fca2e59aa6f7b4c5R249-R252) ```diff "poller_id" | "agent_id" | "device_type" | "service_type" | "service_status" => { - params.push(BindParam::Text(filter.value.as_scalar()?.to_string())); - Ok(()) + collect_text_params(params, filter, false) } ``` - [ ] **Apply / Chat** <!-- /improve --apply_suggestion=5 --> <details><summary>Suggestion importance[1-10]: 7</summary> __ Why: The suggestion correctly identifies that the current implementation would panic on list-based filters and proposes using the `collect_text_params` helper function, which provides proper error handling and improves code reuse. </details></details></td><td align=center>Medium </td></tr><tr><td> <details><summary>Improve boolean parsing for filters</summary> ___ **Refactor the boolean parsing for <code>available</code> and <code>is_available</code> filters to use the <br><code>parse_bool</code> utility for better consistency and to support more truthy values.** [rust/srql/src/query/device_updates.rs [198-203]](https://github.com/carverauto/serviceradar/pull/2139/files#diff-77b99f0ef409a6a3e4ecc65800ae841231a82b27a64e8dc1d4667db6698a539dR198-R203) ```diff "available" | "is_available" => { - let value = filter.value.as_scalar()?.to_lowercase(); - let bool_val = value == "true" || value == "1"; - params.push(BindParam::Bool(bool_val)); + params.push(BindParam::Bool(parse_bool(filter.value.as_scalar()?)?)); Ok(()) } ``` - [ ] **Apply / Chat** <!-- /improve --apply_suggestion=6 --> <details><summary>Suggestion importance[1-10]: 6</summary> __ Why: The suggestion correctly identifies inconsistent and limited boolean parsing, and its proposed change to use the `parse_bool` function aligns with other parts of the codebase, improving robustness and maintainability. </details></details></td><td align=center>Low </td></tr> <tr><td align="center" colspan="2"> - [ ] Update <!-- /improve_multi --more_suggestions=true --> </td><td></td></tr></tbody></table>
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
carverauto/serviceradar!2578
No description provided.