From ae2f4d7819ce5c2ca0a4cd1a29f13443ece65b82 Mon Sep 17 00:00:00 2001 From: zenfun Date: Sun, 4 Jan 2026 00:55:00 +0800 Subject: [PATCH] feat(model): add IPBan entity for global IP blocking Introduces the IPBan model to support global IP/CIDR ban rules enforced by the data plane. Includes fields for CIDR, status, expiration, and hit counts, and registers the model for auto-migration in the server startup. --- cmd/server/main.go | 4 ++-- internal/model/ip_ban.go | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 internal/model/ip_ban.go diff --git a/cmd/server/main.go b/cmd/server/main.go index 768f40e..c759c49 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -146,7 +146,7 @@ func main() { // Auto Migrate if logDB != db { - if err := db.AutoMigrate(&model.Master{}, &model.Key{}, &model.ProviderGroup{}, &model.APIKey{}, &model.Model{}, &model.Binding{}, &model.Namespace{}, &model.OperationLog{}, &model.SyncOutbox{}, &model.Alert{}, &model.AlertThresholdConfig{}); err != nil { + if err := db.AutoMigrate(&model.Master{}, &model.Key{}, &model.ProviderGroup{}, &model.APIKey{}, &model.Model{}, &model.Binding{}, &model.Namespace{}, &model.OperationLog{}, &model.SyncOutbox{}, &model.Alert{}, &model.AlertThresholdConfig{}, &model.IPBan{}); err != nil { fatal(logger, "failed to auto migrate", "err", err) } if err := logDB.AutoMigrate(&model.LogRecord{}); err != nil { @@ -156,7 +156,7 @@ func main() { fatal(logger, "failed to ensure log indexes", "err", err) } } else { - if err := db.AutoMigrate(&model.Master{}, &model.Key{}, &model.ProviderGroup{}, &model.APIKey{}, &model.Model{}, &model.Binding{}, &model.Namespace{}, &model.OperationLog{}, &model.LogRecord{}, &model.SyncOutbox{}, &model.Alert{}, &model.AlertThresholdConfig{}); err != nil { + if err := db.AutoMigrate(&model.Master{}, &model.Key{}, &model.ProviderGroup{}, &model.APIKey{}, &model.Model{}, &model.Binding{}, &model.Namespace{}, &model.OperationLog{}, &model.LogRecord{}, &model.SyncOutbox{}, &model.Alert{}, &model.AlertThresholdConfig{}, &model.IPBan{}); err != nil { fatal(logger, "failed to auto migrate", "err", err) } if err := service.EnsureLogIndexes(db); err != nil { diff --git a/internal/model/ip_ban.go b/internal/model/ip_ban.go new file mode 100644 index 0000000..f1a2188 --- /dev/null +++ b/internal/model/ip_ban.go @@ -0,0 +1,38 @@ +package model + +import ( + "time" + + "gorm.io/gorm" +) + +// IPBan represents a global IP/CIDR ban rule. +// IP bans are enforced by the data plane (balancer) before token validation. +type IPBan struct { + gorm.Model + CIDR string `gorm:"size:64;uniqueIndex;not null" json:"cidr"` // Normalized CIDR (IPv4 /32, IPv6 /128) + Status string `gorm:"size:20;default:'active';index" json:"status"` // active, expired + Reason string `gorm:"size:512" json:"reason,omitempty"` // Optional ban reason + ExpiresAt *int64 `gorm:"index" json:"expires_at,omitempty"` // Unix timestamp, nil = permanent + HitCount int64 `gorm:"default:0" json:"hit_count"` // Number of blocked requests (synced from Redis) + CreatedBy string `gorm:"size:128" json:"created_by,omitempty"` // Creator identifier +} + +// IPBanStatus constants +const ( + IPBanStatusActive = "active" + IPBanStatusExpired = "expired" +) + +// IsExpired returns true if the ban has expired. +func (b *IPBan) IsExpired() bool { + if b.ExpiresAt == nil { + return false // permanent ban + } + return time.Now().Unix() >= *b.ExpiresAt +} + +// TableName returns the table name for IPBan. +func (IPBan) TableName() string { + return "ip_bans" +}