Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .coderabbit.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ reviews:
- "!**/obj/**"
- "!**/Tests/**"
- "!**/.claude/**"
- "!**/.agent/**"
- "!**/*.md"
path_instructions: []
abort_on_close: true
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/dotnet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,8 @@ jobs:
if (pr) {
const body = (pr.body || '')
.replace(/##\s*Summary by CodeRabbit[\s\S]*/i, '')
.replace(/Pull Request Description/gi, 'Summary')
.replace(/^(#+\s*)(Pull Request Description|PR Description)\s*$/gim, '$1Summary')
.replace(/Pull Request Description|PR Description/gi, 'Summary')
.trim() || 'No release notes provided.';
fs.writeFileSync('release_notes.md', body);
} else {
Expand Down
7 changes: 7 additions & 0 deletions Core/Resgrid.Config/SecurityConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@ public static class SecurityConfig
/// </summary>
public static string SystemApiKey = "";

/// <summary>
/// Shared secret required on the internal scheduled-report endpoint (User/Reports/InternalRunReport).
/// The report-delivery worker supplies this value as the "key" query parameter; requests without a
/// matching key are rejected. Must be set (non-empty) for scheduled report delivery to function.
/// </summary>
public static string InternalReportsToken = "";

// ── Encryption ───────────────────────────────────────────────────────────────

/// <summary>AES-256 master key used by IEncryptionService for system-wide encryption.</summary>
Expand Down
6 changes: 6 additions & 0 deletions Core/Resgrid.Config/ServiceBusConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ public static class ServiceBusConfig
public static string PaymentQueueName = "paymenttest";
public static string AuditQueueName = "audittest";
public static string UnitLoactionQueueName = "unitlocationtest";
public static string UnitLocationQueueV2Name = "unitlocation-v2test";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kody code-review Kody Rules high

Implicit immutability violation in UnitLocationQueueV2Name where a compile-time string literal is declared as public static string. Rule [115] requires immutable compile-time constants to be marked const. Change the declaration to public const string.

Kody rule violation: Use `readonly` or `const` for Immutable Data

Prompt for LLM

File Core/Resgrid.Config/ServiceBusConfig.cs:

Line 18:

Implicit immutability violation in `UnitLocationQueueV2Name` where a compile-time string literal is declared as `public static string`. Rule [115] requires immutable compile-time constants to be marked `const`. Change the declaration to `public const string`.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

public static string UnitLocationRetryQueueV2Name = "unitlocation-v2test.retry";
public static string UnitLocationDeadQueueV2Name = "unitlocation-v2test.dead";
public static string PersonnelLoactionQueueName = "personnellocationtest";
public static string SecurityRefreshQueueName = "securityrefreshtest";
public static string WorkflowQueueName = "workflowqueuetest";
Expand All @@ -29,6 +32,9 @@ public static class ServiceBusConfig
public static string PaymentQueueName = "payment";
public static string AuditQueueName = "audit";
public static string UnitLoactionQueueName = "unitlocation";
public static string UnitLocationQueueV2Name = "unitlocation-v2";
public static string UnitLocationRetryQueueV2Name = "unitlocation-v2.retry";
public static string UnitLocationDeadQueueV2Name = "unitlocation-v2.dead";
public static string PersonnelLoactionQueueName = "personnellocation";
public static string SecurityRefreshQueueName = "securityrefresh";
public static string WorkflowQueueName = "workflowqueue";
Expand Down
46 changes: 46 additions & 0 deletions Core/Resgrid.Config/UnitTrackingConfig.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
namespace Resgrid.Config
{
public static class UnitTrackingConfig
{
public static bool Enabled = false;
public static bool HttpsIngressEnabled = false;
public static bool NativeGatewayEnabled = false;
public static string CredentialPepper = "";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kody code-review Kody Rules high

Mutable static fields, such as CredentialPepper, risk accidental runtime modification. Declare compile-time constant values using const or static readonly to ensure immutability and thread safety.

Kody rule violation: Use `readonly` or `const` for Immutable Data

Prompt for LLM

File Core/Resgrid.Config/UnitTrackingConfig.cs:

Line 8:

Mutable static fields, such as `CredentialPepper`, risk accidental runtime modification. Declare compile-time constant values using `const` or `static readonly` to ensure immutability and thread safety.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

public static string PublicHttpsBaseUrl = "";
public static int MaxRequestBytes = 262144;
public static int MaxBatchPositions = 100;
public static int MaxJsonDepth = 16;
public static int MaxFutureSkewSeconds = 300;
public static int DefaultLocationRetentionDays = 90;
public static int MinimumLocationRetentionDays = 1;
public static int MaximumLocationRetentionDays = 3650;
public static int PerDeviceRequestsPerMinute = 120;
public static int PerDeviceRecordsPerMinute = 1200;
public static int UnknownEndpointRequestsPerMinute = 30;
public static int CredentialCacheSeconds = 60;
public static int CredentialRotationOverlapHours = 24;
public static int DeviceMappingCacheSeconds = 300;
public static int UnknownDeviceCacheSeconds = 30;
public static int QueueMessageTtlSeconds = 86400;
public static int QueuePublishTimeoutSeconds = 5;
public static int UnitLocationQueuePrefetchCount = 25;
public static int UnitLocationRetryDelaySeconds = 30;
public static int UnitLocationMaxRetryAttempts = 3;
public static int TcpIdleTimeoutSeconds = 300;
public static int MaxFrameBytes = 65536;
public static int MaxConnections = 5000;
public static int MaxConnectionsPerIp = 100;
public static int GracefulShutdownSeconds = 30;
public static int InternalHealthPort = 8080;
public static int QueclinkTcpPort = 5004;
public static int QueclinkUdpPort = 5004;
public static int Gt06TcpPort = 5023;
public static int Gt06UdpPort = 5023;
public static int TeltonikaTcpPort = 5027;
public static int TeltonikaUdpPort = 5027;
public static bool EnableQueclink = false;
public static bool EnableGt06 = false;
public static bool EnableTeltonika = false;
public static bool RawDiagnosticCaptureEnabled = false;
}
}
30 changes: 29 additions & 1 deletion Core/Resgrid.Framework/SentryTransactionFilter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,15 @@ public static class SentryTransactionFilter
/// </summary>
public static SentryTransaction Filter(SentryTransaction transaction)
{
if (transaction == null || transaction.Status != SpanStatus.NotFound)
if (transaction == null)
return null;

if (transaction.Request != null)
transaction.Request.Url = RedactCapabilityPath(transaction.Request.Url);

((IEventLike)transaction).TransactionName = RedactCapabilityPath(transaction.Name);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kody code-review Kody Rules low

Unhandled InvalidCastException risk caused by a direct cast to (IEventLike)transaction without a safety guard. Use pattern matching, such as if (transaction is IEventLike eventLike), to safely guard against null or incompatible types.

Kody rule violation: Use safe type casting with as operator

Prompt for LLM

File Core/Resgrid.Framework/SentryTransactionFilter.cs:

Line 70:

Unhandled `InvalidCastException` risk caused by a direct cast to `(IEventLike)transaction` without a safety guard. Use pattern matching, such as `if (transaction is IEventLike eventLike)`, to safely guard against null or incompatible types.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.


if (transaction.Status != SpanStatus.NotFound)
Comment thread
coderabbitai[bot] marked this conversation as resolved.
return transaction;

var requestTarget = transaction.Request?.Url;
Expand All @@ -71,6 +79,26 @@ public static SentryTransaction Filter(SentryTransaction transaction)
return ShouldDrop(transaction.Status, requestTarget) ? null : transaction;
}

public static string RedactCapabilityPath(string value)
{
const string prefix = "/api/v4/unit-trackers/c/";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kody code-review Kody Rules low

Decentralized string literals like route names violate reuse guidelines and complicate updates when paths change. Move this route-name constant to a centralized shared constants class, such as RouteConstants.UnitTrackerCapabilityPrefix, and reference it throughout the codebase.

Kody rule violation: Centralize string constants

Prompt for LLM

File Core/Resgrid.Framework/SentryTransactionFilter.cs:

Line 82:

Decentralized string literals like route names violate reuse guidelines and complicate updates when paths change. Move this route-name constant to a centralized shared constants class, such as `RouteConstants.UnitTrackerCapabilityPrefix`, and reference it throughout the codebase.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

if (string.IsNullOrWhiteSpace(value))
return value;

var start = value.IndexOf(prefix, StringComparison.OrdinalIgnoreCase);
if (start < 0)
return value;

var tokenStart = start + prefix.Length;
var tokenEnd = value.IndexOfAny(new[] { '?', '#', '/' }, tokenStart);
if (tokenEnd < 0)
tokenEnd = value.Length;

return value.Substring(0, tokenStart) +
"[REDACTED]" +
value.Substring(tokenEnd);
}

public static bool ShouldDrop(SpanStatus? status, string requestTarget)
{
return status == SpanStatus.NotFound && IsKnownScannerPath(requestTarget);
Expand Down
237 changes: 237 additions & 0 deletions Core/Resgrid.Localization/Areas/User/Units/Units.resx
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="AddTrackingBinding" xml:space="preserve">
<value>Add tracking binding</value>
</data>
<data name="AllowedSourceCidrs" xml:space="preserve">
<value>Allowed source networks</value>
</data>
<data name="AllowedSourceCidrsHelp" xml:space="preserve">
<value>Optional comma-separated IPv4 or IPv6 CIDR ranges permitted to send positions for this tracker.</value>
</data>
<data name="AuthenticationMode" xml:space="preserve">
<value>Authentication mode</value>
</data>
<data name="BasicUsername" xml:space="preserve">
<value>Basic authentication username</value>
</data>
<data name="BasicUsernameRequired" xml:space="preserve">
<value>A Basic authentication username is required.</value>
</data>
<data name="CertificationStatus" xml:space="preserve">
<value>Certification status</value>
</data>
<data name="CopyToClipboard" xml:space="preserve">
<value>Copy</value>
</data>
<data name="CreateCredential" xml:space="preserve">
<value>Create credential</value>
</data>
<data name="CredentialPrefix" xml:space="preserve">
<value>Credential prefix</value>
</data>
<data name="CredentialSaved" xml:space="preserve">
<value>I have saved this credential</value>
</data>
<data name="Credentials" xml:space="preserve">
<value>Credentials</value>
</data>
<data name="CredentialToken" xml:space="preserve">
<value>Credential token</value>
</data>
<data name="CustomHeaderRequired" xml:space="preserve">
<value>A custom header name is required.</value>
</data>
<data name="DeleteTrackingWarning" xml:space="preserve">
<value>Remove this tracking binding? Its credentials will be revoked and it will stop accepting positions.</value>
</data>
<data name="DeviceIdentifier" xml:space="preserve">
<value>Device identifier</value>
</data>
<data name="DeviceIdentifierHelp" xml:space="preserve">
<value>Use the identifier emitted by the device or forwarding service. It is normalized before storage.</value>
</data>
<data name="DisableTracking" xml:space="preserve">
<value>Disable tracking</value>
</data>
<data name="DisableTrackingWarning" xml:space="preserve">
<value>Disable this binding and revoke all of its credentials?</value>
</data>
<data name="EditTrackingBinding" xml:space="preserve">
<value>Edit tracking binding</value>
</data>
<data name="EndpointUrl" xml:space="preserve">
<value>HTTPS endpoint</value>
</data>
<data name="ExpiresOn" xml:space="preserve">
<value>Expires</value>
</data>
<data name="FirmwareVersion" xml:space="preserve">
<value>Firmware version</value>
</data>
<data name="HardwareTracking" xml:space="preserve">
<value>Hardware GPS tracking</value>
</data>
<data name="HardwareTrackingDescription" xml:space="preserve">
<value>Bind a hardware tracker or forwarding service to this Unit, issue credentials, and monitor delivery health.</value>
</data>
<data name="HeaderName" xml:space="preserve">
<value>Header name</value>
</data>
<data name="HeaderValue" xml:space="preserve">
<value>Header value</value>
</data>
<data name="LastErrorCode" xml:space="preserve">
<value>Last error code</value>
</data>
<data name="LastReceived" xml:space="preserve">
<value>Last received</value>
</data>
<data name="LastSeen" xml:space="preserve">
<value>Last seen</value>
</data>
<data name="LastUsed" xml:space="preserve">
<value>Last used</value>
</data>
<data name="LastValidFix" xml:space="preserve">
<value>Last valid fix</value>
</data>
<data name="JsonPayload" xml:space="preserve">
<value>JSON payload</value>
</data>
<data name="Never" xml:space="preserve">
<value>Never</value>
</data>
<data name="NoCredentials" xml:space="preserve">
<value>No credentials have been issued for this binding.</value>
</data>
<data name="NoTrackingBindings" xml:space="preserve">
<value>No hardware tracking bindings are configured for this Unit.</value>
</data>
<data name="OneTimeCredentialHeader" xml:space="preserve">
<value>Save tracking credential</value>
</data>
<data name="OneTimeCredentialWarning" xml:space="preserve">
<value>This credential is shown only once. Copy the endpoint and secret into the tracker configuration before leaving this page.</value>
</data>
<data name="Protocol" xml:space="preserve">
<value>Protocol</value>
</data>
<data name="PreviewJson" xml:space="preserve">
<value>Validate test JSON</value>
</data>
<data name="PreviewJsonMalformed" xml:space="preserve">
<value>The JSON payload is malformed, contains duplicate fields, or exceeds the nesting limit.</value>
</data>
<data name="PreviewJsonPositionInvalid" xml:space="preserve">
<value>Each position requires a non-empty eventId and valid latitude and longitude values.</value>
</data>
<data name="PreviewJsonPositionsRequired" xml:space="preserve">
<value>A batch must contain at least one JSON position object.</value>
</data>
<data name="PreviewJsonRequired" xml:space="preserve">
<value>Enter a JSON payload to validate.</value>
</data>
<data name="PreviewJsonSuccess" xml:space="preserve">
<value>The preview parser accepted {0} position(s). Nothing was queued or stored.</value>
</data>
<data name="PreviewJsonTooLarge" xml:space="preserve">
<value>The JSON payload exceeds the configured request size limit.</value>
</data>
<data name="PreviewJsonTooManyPositions" xml:space="preserve">
<value>The JSON batch exceeds the configured position limit.</value>
</data>
<data name="RevokeCredential" xml:space="preserve">
<value>Revoke</value>
</data>
<data name="RevokeCredentialWarning" xml:space="preserve">
<value>Revoke this credential? The sender will no longer be able to use it.</value>
</data>
<data name="RetryExpectation" xml:space="preserve">
<value>Retry expectation:</value>
</data>
<data name="Revoked" xml:space="preserve">
<value>Revoked</value>
</data>
<data name="RotateCredential" xml:space="preserve">
<value>Rotate</value>
</data>
<data name="SaveTrackingBinding" xml:space="preserve">
<value>Save tracking binding</value>
</data>
<data name="SendTestJson" xml:space="preserve">
<value>Send test JSON</value>
</data>
<data name="SendTestJsonDescription" xml:space="preserve">
<value>Validate a generic Resgrid JSON payload without authenticating, queueing, or storing it. This preview is available only to department administrators outside production.</value>
</data>
<data name="SetupInstructions" xml:space="preserve">
<value>Setup instructions</value>
</data>
<data name="SecondaryIdentifier" xml:space="preserve">
<value>Secondary identifier</value>
</data>
<data name="SelectTrackingProfile" xml:space="preserve">
<value>Select a certified tracking profile</value>
</data>
<data name="SelectValidTrackingProfile" xml:space="preserve">
<value>Select a certified tracking profile.</value>
</data>
<data name="SourcePriority" xml:space="preserve">
<value>Source priority</value>
</data>
<data name="TrackingBindingActions" xml:space="preserve">
<value>Tracking binding actions</value>
</data>
<data name="TrackingBindingCreatedMessage" xml:space="preserve">
<value>Tracking binding created. Generate a credential to finish setup.</value>
</data>
<data name="TrackingBindingDeletedMessage" xml:space="preserve">
<value>Tracking binding removed.</value>
</data>
<data name="TrackingBindingDisabledMessage" xml:space="preserve">
<value>Tracking binding disabled and its credentials revoked.</value>
</data>
<data name="TrackingBindingUpdatedMessage" xml:space="preserve">
<value>Tracking binding updated.</value>
</data>
<data name="TrackingCredentialRevokedMessage" xml:space="preserve">
<value>Tracking credential revoked.</value>
</data>
<data name="TrackingDisplayName" xml:space="preserve">
<value>Display name</value>
</data>
<data name="TrackingEnabled" xml:space="preserve">
<value>Enabled</value>
</data>
<data name="TrackingIdentifierRequired" xml:space="preserve">
<value>A device identifier is required for the selected profile.</value>
</data>
<data name="TrackingProfile" xml:space="preserve">
<value>Tracking profile</value>
</data>
<data name="TrackingStatus" xml:space="preserve">
<value>Tracking status</value>
</data>
<data name="Transport" xml:space="preserve">
<value>Transport</value>
</data>
<data name="UnsupportedTrackingAuth" xml:space="preserve">
<value>The selected authentication mode is not supported by this tracking profile.</value>
</data>
<data name="ViewTrackingStatus" xml:space="preserve">
<value>View tracking status</value>
</data>
</root>
12 changes: 11 additions & 1 deletion Core/Resgrid.Model/AuditLogTypes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,16 @@ public enum AuditLogTypes
WeatherAlertSettingsChanged,
// Feature Toggles
FeatureFlagChanged,
FeatureFlagOverrideChanged
FeatureFlagOverrideChanged,
// Unit hardware tracking
UnitTrackingDeviceCreated,
UnitTrackingDeviceUpdated,
UnitTrackingDeviceDisabled,
UnitTrackingDeviceDeleted,
UnitTrackingCredentialCreated,
UnitTrackingCredentialRotated,
UnitTrackingCredentialRevoked,
// Department deletion lifecycle
DeleteDepartmentRequestExecuted
}
}
Loading
Loading