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>
71 lines
1.8 KiB
Go
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)
|
|
}
|
|
})
|
|
}
|