baseline: api+gui traffic mode + candidates picker

Snapshot before app-launcher (cgroup/mark) work; ignore binaries/backups.
This commit is contained in:
beckline
2026-02-14 15:32:25 +03:00
parent 50e2999cad
commit 10a10f44a8
55 changed files with 16488 additions and 0 deletions

View File

@@ -0,0 +1,109 @@
package app
import (
"os"
"strconv"
"strings"
"sync"
"time"
)
// ---------------------------------------------------------------------
// события / event bus
// ---------------------------------------------------------------------
// EN: In-memory bounded event bus used for SSE replay and polling watchers.
// EN: It keeps only the latest N events and assigns monotonically increasing IDs.
// RU: Ограниченная in-memory шина событий для SSE-реплея и фоновых вотчеров.
// RU: Хранит только последние N событий и присваивает монотонно растущие ID.
type eventBus struct {
mu sync.Mutex
cond *sync.Cond
buf []Event
cap int
next int64
}
// ---------------------------------------------------------------------
// EN: `newEventBus` creates a new instance for event bus.
// RU: `newEventBus` - создает новый экземпляр для event bus.
// ---------------------------------------------------------------------
func newEventBus(capacity int) *eventBus {
if capacity < 16 {
capacity = 16
}
b := &eventBus{
cap: capacity,
buf: make([]Event, 0, capacity),
}
b.cond = sync.NewCond(&b.mu)
return b
}
// ---------------------------------------------------------------------
// EN: `push` contains core logic for push.
// RU: `push` - содержит основную логику для push.
// ---------------------------------------------------------------------
func (b *eventBus) push(kind string, data interface{}) Event {
b.mu.Lock()
defer b.mu.Unlock()
b.next++
evt := Event{
ID: b.next,
Kind: kind,
Ts: time.Now().UTC().Format(time.RFC3339Nano),
Data: data,
}
if len(b.buf) >= b.cap {
b.buf = b.buf[1:]
}
b.buf = append(b.buf, evt)
b.cond.Broadcast()
return evt
}
// ---------------------------------------------------------------------
// EN: `since` contains core logic for since.
// RU: `since` - содержит основную логику для since.
// ---------------------------------------------------------------------
func (b *eventBus) since(id int64) []Event {
b.mu.Lock()
defer b.mu.Unlock()
return b.sinceLocked(id)
}
// ---------------------------------------------------------------------
// EN: `sinceLocked` contains core logic for since locked.
// RU: `sinceLocked` - содержит основную логику для since locked.
// ---------------------------------------------------------------------
func (b *eventBus) sinceLocked(id int64) []Event {
if len(b.buf) == 0 {
return nil
}
var out []Event
for _, ev := range b.buf {
if ev.ID > id {
out = append(out, ev)
}
}
return out
}
// ---------------------------------------------------------------------
// env helpers
// ---------------------------------------------------------------------
// EN: Positive integer env reader with safe default fallback.
// RU: Чтение положительного целого из env с безопасным fallback на дефолт.
func envInt(key string, def int) int {
if v := strings.TrimSpace(os.Getenv(key)); v != "" {
if n, err := strconv.Atoi(v); err == nil && n > 0 {
return n
}
}
return def
}
var events = newEventBus(envInt("SVPN_EVENTS_CAP", defaultEventsCapacity))