Add SQLite message and peer persistence (internal/store)
Each daemon writes to <data-dir>/messages.db on startup. Messages received or sent are stored immediately; duplicate mids (INSERT OR IGNORE) are safe to call multiple times. Peer aliases are upserted on peer_connected and again after hello verification when the real nick is known. Schema - messages(mid UNIQUE, room, from_peer, body, sent_at) — mid is the YAW/2 dedup key added in the proto migration; index on (room, sent_at) for efficient per-room queries. - peers(peer_id PK, alias, last_seen) — cache of every peer ever seen, used to resolve hex ids to names when peers are offline. Wiring - store.Open called in cmd/daemon/main.go, passed to mesh.New. - mesh.Mesh holds *store.Store (nil-safe; persistence is optional). - mesh.SaveMessage called in dispatchPeerMessage (incoming) and ipc CmdSendMessage (outgoing) so the local node's own messages are stored. - mesh.UpdatePeerAlias called after hello verification updates the alias with the verified nick rather than the placeholder short-id. Messages only accumulate from join time forward — no history replay to late-joining peers; each node's view starts from when it connected. test-network.sh: added SQLite verification block that queries each node's DB after the test and prints message + peer counts. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2,10 +2,12 @@
|
||||
package mesh
|
||||
|
||||
import (
|
||||
"log"
|
||||
"sync"
|
||||
|
||||
"github.com/waste-go/internal/crypto"
|
||||
"github.com/waste-go/internal/proto"
|
||||
"github.com/waste-go/internal/store"
|
||||
)
|
||||
|
||||
// PeerConn is a live connection to one peer.
|
||||
@@ -19,9 +21,10 @@ type PeerConn struct {
|
||||
// All methods are safe to call from multiple goroutines.
|
||||
type Mesh struct {
|
||||
Identity *crypto.Identity
|
||||
Store *store.Store // may be nil if persistence is disabled
|
||||
|
||||
mu sync.RWMutex
|
||||
peers map[proto.PeerID]*PeerConn
|
||||
mu sync.RWMutex
|
||||
peers map[proto.PeerID]*PeerConn
|
||||
|
||||
// subscribers receive a copy of every event (fan-out to IPC clients)
|
||||
subMu sync.Mutex
|
||||
@@ -29,9 +32,11 @@ type Mesh struct {
|
||||
}
|
||||
|
||||
// New creates an empty mesh with the given identity.
|
||||
func New(id *crypto.Identity) *Mesh {
|
||||
// Pass a non-nil store to enable message and peer persistence.
|
||||
func New(id *crypto.Identity, st *store.Store) *Mesh {
|
||||
return &Mesh{
|
||||
Identity: id,
|
||||
Store: st,
|
||||
peers: make(map[proto.PeerID]*PeerConn),
|
||||
}
|
||||
}
|
||||
@@ -44,12 +49,38 @@ func (m *Mesh) AddPeer(conn *PeerConn) {
|
||||
m.peers[conn.Info.ID] = conn
|
||||
m.mu.Unlock()
|
||||
|
||||
if m.Store != nil && conn.Info.Alias != "" {
|
||||
if err := m.Store.SavePeer(conn.Info.ID, conn.Info.Alias); err != nil {
|
||||
log.Printf("mesh: store peer %s: %v", conn.Info.ID.Short(), err)
|
||||
}
|
||||
}
|
||||
|
||||
m.emit(proto.IpcMessage{
|
||||
Type: proto.EvtPeerConnected,
|
||||
Peer: &conn.Info,
|
||||
})
|
||||
}
|
||||
|
||||
// SaveMessage persists a chat message if a store is configured.
|
||||
// Duplicate mids are silently dropped.
|
||||
func (m *Mesh) SaveMessage(msg *proto.ChatMessage) {
|
||||
if m.Store == nil || msg == nil {
|
||||
return
|
||||
}
|
||||
if err := m.Store.SaveMessage(msg); err != nil {
|
||||
log.Printf("mesh: store message %s: %v", msg.Mid, err)
|
||||
}
|
||||
}
|
||||
|
||||
// UpdatePeerAlias updates the cached alias for a peer after hello verification.
|
||||
func (m *Mesh) UpdatePeerAlias(id proto.PeerID, alias string) {
|
||||
if m.Store != nil && alias != "" {
|
||||
if err := m.Store.SavePeer(id, alias); err != nil {
|
||||
log.Printf("mesh: update peer alias %s: %v", id.Short(), err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RemovePeer unregisters a peer and notifies subscribers.
|
||||
func (m *Mesh) RemovePeer(id proto.PeerID) {
|
||||
m.mu.Lock()
|
||||
|
||||
@@ -128,6 +128,7 @@ func handleDCMessage(data []byte, from proto.PeerID, localID *crypto.Identity, m
|
||||
conn.Info.PublicKey = hello.ID
|
||||
}
|
||||
m.mu.Unlock()
|
||||
m.UpdatePeerAlias(from, hello.Nick)
|
||||
m.Emit(proto.IpcMessage{
|
||||
Type: proto.EvtSessionReady,
|
||||
PeerID: peerIDPtr(from),
|
||||
@@ -148,6 +149,7 @@ func dispatchPeerMessage(msg proto.PeerMessage, from proto.PeerID, m *Mesh) {
|
||||
switch msg.Type {
|
||||
case proto.MsgChat:
|
||||
if msg.Chat != nil {
|
||||
m.SaveMessage(msg.Chat)
|
||||
m.Emit(proto.IpcMessage{Type: proto.EvtMessageReceived, Message: msg.Chat})
|
||||
}
|
||||
case proto.MsgPeerGossip:
|
||||
|
||||
Reference in New Issue
Block a user