Documentation
¶
Index ¶
- type ActivityItem
- type AssetStatsData
- type CreateReportScheduleInput
- type DashboardAllStats
- type DashboardService
- func (s *DashboardService) GetDataQualityScorecard(ctx context.Context, tenantID shared.ID) (*DataQualityScorecard, error)
- func (s *DashboardService) GetExecutiveSummary(ctx context.Context, tenantID shared.ID, days int) (*ExecutiveSummary, error)
- func (s *DashboardService) GetGlobalStats(ctx context.Context) (*DashboardStats, error)
- func (s *DashboardService) GetMTTRAnalytics(ctx context.Context, tenantID shared.ID, days int) (*MTTRAnalytics, error)
- func (s *DashboardService) GetMTTRMetrics(ctx context.Context, tenantID shared.ID) (map[string]float64, error)
- func (s *DashboardService) GetProcessMetrics(ctx context.Context, tenantID shared.ID, days int) (*ProcessMetrics, error)
- func (s *DashboardService) GetRiskTrend(ctx context.Context, tenantID shared.ID, days int) ([]RiskTrendPoint, error)
- func (s *DashboardService) GetRiskVelocity(ctx context.Context, tenantID shared.ID, weeks int) ([]RiskVelocityPoint, error)
- func (s *DashboardService) GetStats(ctx context.Context, tenantID shared.ID) (*DashboardStats, error)
- func (s *DashboardService) GetStatsForTenants(ctx context.Context, tenantIDs []string) (*DashboardStats, error)
- type DashboardStats
- type DashboardStatsRepository
- type DataQualityScorecard
- type DependencyEdgeOutput
- type DependencyGraphOutput
- type ExecutiveSummary
- type FindingStatsData
- type FindingTrendPoint
- type GetTenantEnabledModulesOutput
- type MTTRAnalytics
- type ModulePresetOutput
- type ModuleRefOutput
- type ModuleRepository
- type ModuleService
- func (s *ModuleService) ApplyPreset(ctx context.Context, tenantID, presetID string, actx auditapp.AuditContext) (*TenantModuleConfigOutput, error)
- func (s *ModuleService) GetDependencyGraph(_ context.Context) *DependencyGraphOutput
- func (s *ModuleService) GetModule(ctx context.Context, moduleID string) (*moduledom.Module, error)
- func (s *ModuleService) GetTenantEnabledModules(ctx context.Context, tenantID string) (*GetTenantEnabledModulesOutput, error)
- func (s *ModuleService) GetTenantModuleConfig(ctx context.Context, tenantID string) (*TenantModuleConfigOutput, error)
- func (s *ModuleService) GetTenantModuleVersion(ctx context.Context, tenantID string) int
- func (s *ModuleService) ListActiveModules(ctx context.Context) ([]*moduledom.Module, error)
- func (s *ModuleService) ListModulePresets(_ context.Context) []ModulePresetOutput
- func (s *ModuleService) PreviewPreset(ctx context.Context, tenantID, presetID string) (*PresetDiffOutput, error)
- func (s *ModuleService) ResetTenantModules(ctx context.Context, tenantID string, actx auditapp.AuditContext) (*TenantModuleConfigOutput, error)
- func (s *ModuleService) SetAuditService(svc *auditapp.AuditService)
- func (s *ModuleService) SetTenantModuleRepo(repo TenantModuleRepository)
- func (s *ModuleService) SetVersionService(v *VersionService)
- func (s *ModuleService) SetWSBroadcaster(b WSBroadcaster)
- func (s *ModuleService) UpdateTenantModules(ctx context.Context, tenantID string, updates []moduledom.TenantModuleUpdate, ...) (*TenantModuleConfigOutput, error)
- func (s *ModuleService) ValidateToggle(ctx context.Context, tenantID string, updates []moduledom.TenantModuleUpdate) (*ValidationIssues, error)
- type PresetDiffOutput
- type ProcessMetrics
- type ReportScheduleService
- func (s *ReportScheduleService) CreateSchedule(ctx context.Context, input CreateReportScheduleInput) (*reportscheduledom.ReportSchedule, error)
- func (s *ReportScheduleService) DeleteSchedule(ctx context.Context, tenantID, scheduleID string) error
- func (s *ReportScheduleService) GetSchedule(ctx context.Context, tenantID, scheduleID string) (*reportscheduledom.ReportSchedule, error)
- func (s *ReportScheduleService) ListSchedules(ctx context.Context, tenantID string, page pagination.Pagination) (pagination.Result[*reportscheduledom.ReportSchedule], error)
- func (s *ReportScheduleService) ToggleSchedule(ctx context.Context, tenantID, scheduleID string, active bool) error
- type RepositoryStatsData
- type RiskTrendPoint
- type RiskVelocityPoint
- type SubModuleInfo
- type TenantModuleConfigOutput
- type TenantModuleInfo
- type TenantModuleRepository
- type TenantModuleSummary
- type ToggleError
- type ToggleIssue
- type TopRisk
- type ValidationIssues
- type VersionService
- type WSBroadcaster
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type ActivityItem ¶
ActivityItem represents a recent activity item.
type AssetStatsData ¶
type AssetStatsData struct {
Total int
ByType map[string]int
BySubType map[string]int
ByStatus map[string]int
AverageRiskScore float64
}
AssetStatsData holds raw asset statistics from repository.
type CreateReportScheduleInput ¶
type CreateReportScheduleInput struct {
TenantID string
Name string
ReportType string
Format string
CronExpression string
Timezone string
Recipients []reportscheduledom.Recipient
Options map[string]any
ActorID string
}
CreateReportScheduleInput holds input for creating a schedule.
type DashboardAllStats ¶
type DashboardAllStats struct {
Assets AssetStatsData
Findings FindingStatsData
Repos RepositoryStatsData
Activity []ActivityItem
}
DashboardAllStats holds all dashboard stats from the optimized batched query.
type DashboardService ¶
type DashboardService struct {
// contains filtered or unexported fields
}
DashboardService provides dashboard-related operations.
func NewDashboardService ¶
func NewDashboardService(repo DashboardStatsRepository, log *logger.Logger) *DashboardService
NewDashboardService creates a new DashboardService.
func (*DashboardService) GetDataQualityScorecard ¶
func (s *DashboardService) GetDataQualityScorecard(ctx context.Context, tenantID shared.ID) (*DataQualityScorecard, error)
GetDataQualityScorecard returns data quality metrics (RFC-005 Gap 5).
func (*DashboardService) GetExecutiveSummary ¶
func (s *DashboardService) GetExecutiveSummary(ctx context.Context, tenantID shared.ID, days int) (*ExecutiveSummary, error)
GetExecutiveSummary returns executive-level metrics for a time period.
func (*DashboardService) GetGlobalStats ¶
func (s *DashboardService) GetGlobalStats(ctx context.Context) (*DashboardStats, error)
GetGlobalStats returns global dashboard statistics (not tenant-scoped). Deprecated: Use GetStatsForTenants for proper multi-tenant authorization.
func (*DashboardService) GetMTTRAnalytics ¶
func (s *DashboardService) GetMTTRAnalytics(ctx context.Context, tenantID shared.ID, days int) (*MTTRAnalytics, error)
GetMTTRAnalytics returns MTTR breakdown by severity and priority class.
func (*DashboardService) GetMTTRMetrics ¶
func (s *DashboardService) GetMTTRMetrics(ctx context.Context, tenantID shared.ID) (map[string]float64, error)
GetMTTRMetrics returns MTTR (Mean Time To Remediate) in hours by severity.
func (*DashboardService) GetProcessMetrics ¶
func (s *DashboardService) GetProcessMetrics(ctx context.Context, tenantID shared.ID, days int) (*ProcessMetrics, error)
GetProcessMetrics returns process efficiency metrics.
func (*DashboardService) GetRiskTrend ¶
func (s *DashboardService) GetRiskTrend(ctx context.Context, tenantID shared.ID, days int) ([]RiskTrendPoint, error)
GetRiskTrend returns risk snapshot time-series (RFC-005 Gap 4).
func (*DashboardService) GetRiskVelocity ¶
func (s *DashboardService) GetRiskVelocity(ctx context.Context, tenantID shared.ID, weeks int) ([]RiskVelocityPoint, error)
GetRiskVelocity returns weekly new vs resolved finding counts.
func (*DashboardService) GetStats ¶
func (s *DashboardService) GetStats(ctx context.Context, tenantID shared.ID) (*DashboardStats, error)
GetStats returns dashboard statistics for a tenant. Uses optimized batched query (2 queries instead of 10+).
func (*DashboardService) GetStatsForTenants ¶
func (s *DashboardService) GetStatsForTenants(ctx context.Context, tenantIDs []string) (*DashboardStats, error)
GetStatsForTenants returns dashboard statistics filtered by accessible tenant IDs. This should be used for multi-tenant authorization - only shows data from tenants the user has access to.
type DashboardStats ¶
type DashboardStats struct {
// Asset stats
AssetCount int
AssetsByType map[string]int
AssetsBySubType map[string]int
AssetsByStatus map[string]int
AverageRiskScore float64
// Finding stats
FindingCount int
FindingsBySeverity map[string]int
FindingsByStatus map[string]int
OverdueFindings int
AverageCVSS float64
// Repository stats (repositories are assets with type 'repository')
RepositoryCount int
RepositoriesWithFindings int
// Recent activity
RecentActivity []ActivityItem
// Finding trend (monthly breakdown by severity)
FindingTrend []FindingTrendPoint
}
DashboardStats represents aggregated dashboard statistics.
type DashboardStatsRepository ¶
type DashboardStatsRepository interface {
// GetAssetStats returns asset statistics for a tenant
GetAssetStats(ctx context.Context, tenantID shared.ID) (AssetStatsData, error)
// GetFindingStats returns finding statistics for a tenant
GetFindingStats(ctx context.Context, tenantID shared.ID) (FindingStatsData, error)
// GetRepositoryStats returns repository statistics for a tenant
GetRepositoryStats(ctx context.Context, tenantID shared.ID) (RepositoryStatsData, error)
// GetRecentActivity returns recent activity for a tenant
GetRecentActivity(ctx context.Context, tenantID shared.ID, limit int) ([]ActivityItem, error)
// GetFindingTrend returns monthly finding counts by severity for a tenant
GetFindingTrend(ctx context.Context, tenantID shared.ID, months int) ([]FindingTrendPoint, error)
// GetAllStats returns all dashboard stats in 2 optimized queries (replaces 10+ individual calls)
GetAllStats(ctx context.Context, tenantID shared.ID) (*DashboardAllStats, error)
// Global stats (not tenant-scoped) - deprecated, use filtered versions
GetGlobalAssetStats(ctx context.Context) (AssetStatsData, error)
GetGlobalFindingStats(ctx context.Context) (FindingStatsData, error)
GetGlobalRepositoryStats(ctx context.Context) (RepositoryStatsData, error)
GetGlobalRecentActivity(ctx context.Context, limit int) ([]ActivityItem, error)
// MTTR & Trending
GetMTTRMetrics(ctx context.Context, tenantID shared.ID) (map[string]float64, error)
GetRiskVelocity(ctx context.Context, tenantID shared.ID, weeks int) ([]RiskVelocityPoint, error)
// Filtered stats (by accessible tenant IDs) - for multi-tenant authorization
GetFilteredAssetStats(ctx context.Context, tenantIDs []string) (AssetStatsData, error)
GetFilteredFindingStats(ctx context.Context, tenantIDs []string) (FindingStatsData, error)
GetFilteredRepositoryStats(ctx context.Context, tenantIDs []string) (RepositoryStatsData, error)
GetFilteredRecentActivity(ctx context.Context, tenantIDs []string, limit int) ([]ActivityItem, error)
// Data Quality Scorecard (RFC-005)
GetDataQualityScorecard(ctx context.Context, tenantID shared.ID) (*DataQualityScorecard, error)
// Risk Trend (RFC-005 Gap 4)
GetRiskTrend(ctx context.Context, tenantID shared.ID, days int) ([]RiskTrendPoint, error)
// Executive Summary (Phase 2)
GetExecutiveSummary(ctx context.Context, tenantID shared.ID, days int) (*ExecutiveSummary, error)
// MTTR Analytics (Phase 2)
GetMTTRAnalytics(ctx context.Context, tenantID shared.ID, days int) (*MTTRAnalytics, error)
// Process Metrics (Phase 2)
GetProcessMetrics(ctx context.Context, tenantID shared.ID, days int) (*ProcessMetrics, error)
}
DashboardStatsRepository defines the interface for dashboard data access.
type DataQualityScorecard ¶
type DataQualityScorecard struct {
AssetOwnershipPct float64 `json:"asset_ownership_pct"`
FindingEvidencePct float64 `json:"finding_evidence_pct"`
MedianLastSeenDays float64 `json:"median_last_seen_days"`
DeduplicationRate float64 `json:"deduplication_rate"`
TotalAssets int `json:"total_assets"`
TotalFindings int `json:"total_findings"`
}
DataQualityScorecard holds data quality metrics (RFC-005 Gap 5).
type DependencyEdgeOutput ¶
type DependencyEdgeOutput struct {
From string `json:"from"` // Module that depends on the target.
To string `json:"to"` // Target module required by From.
Type string `json:"type"` // "hard" or "soft".
Reason string `json:"reason,omitempty"` // Human-readable explanation.
}
DependencyEdgeOutput is the UI-facing shape of one dependency edge.
type DependencyGraphOutput ¶
type DependencyGraphOutput struct {
Edges []DependencyEdgeOutput `json:"edges"`
}
DependencyGraphOutput is returned by GetDependencyGraph. Flat edge list so the UI can render the graph with any layout library without re-shaping the server response.
type ExecutiveSummary ¶
type ExecutiveSummary struct {
Period string `json:"period"`
RiskScoreCurrent float64 `json:"risk_score_current"`
RiskScoreChange float64 `json:"risk_score_change"`
FindingsTotal int `json:"findings_total"`
FindingsResolved int `json:"findings_resolved_period"`
FindingsNew int `json:"findings_new_period"`
P0Open int `json:"p0_open"`
P0Resolved int `json:"p0_resolved_period"`
P1Open int `json:"p1_open"`
P1Resolved int `json:"p1_resolved_period"`
SLACompliancePct float64 `json:"sla_compliance_pct"`
SLABreached int `json:"sla_breached"`
MTTRCriticalHrs float64 `json:"mttr_critical_hours"`
MTTRHighHrs float64 `json:"mttr_high_hours"`
CrownJewelsAtRisk int `json:"crown_jewels_at_risk"`
RegressionCount int `json:"regression_count"`
RegressionRatePct float64 `json:"regression_rate_pct"`
TopRisks []TopRisk `json:"top_risks"`
}
ExecutiveSummary holds executive-level metrics for a time period.
type FindingStatsData ¶
type FindingStatsData struct {
Total int
BySeverity map[string]int
ByStatus map[string]int
Overdue int
AverageCVSS float64
}
FindingStatsData holds raw finding statistics from repository.
type FindingTrendPoint ¶
type FindingTrendPoint struct {
Date string // "Jan", "Feb", etc.
Critical int
High int
Medium int
Low int
Info int
}
FindingTrendPoint represents one month's finding counts by severity.
type GetTenantEnabledModulesOutput ¶
type GetTenantEnabledModulesOutput struct {
ModuleIDs []string
Modules []*moduledom.Module
SubModules map[string][]*moduledom.Module
}
GetTenantEnabledModulesOutput represents the output for GetTenantEnabledModules.
type MTTRAnalytics ¶
type MTTRAnalytics struct {
BySeverity map[string]float64 `json:"by_severity"`
ByPriorityClass map[string]float64 `json:"by_priority_class"`
Overall float64 `json:"overall_hours"`
SampleSize int `json:"sample_size"`
}
MTTRAnalytics holds MTTR breakdown by severity and priority class.
type ModulePresetOutput ¶
type ModulePresetOutput struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
TargetPersona string `json:"target_persona"`
Icon string `json:"icon"`
KeyOutcomes []string `json:"key_outcomes"`
RecommendedFor []string `json:"recommended_for"`
ModuleCount int `json:"module_count"` // resolved count incl. core + transitive
}
ModulePresetOutput is the API-facing shape of one preset.
type ModuleRefOutput ¶
ModuleRefOutput is a thin (id, name) pair used in diff listings.
type ModuleRepository ¶
type ModuleRepository interface {
ListAllModules(ctx context.Context) ([]*moduledom.Module, error)
ListActiveModules(ctx context.Context) ([]*moduledom.Module, error)
GetModuleByID(ctx context.Context, id string) (*moduledom.Module, error)
GetSubModules(ctx context.Context, parentModuleID string) ([]*moduledom.Module, error)
ListAllSubModules(ctx context.Context) (map[string][]*moduledom.Module, error)
}
ModuleRepository interface for module operations.
type ModuleService ¶
type ModuleService struct {
// contains filtered or unexported fields
}
ModuleService handles module-related business operations.
toggleLocks serialises concurrent UpdateTenantModules calls for the same tenant. Without this, two admins flipping related modules at the same time could interleave reads and writes such that the dependency check passes for each request individually but the resulting combined state violates the invariant (TOCTOU). The lock is per-tenant so different tenants don't contend.
Caveat: this is an in-process mutex. A multi-replica deployment needs a cross-process lock (Redis SETNX, Postgres advisory lock on a shared connection, etc.). In the CTEM deployment model the API runs as a single replica today; when horizontal scale lands, swap this map for the advisory-lock variant.
func NewModuleService ¶
func NewModuleService(moduleRepo ModuleRepository, log *logger.Logger) *ModuleService
NewModuleService creates a new ModuleService.
func (*ModuleService) ApplyPreset ¶
func (s *ModuleService) ApplyPreset(ctx context.Context, tenantID, presetID string, actx auditapp.AuditContext) (*TenantModuleConfigOutput, error)
ApplyPreset materialises the preset into tenant_modules. Diff is computed, then turned into UpdateTenantModules calls so the dependency-graph validation and audit logging run exactly as if the admin had toggled each module manually. Same locking semantics too.
If tenantID is empty or the audit context has no actor, this still works for the initial tenant-creation code path — the caller is expected to have populated actx in that case.
func (*ModuleService) GetDependencyGraph ¶
func (s *ModuleService) GetDependencyGraph(_ context.Context) *DependencyGraphOutput
GetDependencyGraph returns the static module dependency graph. The graph is read from pkg/domain/module/dependency.go (platform-wide spec, does not change per tenant or at runtime). The UI uses this to render the Settings → Modules page with dependency badges and to show "disabling X will also affect Y, Z" confirmation dialogs.
func (*ModuleService) GetTenantEnabledModules ¶
func (s *ModuleService) GetTenantEnabledModules(ctx context.Context, tenantID string) (*GetTenantEnabledModulesOutput, error)
GetTenantEnabledModules returns all enabled modules for a tenant. Filters by tenant module overrides if configured. Optimized: 2 queries (modules + tenant_modules) instead of 3. Sub-modules are extracted from the same ListActiveModules result.
func (*ModuleService) GetTenantModuleConfig ¶
func (s *ModuleService) GetTenantModuleConfig(ctx context.Context, tenantID string) (*TenantModuleConfigOutput, error)
GetTenantModuleConfig returns the full module configuration for a tenant admin. Optimized: 2 queries (modules + tenant_modules) instead of 3.
func (*ModuleService) GetTenantModuleVersion ¶
func (s *ModuleService) GetTenantModuleVersion(ctx context.Context, tenantID string) int
GetTenantModuleVersion returns the current module-config version for a tenant. Used by HTTP handlers to construct ETag headers; the returned value is opaque to callers (treat as a token, not a count).
func (*ModuleService) ListActiveModules ¶
ListActiveModules returns all active modules.
func (*ModuleService) ListModulePresets ¶
func (s *ModuleService) ListModulePresets(_ context.Context) []ModulePresetOutput
ListModulePresets returns every preset registered in the domain catalogue, with the module count already resolved so the UI can render "Enables X modules" badges without a second round-trip.
func (*ModuleService) PreviewPreset ¶
func (s *ModuleService) PreviewPreset(ctx context.Context, tenantID, presetID string) (*PresetDiffOutput, error)
PreviewPreset computes the diff between the tenant's current module state and the state that would result from applying the preset. Read-only — never mutates tenant_modules.
func (*ModuleService) ResetTenantModules ¶
func (s *ModuleService) ResetTenantModules(ctx context.Context, tenantID string, actx auditapp.AuditContext) (*TenantModuleConfigOutput, error)
ResetTenantModules resets all module overrides for a tenant (all modules enabled).
func (*ModuleService) SetAuditService ¶
func (s *ModuleService) SetAuditService(svc *auditapp.AuditService)
SetAuditService sets the audit service for logging module changes.
func (*ModuleService) SetTenantModuleRepo ¶
func (s *ModuleService) SetTenantModuleRepo(repo TenantModuleRepository)
SetTenantModuleRepo sets the tenant module repository.
func (*ModuleService) SetVersionService ¶
func (s *ModuleService) SetVersionService(v *VersionService)
SetVersionService wires the per-tenant module-version counter used for ETag generation and Redis cache key suffixes. Optional — without it Get returns 1 forever and ETag never matches (acceptable degraded mode, just no caching benefit).
func (*ModuleService) SetWSBroadcaster ¶
func (s *ModuleService) SetWSBroadcaster(b WSBroadcaster)
SetWSBroadcaster wires the WebSocket fan-out used to push "module.updated" events to clients on the tenant channel. Optional — without it, mutations still succeed but clients have to wait for the next SWR dedup window to see changes.
func (*ModuleService) UpdateTenantModules ¶
func (s *ModuleService) UpdateTenantModules(ctx context.Context, tenantID string, updates []moduledom.TenantModuleUpdate, actx auditapp.AuditContext) (*TenantModuleConfigOutput, error)
UpdateTenantModules toggles modules for a tenant.
func (*ModuleService) ValidateToggle ¶
func (s *ModuleService) ValidateToggle(ctx context.Context, tenantID string, updates []moduledom.TenantModuleUpdate) (*ValidationIssues, error)
ValidateToggle runs the same dependency gate as UpdateTenantModules but WITHOUT persisting. The UI calls this from the module-toggle UI to preview blockers/warnings before the user commits. Hit path:
POST /api/v1/tenants/{t}/settings/modules/validate
type PresetDiffOutput ¶
type PresetDiffOutput struct {
PresetID string `json:"preset_id"`
PresetName string `json:"preset_name"`
ToEnable []ModuleRefOutput `json:"to_enable"`
ToDisable []ModuleRefOutput `json:"to_disable"`
Unchanged int `json:"unchanged"`
TotalAfter int `json:"total_after"`
AuditNotice string `json:"audit_notice,omitempty"`
}
PresetDiffOutput describes what would change if a preset were applied.
type ProcessMetrics ¶
type ProcessMetrics struct {
ApprovalAvgHours float64 `json:"approval_avg_hours"`
ApprovalCount int `json:"approval_count"`
RetestAvgHours float64 `json:"retest_avg_hours"`
RetestCount int `json:"retest_count"`
StaleAssets int `json:"stale_assets"`
StaleAssetsPct float64 `json:"stale_assets_pct"`
FindingsWithoutOwner int `json:"findings_without_owner"`
AvgTimeToAssignHours float64 `json:"avg_time_to_assign_hours"`
}
ProcessMetrics holds process efficiency metrics.
type ReportScheduleService ¶
type ReportScheduleService struct {
// contains filtered or unexported fields
}
ReportScheduleService handles report schedule business logic.
func NewReportScheduleService ¶
func NewReportScheduleService(repo reportscheduledom.Repository, log *logger.Logger) *ReportScheduleService
NewReportScheduleService creates a new ReportScheduleService.
func (*ReportScheduleService) CreateSchedule ¶
func (s *ReportScheduleService) CreateSchedule(ctx context.Context, input CreateReportScheduleInput) (*reportscheduledom.ReportSchedule, error)
CreateSchedule creates a new report schedule.
func (*ReportScheduleService) DeleteSchedule ¶
func (s *ReportScheduleService) DeleteSchedule(ctx context.Context, tenantID, scheduleID string) error
DeleteSchedule removes a schedule.
func (*ReportScheduleService) GetSchedule ¶
func (s *ReportScheduleService) GetSchedule(ctx context.Context, tenantID, scheduleID string) (*reportscheduledom.ReportSchedule, error)
GetSchedule retrieves a single schedule.
func (*ReportScheduleService) ListSchedules ¶
func (s *ReportScheduleService) ListSchedules(ctx context.Context, tenantID string, page pagination.Pagination) (pagination.Result[*reportscheduledom.ReportSchedule], error)
ListSchedules returns schedules for a tenant.
func (*ReportScheduleService) ToggleSchedule ¶
func (s *ReportScheduleService) ToggleSchedule(ctx context.Context, tenantID, scheduleID string, active bool) error
ToggleSchedule activates or deactivates a schedule.
type RepositoryStatsData ¶
RepositoryStatsData holds raw repository statistics from repository.
type RiskTrendPoint ¶
type RiskTrendPoint struct {
Date string `json:"date"`
RiskScoreAvg float64 `json:"risk_score_avg"`
FindingsOpen int `json:"findings_open"`
SLACompliancePct float64 `json:"sla_compliance_pct"`
P0Open int `json:"p0_open"`
P1Open int `json:"p1_open"`
P2Open int `json:"p2_open"`
P3Open int `json:"p3_open"`
}
RiskTrendPoint represents a single point in a risk trend time-series.
type RiskVelocityPoint ¶
type RiskVelocityPoint struct {
Week time.Time `json:"week"`
NewCount int `json:"new_count"`
ResolvedCount int `json:"resolved_count"`
Velocity int `json:"velocity"` // new - resolved (positive = losing ground)
}
RiskVelocityPoint represents weekly new vs resolved finding counts.
type SubModuleInfo ¶
SubModuleInfo combines sub-module metadata with tenant-specific enabled state.
type TenantModuleConfigOutput ¶
type TenantModuleConfigOutput struct {
Modules []*TenantModuleInfo
Summary TenantModuleSummary
// Warnings is a best-effort list of soft-dependency degradations
// introduced by the MOST RECENT toggle. On GET the field is empty;
// on PATCH it carries the warnings the service decided not to
// escalate to a blocker. Handlers render these as toast/banner on
// the Settings → Modules page.
Warnings []ToggleIssue
}
TenantModuleConfigOutput represents the full module configuration for a tenant.
type TenantModuleInfo ¶
type TenantModuleInfo struct {
Module *moduledom.Module
IsEnabled bool
SubModules []*SubModuleInfo
}
TenantModuleInfo combines module metadata with tenant-specific enabled state.
type TenantModuleRepository ¶
type TenantModuleRepository interface {
ListByTenant(ctx context.Context, tenantID shared.ID) ([]*moduledom.TenantModuleOverride, error)
UpsertBatch(ctx context.Context, tenantID shared.ID, updates []moduledom.TenantModuleUpdate, updatedBy *shared.ID) error
DeleteByTenant(ctx context.Context, tenantID shared.ID) error
}
TenantModuleRepository interface for per-tenant module configuration.
type TenantModuleSummary ¶
TenantModuleSummary provides counts of module states.
type ToggleError ¶
type ToggleError struct {
// ModuleID is the one the caller tried to toggle.
ModuleID string `json:"module_id"`
// ModuleName is the display name (used in the top-level message).
ModuleName string `json:"module_name"`
// Action is "enable" or "disable" — tells the UI which flow the
// error came from so it can phrase the dialog correctly.
Action string `json:"action"`
// Blockers are hard-dependent modules that stop the toggle. Each
// entry is suitable for a bullet list in the UI.
Blockers []ToggleIssue `json:"blockers,omitempty"`
// Required is the inverse — when enabling, these modules must be
// enabled first.
Required []ToggleIssue `json:"required,omitempty"`
}
ToggleError is returned when a module toggle is rejected by the dependency gate. It implements error so callers can errors.As() or errors.Is() against shared.ErrValidation; the HTTP handler type- asserts on ToggleError to render a structured 400 body the UI can parse without regex-ing the message.
func (*ToggleError) Error ¶
func (e *ToggleError) Error() string
Error formats a plain-text fallback for log lines / CLI. The HTTP handler should NOT rely on this and instead serialise the struct itself as JSON.
func (*ToggleError) Unwrap ¶
func (e *ToggleError) Unwrap() error
Unwrap lets callers errors.Is(err, shared.ErrValidation) succeed.
type ToggleIssue ¶
type ToggleIssue struct {
// ModuleID is the dependent module (the one that would be broken
// by the toggle). For blockers this is the module that stops the
// toggle; for warnings this is the module that will degrade.
ModuleID string `json:"module_id"`
// Name is the human-readable display name from the modules row.
Name string `json:"name"`
// Reason is the short sentence from the dependency spec explaining
// why the edge exists.
Reason string `json:"reason"`
}
ToggleIssue is one blocker or warning raised by module-toggle validation. The fields mirror the shape rendered on the Settings → Modules page: module ID for programmatic lookup, name for display, reason for a tooltip.
type TopRisk ¶
type TopRisk struct {
FindingTitle string `json:"title"`
Severity string `json:"severity"`
PriorityClass string `json:"priority_class"`
AssetName string `json:"asset_name"`
EPSSScore *float64 `json:"epss_score"`
IsInKEV bool `json:"is_in_kev"`
}
TopRisk represents a high-priority open finding for executive view.
type ValidationIssues ¶
type ValidationIssues struct {
// Blockers — HARD-dependent modules still enabled. Non-empty
// means the toggle is rejected.
Blockers []ToggleIssue `json:"blockers,omitempty"`
// Warnings — SOFT-dependent modules still enabled. Non-empty
// means the toggle proceeds but the UI should confirm with the
// user first.
Warnings []ToggleIssue `json:"warnings,omitempty"`
// Required — when enabling, modules that must be enabled first.
Required []ToggleIssue `json:"required,omitempty"`
}
ValidationIssues pairs blockers + warnings so callers that preview without applying still get both arrays. Warnings are not fatal — the toggle is allowed but the UI should surface them.
type VersionService ¶
type VersionService struct {
// contains filtered or unexported fields
}
VersionService tracks a per-tenant "module configuration version" in Redis. The counter increments on every mutation (toggle, reset, preset apply). Consumers use the version for:
- ETag headers → 304 Not Modified when the client's cached response matches the current version.
- Redis cache key suffix → old payloads auto-expire rather than needing active DELETEs.
- WebSocket "module.updated" payload → clients compare to their cached version and invalidate their SWR cache accordingly.
The pattern mirrors accesscontrol.PermissionVersionService — same INCR-based atomicity, same TTL refresh. One key per tenant rather than per (tenant, user) because module state is tenant-scoped.
Key format: mod_ver:{tenant_id} → integer version (starts at 1)
Graceful degradation: on Redis failure, reads return 1 and writes are best-effort — the HTTP layer still works, ETag just never matches (serving fresh payloads), and WS broadcasts still fire.
func NewVersionService ¶
func NewVersionService(redisClient *redis.Client, log *logger.Logger) *VersionService
NewVersionService creates a module version tracker. A nil redisClient is OK — every method degrades to the default (version 1) so the HTTP layer continues to work in tests or when Redis is down.
func (*VersionService) Get ¶
func (s *VersionService) Get(ctx context.Context, tenantID string) int
Get returns the current module version for the tenant. Returns 1 when no version exists yet or Redis is unavailable — callers must treat this as "unknown / probably changed" rather than a literal version.
func (*VersionService) Increment ¶
func (s *VersionService) Increment(ctx context.Context, tenantID string) int
Increment atomically bumps the module version for a tenant and refreshes the TTL. Called after every successful toggle / reset / preset apply. The returned version is the fresh value, useful for embedding in WebSocket broadcasts and log lines.
type WSBroadcaster ¶
WSBroadcaster is the minimal interface ModuleService needs to fan out "module.updated" events to subscribers on the tenant channel. Defined locally rather than imported from the websocket package to keep the dependency direction app→infra (not the other way) and to allow nil/no-op in tests.