mirror of
https://gitee.com/samwaf/SamWaf.git
synced 2026-10-31 18:21:51 +08:00
* fix: make archived log shard connections concurrency-safe and recoverable * fix: build the WebSocket online table before its consumers start * perf: cap stored payloads, index log lookups by IP and rule, batch IP tag writes * fix: keep archived log shards readable when the live table gains a column * feat: merge IP tag history when their database location changes * refactor: move request payloads out of the access log into their own table * feat: scope log database export to a time range and selected tiers * feat: add an IP watchlist that full-captures watched IPs * feat: split log storage into security events, access rows, and payload tiers * feat: roll up requests per actor and path into daily analysis tables * fix:actor key by ip * feat: add a source and path analysis page over the daily rollups * feat: exclude chosen source IPs from web log recording * docs: add upgrade notes for the analysis page and log IP exclusion * feat: derive the log partition from the time range and look up identifiers across all of them * feat: add time partition verbs and the period naming they share * feat: cut log archives per month and expire them by dropping partitions * feat: manage log partitions, and answer why an IP has no logs * fix: read archived SQLite log shards read-only and choose tiers by data * fix: count the live log shard from access_log instead of a stale snapshot * fix:export db bug * fix: default backend keep-alive pool (512/128/90s) to stop connect-close churn under load * chore: gen_swagger.bat outputs json only, syncs doc site, runs route test * feat: pin web frontend version and embed it into the binary
62 lines
1.4 KiB
Go
62 lines
1.4 KiB
Go
package global
|
|
|
|
import (
|
|
"SamWaf/model"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// GWAF_IP_WATCH 重点 IP 观察名单的内存缓存。写入路径每条日志都要问一次,不能逐条查库;
|
|
// 名单是人工维护的、条数很小,全量放内存,定期重载兜底,增删后由服务层调 Reload 立即生效。
|
|
var GWAF_IP_WATCH = &ipWatchCache{ips: map[string]int64{}}
|
|
|
|
const ipWatchReloadInterval = 30 * time.Second
|
|
|
|
type ipWatchCache struct {
|
|
mu sync.RWMutex
|
|
ips map[string]int64 // ip -> 到期 unix 秒
|
|
loadedAt time.Time
|
|
}
|
|
|
|
// Has 该 IP 当前是否在观察名单内(未到期)。DB 未初始化时一律视为不在。
|
|
func (c *ipWatchCache) Has(ip string) bool {
|
|
if ip == "" {
|
|
return false
|
|
}
|
|
c.reloadIfDue(false)
|
|
c.mu.RLock()
|
|
exp, ok := c.ips[ip]
|
|
c.mu.RUnlock()
|
|
return ok && exp > time.Now().Unix()
|
|
}
|
|
|
|
// Reload 名单增删后立即重载。
|
|
func (c *ipWatchCache) Reload() {
|
|
c.reloadIfDue(true)
|
|
}
|
|
|
|
func (c *ipWatchCache) reloadIfDue(force bool) {
|
|
if GWAF_LOCAL_DB == nil {
|
|
return
|
|
}
|
|
c.mu.RLock()
|
|
due := time.Since(c.loadedAt) > ipWatchReloadInterval
|
|
c.mu.RUnlock()
|
|
if !force && !due {
|
|
return
|
|
}
|
|
var rows []model.IPWatchlist
|
|
if err := GWAF_LOCAL_DB.Select("ip", "expire_at").
|
|
Where("expire_at > ?", time.Now().Unix()).Find(&rows).Error; err != nil {
|
|
return
|
|
}
|
|
m := make(map[string]int64, len(rows))
|
|
for _, r := range rows {
|
|
m[r.IP] = r.ExpireAt
|
|
}
|
|
c.mu.Lock()
|
|
c.ips = m
|
|
c.loadedAt = time.Now()
|
|
c.mu.Unlock()
|
|
}
|