138 lines
3.2 KiB
Go
138 lines
3.2 KiB
Go
package main
|
|
|
|
import (
|
|
"log"
|
|
|
|
"verstak/internal/core/bridge"
|
|
"verstak/internal/core/browser"
|
|
"verstak/internal/core/config"
|
|
)
|
|
|
|
// startBridge creates and starts the local HTTP bridge for browser extension.
|
|
func (a *App) startBridge(appCfg *config.AppConfig) {
|
|
// Determine bridge config
|
|
bc := a.bridgeConfig(appCfg)
|
|
|
|
handler := func(events []bridge.Event) {
|
|
// Convert to browser events and store in staging.
|
|
be := make([]browser.Event, 0, len(events))
|
|
for _, ev := range events {
|
|
be = append(be, bridgeToBrowser(ev))
|
|
}
|
|
n, err := a.browser.InsertEvents(be)
|
|
if err != nil {
|
|
log.Printf("[bridge] store events: %v", err)
|
|
return
|
|
}
|
|
if n > 0 {
|
|
log.Printf("[bridge] stored %d/%d events", n, len(be))
|
|
}
|
|
}
|
|
|
|
srv := bridge.NewServer(bridge.Config{
|
|
Port: bc.Port,
|
|
Secret: bc.Secret,
|
|
}, handler)
|
|
|
|
port, err := srv.Start(bridge.Config{
|
|
Port: bc.Port,
|
|
AutoGenPort: bc.AutoGenPort,
|
|
Secret: bc.Secret,
|
|
})
|
|
if err != nil {
|
|
log.Printf("[bridge] failed to start: %v", err)
|
|
return
|
|
}
|
|
|
|
// Save the actual port and secret back to config if auto-generated.
|
|
if bc.AutoGenPort {
|
|
bc.Port = port
|
|
}
|
|
if bc.Secret == "" {
|
|
bc.Secret = srv.Secret()
|
|
}
|
|
a.saveBridgeConfig(appCfg, bc)
|
|
|
|
a.mu.Lock()
|
|
a.bridge = srv
|
|
a.mu.Unlock()
|
|
}
|
|
|
|
// bridgeConfig extracts bridge config from app config, generating defaults if needed.
|
|
func (a *App) bridgeConfig(appCfg *config.AppConfig) *config.BridgeConfig {
|
|
if appCfg != nil && appCfg.Vault.Bridge.Port != 0 {
|
|
bc := &appCfg.Vault.Bridge
|
|
// If secret is empty, generate one on first run
|
|
if bc.Secret == "" {
|
|
bc.Secret = bridge.GenerateSecret()
|
|
}
|
|
return bc
|
|
}
|
|
return &config.BridgeConfig{
|
|
Port: 9786,
|
|
AutoGenPort: true,
|
|
Secret: bridge.GenerateSecret(),
|
|
}
|
|
}
|
|
|
|
// saveBridgeConfig persists the bridge config to disk.
|
|
func (a *App) saveBridgeConfig(appCfg *config.AppConfig, bc *config.BridgeConfig) {
|
|
if appCfg == nil {
|
|
// Load or create fresh
|
|
loaded, err := config.LoadAppConfig()
|
|
if err != nil || loaded == nil {
|
|
loaded = config.DefaultAppConfig()
|
|
}
|
|
appCfg = loaded
|
|
}
|
|
appCfg.Vault.Bridge = *bc
|
|
if err := config.SaveAppConfig(appCfg); err != nil {
|
|
log.Printf("[bridge] save config: %v", err)
|
|
}
|
|
}
|
|
|
|
// BridgeInfo returns the current bridge server status.
|
|
func (a *App) BridgeInfo() map[string]interface{} {
|
|
info := map[string]interface{}{
|
|
"running": false,
|
|
"port": 0,
|
|
}
|
|
if a.bridge != nil {
|
|
info["running"] = a.bridge.Running()
|
|
info["port"] = a.bridge.Port()
|
|
info["secret"] = a.bridge.Secret()
|
|
}
|
|
return info
|
|
}
|
|
|
|
// bridgeToBrowser converts a bridge.Event to a browser.Event.
|
|
func bridgeToBrowser(ev bridge.Event) browser.Event {
|
|
deviceID := ev.ID
|
|
if idx := indexOf(ev.ID, "_"); idx > 0 {
|
|
deviceID = ev.ID[:idx]
|
|
}
|
|
return browser.Event{
|
|
ID: ev.ID,
|
|
DeviceID: deviceID,
|
|
Type: ev.Type,
|
|
URL: ev.URL,
|
|
Title: ev.Title,
|
|
Domain: ev.Domain,
|
|
ActiveSeconds: ev.ActiveSeconds,
|
|
TSStart: ev.TSStart,
|
|
TSEnd: ev.TSEnd,
|
|
TS: ev.TS,
|
|
SelectedText: ev.SelectedText,
|
|
Note: ev.Note,
|
|
}
|
|
}
|
|
|
|
func indexOf(s string, sub string) int {
|
|
for i := 0; i < len(s); i++ {
|
|
if string(s[i]) == sub {
|
|
return i
|
|
}
|
|
}
|
|
return -1
|
|
}
|