Files
VeillaxandClaude Sonnet 5.5 f521fa8668 Add the Hammerhead control plane
Dashboard and API, accounts (organizations, roles, invitations, 2FA, OIDC
single sign-on, API keys, revocable sessions), an encrypted SQLite database,
resource logins, alerts and /metrics, mail egress management with DKIM keys,
and the .env loader. Split out of the original single repository; depends on
Reef for the shared types. The end-to-end tests that run Hammerhead, Blacktip
and Bamboo together live in e2e/.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-26 02:50:03 -06:00

71 lines
1.8 KiB
Go

package hammerhead
import (
"bufio"
"encoding/json"
"os"
"path/filepath"
"time"
"git.veillax.com/Veillax/reef/model"
)
type AuditEntry struct {
ID int64 `json:"id"`
Time time.Time `json:"time"`
OrgID string `json:"orgId,omitempty"`
Actor string `json:"actor"`
Action string `json:"action"`
Detail string `json:"detail,omitempty"`
}
// logStore is a thin wrapper so the handlers don't care where logs live.
type logStore struct{ db *DB }
func (l *logStore) addAccess(es []model.LogEntry) { l.db.addAccess(es) }
func (l *logStore) addAudit(e AuditEntry) { l.db.addAudit(e) }
func (l *logStore) queryAccess(f accessFilter) []model.LogEntry { return l.db.queryAccess(f) }
// queryAudit returns newest-first entries; org "" selects server-level events.
func (l *logStore) queryAudit(org string, limit int, before int64) []AuditEntry {
return l.db.queryAudit(org, limit, before)
}
func (s *Server) audit(orgID, actor, action, detail string) {
s.logs.addAudit(AuditEntry{Time: time.Now(), OrgID: orgID, Actor: actor, Action: action, Detail: detail})
}
// importLegacyLogs moves logs from the old JSONL files into the database, once.
func (s *Server) importLegacyLogs() {
dir := filepath.Join(s.dir, "logs")
read := func(name string, each func([]byte)) {
path := filepath.Join(dir, name)
f, err := os.Open(path)
if err != nil {
return
}
sc := bufio.NewScanner(f)
sc.Buffer(make([]byte, 1<<20), 1<<20)
for sc.Scan() {
each(append([]byte(nil), sc.Bytes()...))
}
f.Close()
os.Rename(path, path+".migrated")
}
var batch []model.LogEntry
read("access.jsonl", func(b []byte) {
var e model.LogEntry
if json.Unmarshal(b, &e) == nil {
batch = append(batch, e)
}
})
s.db.addAccess(batch)
read("audit.jsonl", func(b []byte) {
var e AuditEntry
if json.Unmarshal(b, &e) == nil {
s.db.addAudit(e)
}
})
}