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
6 changes: 6 additions & 0 deletions internal/admin/dashboard/static/css/dashboard.css
Original file line number Diff line number Diff line change
Expand Up @@ -3187,6 +3187,12 @@ textarea:focus {
}

/* Audit Log Section */
.audit-retention-note {
margin-top: 5px;
color: var(--text-muted);
font-size: 13px;
}

.audit-log-section {
background: var(--bg-surface);
border: 1px solid var(--border);
Expand Down
11 changes: 11 additions & 0 deletions internal/admin/dashboard/static/js/modules/audit-list.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,17 @@
: null;

return {
auditRetentionText() {
const raw = this.workflowRuntimeConfig && this.workflowRuntimeConfig.LOGGING_RETENTION_DAYS;
if (raw === undefined || raw === null || String(raw).trim() === '') return '';

const days = Number(raw);
if (!Number.isInteger(days) || days < 0) return '';
if (days === 0) return 'Audit logs are retained indefinitely.';
if (days === 1) return 'Audit logs are retained for 1 day.';
return 'Audit logs are retained for ' + days + ' days.';
},

_auditQueryStr() {
if (this.customStartDate && this.customEndDate) {
return 'start_date=' + this._formatDate(this.customStartDate) +
Expand Down
26 changes: 26 additions & 0 deletions internal/admin/dashboard/static/js/modules/audit-list.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,32 @@ function loadConversationHelpers() {
return context.window.DashboardConversationHelpers;
}

test('auditRetentionText describes finite and indefinite retention', () => {
const module = createAuditListModule();

module.workflowRuntimeConfig = { LOGGING_RETENTION_DAYS: '30' };
assert.equal(module.auditRetentionText(), 'Audit logs are retained for 30 days.');

module.workflowRuntimeConfig.LOGGING_RETENTION_DAYS = '1';
assert.equal(module.auditRetentionText(), 'Audit logs are retained for 1 day.');

module.workflowRuntimeConfig.LOGGING_RETENTION_DAYS = '0';
assert.equal(module.auditRetentionText(), 'Audit logs are retained indefinitely.');
});

test('auditRetentionText hides missing or invalid retention values', () => {
const module = createAuditListModule();

module.workflowRuntimeConfig = {};
assert.equal(module.auditRetentionText(), '');

module.workflowRuntimeConfig.LOGGING_RETENTION_DAYS = '-1';
assert.equal(module.auditRetentionText(), '');

module.workflowRuntimeConfig.LOGGING_RETENTION_DAYS = 'unknown';
assert.equal(module.auditRetentionText(), '');
});

test('auditRequestPane returns the shared request-pane contract', () => {
const module = createAuditListModule();
const entry = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -862,6 +862,14 @@ test("audit toolbar uses a full-width search row above the select row with a rig
const iconTemplate = readFixture("../../../templates/x-icon.html");
const css = readFixture("../../css/dashboard.css");

assert.match(
indexTemplate,
/<h2>Audit Logs<\/h2>\s*<p class="audit-retention-note" x-show="auditRetentionText\(\)" x-text="auditRetentionText\(\)"><\/p>/,
);
const retentionRule = readCSSRule(css, ".audit-retention-note");
assert.match(retentionRule, /color:\s*var\(--text-muted\)/);
assert.match(retentionRule, /font-size:\s*13px/);

assert.match(
indexTemplate,
/<div class="audit-filter-row audit-filter-row-search">[\s\S]*id="audit-filter-search"[\s\S]*<\/div>\s*<div class="audit-filter-row audit-filter-row-controls">[\s\S]*id="audit-filter-method"[\s\S]*id="audit-filter-status"[\s\S]*id="audit-filter-stream"[\s\S]*class="pagination-btn audit-clear-btn" @click="clearAuditFilters\(\)"/,
Expand Down
1 change: 1 addition & 0 deletions internal/admin/dashboard/static/js/modules/workflows.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
return [
'FAILOVER_ENABLED',
'LOGGING_ENABLED',
'LOGGING_RETENTION_DAYS',
'USAGE_ENABLED',
'BUDGETS_ENABLED',
'RATE_LIMITS_ENABLED',
Expand Down
2 changes: 2 additions & 0 deletions internal/admin/dashboard/static/js/modules/workflows.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -1286,6 +1286,7 @@ test('fetchWorkflowRuntimeConfig loads FAILOVER_ENABLED from the admin config en
json: async () => ({
FAILOVER_ENABLED: 'on',
LOGGING_ENABLED: 'on',
LOGGING_RETENTION_DAYS: 30,
USAGE_ENABLED: 'off',
BUDGETS_ENABLED: 'on',
RATE_LIMITS_ENABLED: 'off',
Expand All @@ -1308,6 +1309,7 @@ test('fetchWorkflowRuntimeConfig loads FAILOVER_ENABLED from the admin config en
JSON.stringify({
FAILOVER_ENABLED: 'on',
LOGGING_ENABLED: 'on',
LOGGING_RETENTION_DAYS: '30',
USAGE_ENABLED: 'off',
BUDGETS_ENABLED: 'on',
RATE_LIMITS_ENABLED: 'off',
Expand Down
5 changes: 4 additions & 1 deletion internal/admin/dashboard/templates/page-audit-logs.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
<template x-if="page==='audit-logs'">
<div>
<div class="page-header">
<h2>Audit Logs</h2>
<div>
<h2>Audit Logs</h2>
<p class="audit-retention-note" x-show="auditRetentionText()" x-text="auditRetentionText()"></p>
</div>
<div class="page-header-controls">
{{template "date-picker" .}}
</div>
Expand Down
3 changes: 3 additions & 0 deletions internal/admin/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ type Option func(*Handler)
const (
DashboardConfigFailoverEnabled = "FAILOVER_ENABLED"
DashboardConfigLoggingEnabled = "LOGGING_ENABLED"
DashboardConfigLoggingRetentionDays = "LOGGING_RETENTION_DAYS"
DashboardConfigUsageEnabled = "USAGE_ENABLED"
DashboardConfigBudgetsEnabled = "BUDGETS_ENABLED"
DashboardConfigRateLimitsEnabled = "RATE_LIMITS_ENABLED"
Expand All @@ -82,6 +83,7 @@ const statusClientClosedRequest = 499
type DashboardConfigResponse struct {
FailoverEnabled string `json:"FAILOVER_ENABLED,omitempty"`
LoggingEnabled string `json:"LOGGING_ENABLED,omitempty"`
LoggingRetentionDays string `json:"LOGGING_RETENTION_DAYS,omitempty"`
UsageEnabled string `json:"USAGE_ENABLED,omitempty"`
BudgetsEnabled string `json:"BUDGETS_ENABLED,omitempty"`
RateLimitsEnabled string `json:"RATE_LIMITS_ENABLED,omitempty"`
Expand Down Expand Up @@ -326,6 +328,7 @@ func normalizeDashboardRuntimeConfig(values DashboardConfigResponse) DashboardCo
return DashboardConfigResponse{
FailoverEnabled: strings.TrimSpace(values.FailoverEnabled),
LoggingEnabled: strings.TrimSpace(values.LoggingEnabled),
LoggingRetentionDays: strings.TrimSpace(values.LoggingRetentionDays),
UsageEnabled: strings.TrimSpace(values.UsageEnabled),
BudgetsEnabled: strings.TrimSpace(values.BudgetsEnabled),
RateLimitsEnabled: strings.TrimSpace(values.RateLimitsEnabled),
Expand Down
4 changes: 4 additions & 0 deletions internal/admin/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2252,6 +2252,7 @@ func TestDashboardConfig_ReturnsAllowlistedRuntimeFlags(t *testing.T) {
h := NewHandler(nil, nil, WithDashboardRuntimeConfig(DashboardConfigResponse{
FailoverEnabled: "on",
LoggingEnabled: "on",
LoggingRetentionDays: "14",
UsageEnabled: "off",
BudgetsEnabled: "on",
RateLimitsEnabled: "off",
Expand Down Expand Up @@ -2281,6 +2282,9 @@ func TestDashboardConfig_ReturnsAllowlistedRuntimeFlags(t *testing.T) {
if got := body.LoggingEnabled; got != "on" {
t.Fatalf("LOGGING_ENABLED = %q, want on", got)
}
if got := body.LoggingRetentionDays; got != "14" {
t.Fatalf("LOGGING_RETENTION_DAYS = %q, want 14", got)
}
if got := body.UsageEnabled; got != "off" {
t.Fatalf("USAGE_ENABLED = %q, want off", got)
}
Expand Down
8 changes: 8 additions & 0 deletions internal/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -1281,6 +1281,7 @@ func dashboardRuntimeConfig(cfg *config.Config, usageEnabled bool) admin.Dashboa
return admin.DashboardConfigResponse{
FailoverEnabled: dashboardEnabledValue(failoverFeatureEnabledGlobally(cfg)),
LoggingEnabled: dashboardEnabledValue(cfg != nil && cfg.Logging.Enabled),
LoggingRetentionDays: dashboardLoggingRetentionDays(cfg),
UsageEnabled: dashboardEnabledValue(cfg != nil && cfg.Usage.Enabled),
BudgetsEnabled: dashboardEnabledValue(cfg != nil && cfg.Budgets.Enabled),
RateLimitsEnabled: dashboardEnabledValue(cfg != nil && cfg.RateLimits.Enabled),
Expand All @@ -1292,6 +1293,13 @@ func dashboardRuntimeConfig(cfg *config.Config, usageEnabled bool) admin.Dashboa
}
}

func dashboardLoggingRetentionDays(cfg *config.Config) string {
if cfg == nil {
return ""
}
return fmt.Sprintf("%d", cfg.Logging.RetentionDays)
}

func usagePricingRecalculationConfigured(cfg *config.Config) bool {
return cfg != nil && cfg.Usage.Enabled && cfg.Usage.PricingRecalculationEnabled
}
Expand Down
13 changes: 12 additions & 1 deletion internal/app/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,8 @@ func TestDashboardRuntimeConfig_ExposesFeatureAvailabilityFlags(t *testing.T) {
semanticOff := false
cfg := &config.Config{
Logging: config.LogConfig{
Enabled: true,
Enabled: true,
RetentionDays: 14,
},
Usage: config.UsageConfig{
Enabled: true,
Expand Down Expand Up @@ -509,6 +510,9 @@ func TestDashboardRuntimeConfig_ExposesFeatureAvailabilityFlags(t *testing.T) {
if got := values.LoggingEnabled; got != "on" {
t.Fatalf("dashboardRuntimeConfig()[%q] = %q, want on", admin.DashboardConfigLoggingEnabled, got)
}
if got := values.LoggingRetentionDays; got != "14" {
t.Fatalf("dashboardRuntimeConfig()[%q] = %q, want 14", admin.DashboardConfigLoggingRetentionDays, got)
}
if got := values.UsageEnabled; got != "on" {
t.Fatalf("dashboardRuntimeConfig()[%q] = %q, want on", admin.DashboardConfigUsageEnabled, got)
}
Expand All @@ -532,6 +536,13 @@ func TestDashboardRuntimeConfig_ExposesFeatureAvailabilityFlags(t *testing.T) {
}
}

func TestDashboardRuntimeConfig_ExposesIndefiniteLoggingRetention(t *testing.T) {
values := dashboardRuntimeConfig(&config.Config{}, false)
if got := values.LoggingRetentionDays; got != "0" {
t.Fatalf("dashboardRuntimeConfig()[%q] = %q, want 0", admin.DashboardConfigLoggingRetentionDays, got)
}
}

func TestDashboardRuntimeConfig_HidesCacheAnalyticsWhenUsageDisabled(t *testing.T) {
cfg := &config.Config{
Usage: config.UsageConfig{
Expand Down