mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-07-09 16:00:59 +00:00
parent
7ddc2fcb47
commit
4a597fa425
3 changed files with 54 additions and 0 deletions
|
|
@ -560,6 +560,11 @@ uninitialized audit stores must surface as `audit_store_unavailable`. The
|
|||
Audit Log settings surface may translate those stable API codes into recovery
|
||||
copy, but it must not show raw internal server errors or collapse audit-store
|
||||
state into a generic frontend failure.
|
||||
The persistent audit reader must also tolerate legacy timestamp encodings that
|
||||
were previously written into `audit_events.timestamp`, including Unix seconds,
|
||||
SQLite datetime values, and Go wall-clock strings carrying a monotonic
|
||||
`m=+...` suffix, so valid historical audit rows cannot make `/api/audit`
|
||||
return `query_failed`.
|
||||
That shared token-management boundary now also includes
|
||||
`frontend-modern/src/utils/apiTokenPresentation.ts`, so API-token load,
|
||||
generate, and revoke errors stay on one governed customer-facing wording path
|
||||
|
|
|
|||
|
|
@ -465,6 +465,12 @@ func parseAuditTimestampString(raw string) (time.Time, error) {
|
|||
if unix, err := strconv.ParseInt(raw, 10, 64); err == nil {
|
||||
return time.Unix(unix, 0), nil
|
||||
}
|
||||
if monotonicIndex := strings.LastIndex(raw, " m="); monotonicIndex > 0 && monotonicIndex+3 < len(raw) {
|
||||
sign := raw[monotonicIndex+3]
|
||||
if sign == '+' || sign == '-' {
|
||||
raw = strings.TrimSpace(raw[:monotonicIndex])
|
||||
}
|
||||
}
|
||||
|
||||
layouts := []string{
|
||||
time.RFC3339Nano,
|
||||
|
|
|
|||
|
|
@ -310,6 +310,49 @@ func TestSQLiteLoggerQueryHandlesLegacyDatetimeTimestampRows(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestSQLiteLoggerQueryHandlesLegacyGoMonotonicTimestampRows(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
|
||||
logger, err := NewSQLiteLogger(SQLiteLoggerConfig{
|
||||
DataDir: tempDir,
|
||||
CryptoMgr: newMockCryptoManager(),
|
||||
RetentionDays: 30,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("NewSQLiteLogger failed: %v", err)
|
||||
}
|
||||
defer logger.Close()
|
||||
|
||||
const legacyTimestamp = "2026-07-05 08:51:23.65653076 +0000 UTC m=+0.009025344"
|
||||
want := time.Date(2026, 7, 5, 8, 51, 23, 656530760, time.UTC)
|
||||
if _, err := logger.db.Exec(`
|
||||
INSERT INTO audit_events (id, timestamp, event_type, user, ip, path, success, details, signature)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
"legacy-go-monotonic",
|
||||
legacyTimestamp,
|
||||
"startup",
|
||||
sql.NullString{},
|
||||
sql.NullString{},
|
||||
"/api/audit",
|
||||
1,
|
||||
"legacy Go monotonic timestamp",
|
||||
"legacy-signature",
|
||||
); err != nil {
|
||||
t.Fatalf("insert legacy Go monotonic row: %v", err)
|
||||
}
|
||||
|
||||
events, err := logger.Query(QueryFilter{ID: "legacy-go-monotonic", Limit: 1})
|
||||
if err != nil {
|
||||
t.Fatalf("Query failed for legacy Go monotonic timestamp row: %v", err)
|
||||
}
|
||||
if len(events) != 1 {
|
||||
t.Fatalf("Expected 1 event, got %d", len(events))
|
||||
}
|
||||
if !events[0].Timestamp.Equal(want) {
|
||||
t.Fatalf("timestamp = %s, want %s", events[0].Timestamp, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSQLiteLoggerCount(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue