Files
browser/plugins/examples/scum-server-plugin/companion/console_storage.go
T

128 lines
4.8 KiB
Go

package companion
import (
"context"
"crypto/sha256"
"encoding/hex"
"fmt"
"strings"
"time"
)
func (store *SCUMSQLStore) StoreConsoleRecords(ctx context.Context, records []ConsoleRecord) (int, error) {
if store == nil || store.db == nil {
return 0, fmt.Errorf("SCUM plugin SQL store is not configured")
}
normalized := make([]ConsoleRecord, 0, len(records))
for _, record := range records {
value, err := normalizeConsoleRecord(record)
if err != nil {
return 0, err
}
normalized = append(normalized, value)
}
if len(normalized) == 0 {
return 0, nil
}
tx, err := store.db.BeginTx(ctx, nil)
if err != nil {
return 0, fmt.Errorf("begin SCUM console log write: %w", err)
}
defer tx.Rollback()
stamp := time.Now().UTC()
for _, record := range normalized {
if _, err := tx.ExecContext(ctx, scumConsoleLogInsertSQL, scumConsoleRecordKey(record), record.ServerID, record.Stream, record.Sequence, record.OccurredAt, record.Text, stamp, stamp); err != nil {
return 0, fmt.Errorf("write SCUM console log: %w", err)
}
}
if err := tx.Commit(); err != nil {
return 0, fmt.Errorf("commit SCUM console logs: %w", err)
}
return len(normalized), nil
}
func (store *SCUMSQLStore) StoreSemanticEventBatch(ctx context.Context, batch SemanticEventBatch) (int, error) {
if store == nil || store.db == nil {
return 0, fmt.Errorf("SCUM plugin SQL store is not configured")
}
normalized := make([]SemanticEvent, 0, len(batch.Events))
for _, event := range batch.Events {
value, err := normalizeSemanticEvent(event)
if err != nil {
return 0, err
}
normalized = append(normalized, value)
}
if len(normalized) == 0 {
return 0, nil
}
tx, err := store.db.BeginTx(ctx, nil)
if err != nil {
return 0, fmt.Errorf("begin SCUM semantic event write: %w", err)
}
defer tx.Rollback()
stamp := time.Now().UTC()
for _, event := range normalized {
if _, err := tx.ExecContext(ctx, scumSemanticEventInsertSQL, scumSemanticEventRecordKey(event), event.ServerID, event.Sequence, event.Type, event.PlayerID, nullText(event.DisplayName), event.OccurredAt, nullText(event.NetworkCorrelation), stamp, stamp); err != nil {
return 0, fmt.Errorf("write SCUM semantic event: %w", err)
}
}
if err := tx.Commit(); err != nil {
return 0, fmt.Errorf("commit SCUM semantic events: %w", err)
}
return len(normalized), nil
}
func normalizeConsoleRecord(record ConsoleRecord) (ConsoleRecord, error) {
record.ServerID = strings.TrimSpace(record.ServerID)
record.Stream = strings.TrimSpace(record.Stream)
record.Text = strings.TrimRight(record.Text, "\r\n")
if record.ServerID == "" || (record.Stream != "stdout" && record.Stream != "stderr") || record.Sequence == 0 || record.OccurredAt.IsZero() || strings.TrimSpace(record.Text) == "" || len(record.Text) > 8192 {
return ConsoleRecord{}, fmt.Errorf("SCUM console record is invalid")
}
record.OccurredAt = record.OccurredAt.UTC()
return record, nil
}
func normalizeSemanticEvent(event SemanticEvent) (SemanticEvent, error) {
event.ServerID = strings.TrimSpace(event.ServerID)
event.Type = strings.TrimSpace(event.Type)
event.PlayerID = strings.TrimSpace(event.PlayerID)
event.DisplayName = strings.TrimSpace(event.DisplayName)
event.NetworkCorrelation = strings.TrimSpace(event.NetworkCorrelation)
if event.ServerID == "" || event.Sequence == 0 || event.Type == "" || event.PlayerID == "" || event.OccurredAt.IsZero() || len(event.Type) > 80 || len(event.PlayerID) > 80 || len(event.DisplayName) > 120 || len(event.NetworkCorrelation) > 128 {
return SemanticEvent{}, fmt.Errorf("SCUM semantic event is invalid")
}
event.OccurredAt = event.OccurredAt.UTC()
return event, nil
}
func scumConsoleRecordKey(record ConsoleRecord) string {
digest := sha256.Sum256([]byte(strings.Join([]string{record.ServerID, record.Stream, fmt.Sprintf("%d", record.Sequence)}, "\x00")))
return hex.EncodeToString(digest[:])
}
func scumSemanticEventRecordKey(event SemanticEvent) string {
digest := sha256.Sum256([]byte(strings.Join([]string{event.ServerID, event.Type, event.PlayerID, fmt.Sprintf("%d", event.Sequence)}, "\x00")))
return hex.EncodeToString(digest[:])
}
const scumConsoleLogInsertSQL = `
INSERT INTO scum_console_logs (
record_key, server_instance_id, stream, sequence, occurred_at, line_text, created_at, updated_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE
occurred_at = VALUES(occurred_at),
line_text = VALUES(line_text),
updated_at = VALUES(updated_at)`
const scumSemanticEventInsertSQL = `
INSERT INTO scum_semantic_events (
record_key, server_instance_id, sequence, event_type, player_id, display_name, occurred_at, network_correlation, created_at, updated_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE
display_name = VALUES(display_name),
occurred_at = VALUES(occurred_at),
network_correlation = VALUES(network_correlation),
updated_at = VALUES(updated_at)`