alot more changes
This commit is contained in:
1 parent
23243ddbdc
commit
0ed2935f04
40 files changed
+2889
-115
No files matched your search
@@ -25,6 +25,42 @@ export function trendPoints(issue: Issue, hours = 24): TrendPoint[] {
|
||||
return points
|
||||
}
|
||||
|
||||
/**
|
||||
* Picks a compact chart window around real activity. A quiet 24-hour period
|
||||
* remains a useful context, but a short burst should not be reduced to a tiny
|
||||
* pair of points at the edge of a mostly empty chart.
|
||||
*/
|
||||
export function trendWindow(issue: Issue, maxHours = 24): { start: Date; hours: number } {
|
||||
const activeHours = Object.entries(issue.trend ?? {})
|
||||
.filter(([, count]) => count > 0)
|
||||
.map(([hour]) => new Date(`${hour}:00Z`))
|
||||
.filter((date) => !Number.isNaN(date.getTime()))
|
||||
.sort((a, b) => a.getTime() - b.getTime())
|
||||
|
||||
const now = new Date()
|
||||
const currentHour = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(), now.getUTCHours()))
|
||||
if (activeHours.length === 0) return { start: new Date(currentHour.getTime() - (maxHours - 1) * 3_600_000), hours: maxHours }
|
||||
|
||||
const first = activeHours[0]
|
||||
const last = activeHours[activeHours.length - 1]
|
||||
const activityHours = Math.floor((last.getTime() - first.getTime()) / 3_600_000) + 1
|
||||
const hours = Math.min(maxHours, Math.max(6, activityHours + 2))
|
||||
const start = new Date(first.getTime() - Math.floor((hours - activityHours) / 2) * 3_600_000)
|
||||
return { start, hours }
|
||||
}
|
||||
|
||||
/** Dense points for the adaptive issue-card window, oldest first. */
|
||||
export function trendWindowPoints(issue: Issue, maxHours = 24): TrendPoint[] {
|
||||
const { start, hours } = trendWindow(issue, maxHours)
|
||||
const points: TrendPoint[] = []
|
||||
for (let i = 0; i < hours; i++) {
|
||||
const d = new Date(start.getTime() + i * 3_600_000)
|
||||
const hour = d.toISOString().slice(0, 13) + ":00"
|
||||
points.push({ hour, count: issue.trend?.[hour] ?? 0 })
|
||||
}
|
||||
return points
|
||||
}
|
||||
|
||||
/** True when the issue has at least one occurrence in the last hour. */
|
||||
export function isSpike(issue: Issue): boolean {
|
||||
const points = trendPoints(issue, 1)
|
||||
@@ -167,6 +203,7 @@ export function strongestCorrelate(issue: Issue): Correlate | null {
|
||||
|
||||
export interface SparklineSeries {
|
||||
points: TrendPoint[]
|
||||
hours: number
|
||||
max: number
|
||||
/** Reference level (mean occurrence/hour) drawn as the expected band. */
|
||||
reference: number
|
||||
@@ -181,7 +218,7 @@ export interface SparklineSeries {
|
||||
* over real occurrence counts, not a forecast or ML-derived expected band.
|
||||
*/
|
||||
export function sparklineSeries(issue: Issue, hours = 24): SparklineSeries {
|
||||
const points = trendPoints(issue, hours)
|
||||
const points = trendWindowPoints(issue, hours)
|
||||
const max = Math.max(...points.map((p) => p.count), 1)
|
||||
const mean = points.reduce((sum, p) => sum + p.count, 0) / points.length
|
||||
const threshold = Math.max(mean * 1.5, mean + 1)
|
||||
@@ -192,7 +229,7 @@ export function sparklineSeries(issue: Issue, hours = 24): SparklineSeries {
|
||||
break
|
||||
}
|
||||
}
|
||||
return { points, max, reference: mean, breachIndex }
|
||||
return { points, hours: points.length, max, reference: mean, breachIndex }
|
||||
}
|
||||
|
||||
/** One row per trace (all its samples grouped), plus one per traceless sample; newest first. */
|
||||
|
||||
@@ -9,6 +9,7 @@ export const TAB_PATHS: Record<string, string> = {
|
||||
"/issues": "tab0",
|
||||
"/patterns": "tab8",
|
||||
"/rollouts": "tab9",
|
||||
"/ips": "tab10",
|
||||
"/traces": "tab1",
|
||||
"/metrics": "tab2",
|
||||
"/logs": "tab3",
|
||||
|
||||
@@ -301,3 +301,36 @@ export interface IssueTracesResponse {
|
||||
/** false when the server only had the bounded in-memory samples. */
|
||||
complete: boolean;
|
||||
}
|
||||
|
||||
export interface SourceIp {
|
||||
ip: string
|
||||
firstSeen: string
|
||||
lastSeen: string
|
||||
requests: number
|
||||
errors: number
|
||||
services: string[]
|
||||
userAgent?: string
|
||||
uaClass?: string
|
||||
/** false for loopback/private/reserved addresses (usually a proxy hop) */
|
||||
public: boolean
|
||||
/** matched bad-actor rule over the last 7 days ("" when none) */
|
||||
badActor?: BadActorReason
|
||||
probePaths?: number
|
||||
}
|
||||
|
||||
export interface SourceIpEvent {
|
||||
timestamp: string
|
||||
service: string
|
||||
method?: string
|
||||
host?: string
|
||||
route?: string
|
||||
spanName?: string
|
||||
statusCode: number
|
||||
isError: boolean
|
||||
traceId: string
|
||||
spanId: string
|
||||
userAgent?: string
|
||||
}
|
||||
|
||||
/** Reason from the server's bad-actor rules (features.MarkBadActors). */
|
||||
export type BadActorReason = "" | "probe-scanner" | "enumerator" | "bot-4xx"
|
||||
Reference in new issue
Block a user