feat:add mysql

This commit is contained in:
samwaf
2026-05-12 17:00:04 +08:00
parent 14039ed290
commit a5c2e72740
52 changed files with 1189 additions and 476 deletions
+3 -1
View File
@@ -32,7 +32,9 @@ func (t *JsonTime) UnmarshalJSON(data []byte) error {
func (t JsonTime) Value() (driver.Value, error) {
tm := time.Time(t)
//return tm.Format(constant.TimeLayout), nil
if tm.IsZero() {
return nil, nil // MySQL strict mode rejects 0000-00-00; use NULL for unset times
}
return tm, nil
}
+16
View File
@@ -186,6 +186,22 @@ var (
GWAF_PWD_STATDB = "3Y)(27EtO^tK8Bj~STAT" //加密
GWAF_PWD_LOGDB = "3Y)(27EtO^tK8Bj~LOG" //加密
// Database driver: "sqlite" | "mysql" | "sqlserver" (default: sqlite)
GWAF_DB_DRIVER string = "sqlite"
// MySQL connection config (populated from conf/config.yml database.mysql.*)
GWAF_MYSQL_HOST string = "127.0.0.1"
GWAF_MYSQL_PORT int = 3306
GWAF_MYSQL_USER string = "samwaf"
GWAF_MYSQL_PASSWORD string = ""
GWAF_MYSQL_CHARSET string = "utf8mb4"
GWAF_MYSQL_CORE_DB string = "samwaf_core"
GWAF_MYSQL_LOG_DB string = "samwaf_log"
GWAF_MYSQL_STATS_DB string = "samwaf_stats"
GWAF_MYSQL_MAX_OPEN_CONNS int = 50
GWAF_MYSQL_MAX_IDLE_CONNS int = 10
GWAF_MYSQL_CONN_MAX_LIFETIME_MINUTES int = 60
//默认创建的账户和密码
GWAF_DEFAULT_ACCOUNT string = "admin" //默认创建的账户
GWAF_DEFAULT_ACCOUNT_PWD string = "admin868" //默认创建的密码
+4 -1
View File
@@ -51,11 +51,12 @@ require (
google.golang.org/grpc v1.77.0
gopkg.in/natefinch/lumberjack.v2 v2.0.0
gopkg.in/yaml.v3 v3.0.1
gorm.io/gorm v1.26.1
gorm.io/gorm v1.30.0
)
require (
dario.cat/mergo v1.0.0 // indirect
filippo.io/edwards25519 v1.1.0 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5 // indirect
@@ -93,6 +94,7 @@ require (
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.28.0 // indirect
github.com/go-sql-driver/mysql v1.8.1 // indirect
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
github.com/goccy/go-json v0.10.5 // indirect
github.com/goccy/go-yaml v1.18.0 // indirect
@@ -165,6 +167,7 @@ require (
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gorm.io/driver/mysql v1.6.0 // indirect
rsc.io/binaryregexp v0.2.0 // indirect
)
+8
View File
@@ -1,6 +1,8 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
@@ -177,6 +179,8 @@ github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
github.com/go-playground/validator/v10 v10.28.0 h1:Q7ibns33JjyW48gHkuFT91qX48KG0ktULL6FgHdG688=
github.com/go-playground/validator/v10 v10.28.0/go.mod h1:GoI6I1SjPBh9p7ykNE/yj3fFYbyDOpwMn5KXd+m2hUU=
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
@@ -674,8 +678,12 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/mysql v1.6.0 h1:eNbLmNTpPpTOVZi8MMxCi2aaIm0ZpInbORNXDwyLGvg=
gorm.io/driver/mysql v1.6.0/go.mod h1:D/oCC2GWK3M/dqoLxnOlaNKmXz8WNTfcS9y5ovaSqKo=
gorm.io/gorm v1.26.1 h1:ghB2gUI9FkS46luZtn6DLZ0f6ooBJ5IbVej2ENFDjRw=
gorm.io/gorm v1.26.1/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE=
gorm.io/gorm v1.30.0 h1:qbT5aPv1UH8gI99OsRlvDToLxW5zR7FzS9acZDOZcgs=
gorm.io/gorm v1.30.0/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
rsc.io/binaryregexp v0.2.0 h1:HfqmD5MEmC0zvwBuF187nq9mdnXjXsSivRiXN7SmRkE=
+47 -47
View File
@@ -3,55 +3,55 @@ package innerbean
import "strings"
type WebLog struct {
WafInnerDFlag string `json:"waf_inner_dflag"` //日志队列处理方式
HOST string `json:"host"`
URL string `json:"url"`
RawQuery string `json:"raw_query"` //原始URL查询
REFERER string `json:"referer"`
USER_AGENT string `json:"user_agent"`
METHOD string `json:"method"`
HEADER string `json:"header"`
SRC_IP string `json:"src_ip"`
SRC_PORT string `json:"src_port"`
COUNTRY string `json:"country"`
PROVINCE string `json:"province"`
CITY string `json:"city"`
CREATE_TIME string `gorm:"index:idx_weblog_time" json:"create_time"`
WafInnerDFlag string `gorm:"size:10" json:"waf_inner_dflag"` //日志队列处理方式
HOST string `gorm:"size:255" json:"host"`
URL string `gorm:"type:text" json:"url"`
RawQuery string `gorm:"type:text" json:"raw_query"` //原始URL查询
REFERER string `gorm:"type:text" json:"referer"`
USER_AGENT string `gorm:"size:500" json:"user_agent"`
METHOD string `gorm:"size:20" json:"method"`
HEADER string `gorm:"type:text" json:"header"`
SRC_IP string `gorm:"size:64" json:"src_ip"`
SRC_PORT string `gorm:"size:10" json:"src_port"`
COUNTRY string `gorm:"size:100" json:"country"`
PROVINCE string `gorm:"size:100" json:"province"`
CITY string `gorm:"size:100" json:"city"`
CREATE_TIME string `gorm:"size:32;index:idx_weblog_time" json:"create_time"`
CONTENT_LENGTH int64 `json:"content_length"`
RES_CONTENT_LENGTH int64 `json:"res_content_length"` //响应内容大小(字节)
COOKIES string `json:"cookies"`
BODY string `json:"body"`
REQ_UUID string `json:"req_uuid"`
USER_CODE string `json:"user_code" gorm:"index"`
TenantId string `json:"tenant_id" gorm:"index"` //租户ID(主要键)
HOST_CODE string `json:"host_code" ` //主机ID (主要键)
Day int `json:"day"` //日 (主要键)
ACTION string `json:"action"`
RULE string `json:"rule"`
STATUS string `json:"status"` //状态
STATUS_CODE int `json:"status_code"` //状态编码
RES_BODY string `json:"res_body"` //返回信息
POST_FORM string `json:"post_form"` //提交的表单数据
TASK_FLAG int `json:"task_flag" gorm:"default:-1;index"` //任务处理标记 -1 等待处理;1 可以进行处理;2 处理完毕
UNIX_ADD_TIME int64 `json:"unix_add_time" gorm:"index"` //添加日期unix
RISK_LEVEL int `json:"risk_level"` //危险等级 0:正常 1:轻微 2:有害 3:严重 4:特别严重
GUEST_IDENTIFICATION string `json:"guest_identification"` //访客身份识别
IsBot int `json:"is_bot"` //是否是机器人 0 不是机器人 1 机器人
TimeSpent int64 `json:"time_spent"` //用时
NetSrcIp string `json:"net_src_ip"` //获取的原始IP
SrcByteBody []byte `json:"src_byte_body"` //原始body信息
SrcByteResBody []byte `json:"src_byte_res_body"` //返回body bytes信息
WebLogVersion int `json:"web_log_version"` //日志版本信息早期的是空和0,后期实时增加
Scheme string `json:"scheme"` //HTTP 协议
SrcURL []byte `json:"src_url"` //原始url信息
PreCheckCost int64 `json:"pre_check_cost"` // 前置检查耗时(ms)
ForwardCost int64 `json:"forward_cost"` // 转发耗时(ms)
BackendCheckCost int64 `json:"backend_check_cost"` // 后端处理耗时(ms)
ResHeader string `json:"res_header"` // 返回header情况
BodyHash string `json:"body_hash"` // body hash值
LogOnlyMode int `json:"log_only_mode"` //是否只记录日志 1 是 0 不是
IsBalance int `json:"is_balance"` //是否是负载均衡 1 是 0 不是
BalanceInfo string `json:"balance_info"` //负载均衡IP端口信息
COOKIES string `gorm:"type:text" json:"cookies"`
BODY string `gorm:"type:text" json:"body"`
REQ_UUID string `gorm:"size:64" json:"req_uuid"`
USER_CODE string `gorm:"size:64;index" json:"user_code"`
TenantId string `gorm:"size:64;index" json:"tenant_id"` //租户ID(主要键)
HOST_CODE string `gorm:"size:64" json:"host_code"` //主机ID (主要键)
Day int `json:"day"` //日 (主要键)
ACTION string `gorm:"size:100" json:"action"`
RULE string `gorm:"type:text" json:"rule"`
STATUS string `gorm:"size:50" json:"status"` //状态
STATUS_CODE int `json:"status_code"` //状态编码
RES_BODY string `gorm:"type:text" json:"res_body"` //返回信息
POST_FORM string `gorm:"type:text" json:"post_form"` //提交的表单数据
TASK_FLAG int `json:"task_flag" gorm:"default:-1;index"` //任务处理标记 -1 等待处理;1 可以进行处理;2 处理完毕
UNIX_ADD_TIME int64 `json:"unix_add_time" gorm:"index"` //添加日期unix
RISK_LEVEL int `json:"risk_level"` //危险等级 0:正常 1:轻微 2:有害 3:严重 4:特别严重
GUEST_IDENTIFICATION string `gorm:"column:guest_identification;size:191" json:"guest_identification"` //访客身份识别
IsBot int `json:"is_bot"` //是否是机器人 0 不是机器人 1 机器人
TimeSpent int64 `json:"time_spent"` //用时
NetSrcIp string `gorm:"size:64" json:"net_src_ip"` //获取的原始IP
SrcByteBody []byte `json:"src_byte_body"` //原始body信息
SrcByteResBody []byte `json:"src_byte_res_body"` //返回body bytes信息
WebLogVersion int `json:"web_log_version"` //日志版本信息早期的是空和0,后期实时增加
Scheme string `gorm:"size:20" json:"scheme"` //HTTP 协议
SrcURL []byte `json:"src_url"` //原始url信息
PreCheckCost int64 `json:"pre_check_cost"` // 前置检查耗时(ms)
ForwardCost int64 `json:"forward_cost"` // 转发耗时(ms)
BackendCheckCost int64 `json:"backend_check_cost"` // 后端处理耗时(ms)
ResHeader string `gorm:"type:text" json:"res_header"` // 返回header情况
BodyHash string `gorm:"size:100" json:"body_hash"` // body hash值
LogOnlyMode int `json:"log_only_mode"` //是否只记录日志 1 是 0 不是
IsBalance int `json:"is_balance"` //是否是负载均衡 1 是 0 不是
BalanceInfo string `gorm:"size:255" json:"balance_info"` //负载均衡IP端口信息
}
// GetHeaderValue 从HEADER字段中提取指定header的值
+4 -4
View File
@@ -8,10 +8,10 @@ IP Tag 信息
*/
type IPTag struct {
baseorm.BaseOrm
IP string `json:"ip"` //ip
IPTag string `json:"ip_tag"`
Cnt int64 `json:"cnt"` //触发次数
Remarks string `json:"remarks"` //备注
IP string `gorm:"size:64" json:"ip"` //ip
IPTag string `gorm:"size:255" json:"ip_tag"`
Cnt int64 `json:"cnt"` //触发次数
Remarks string `gorm:"size:500" json:"remarks"` //备注
}
type AttackIPTag struct {
TenantID string `json:"tenant_id"`
+13 -13
View File
@@ -6,24 +6,24 @@ import (
type Account struct {
baseorm.BaseOrm
LoginAccount string `json:"login_account"` //登录账号
LoginPassword string `json:"login_password"` //密码md5加密
Role string `json:"role"` //登录角色
Status int `json:"status"` //状态
Remarks string `json:"remarks"` //备注
LoginAccount string `gorm:"size:100" json:"login_account"` //登录账号
LoginPassword string `gorm:"size:255" json:"login_password"` //密码md5加密
Role string `gorm:"size:50" json:"role"` //登录角色
Status int `json:"status"` //状态
Remarks string `gorm:"size:500" json:"remarks"` //备注
}
type AccountLog struct {
baseorm.BaseOrm
LoginAccount string `json:"login_account"` //登录账号
OpType string `json:"op_type"` //操作类型
OpContent string `json:"op_content"` //操作内容
LoginAccount string `gorm:"size:100" json:"login_account"` //登录账号
OpType string `gorm:"size:100" json:"op_type"` //操作类型
OpContent string `gorm:"type:text" json:"op_content"` //操作内容
}
type TokenInfo struct {
baseorm.BaseOrm
LoginAccount string `json:"login_account"` //登录账号
LoginIp string `json:"login_ip"` //登录IP
AccessToken string `json:"access_token" crypto:"aes"` //访问码
DeviceFingerprint string `json:"device_fingerprint"` //设备指纹
LoginType string `json:"login_type"` //登录类型 web/mobile
LoginAccount string `gorm:"size:100" json:"login_account"` //登录账号
LoginIp string `gorm:"size:64" json:"login_ip"` //登录IP
AccessToken string `gorm:"type:text" json:"access_token" crypto:"aes"` //访问码
DeviceFingerprint string `gorm:"size:255" json:"device_fingerprint"` //设备指纹
LoginType string `gorm:"size:50" json:"login_type"` //登录类型 web/mobile
}
+7 -7
View File
@@ -6,14 +6,14 @@ import (
type IPAllowList struct {
baseorm.BaseOrm
HostCode string `json:"host_code"` //网站唯一码(主要键)
Ip string `json:"ip"` //白名单ip
Remarks string `json:"remarks"` //备注
HostCode string `gorm:"size:64" json:"host_code"` //网站唯一码(主要键)
Ip string `gorm:"size:64" json:"ip"` //白名单ip
Remarks string `gorm:"size:500" json:"remarks"` //备注
}
type URLAllowList struct {
baseorm.BaseOrm
HostCode string `json:"host_code"` //网站唯一码(主要键)
CompareType string `json:"compare_type"` //判断类型,包含、开始、结束、完全匹配
Url string `json:"url"` //请求地址
Remarks string `json:"remarks"` //备注
HostCode string `gorm:"size:64" json:"host_code"` //网站唯一码(主要键)
CompareType string `gorm:"size:50" json:"compare_type"` //判断类型,包含、开始、结束、完全匹配
Url string `gorm:"type:text" json:"url"` //请求地址
Remarks string `gorm:"size:500" json:"remarks"` //备注
}
+8 -8
View File
@@ -10,15 +10,15 @@ import "SamWaf/model/baseorm"
type AntiCC struct {
baseorm.BaseOrm
HostCode string `json:"host_code" gorm:"column:host_code"`
HostCode string `json:"host_code" gorm:"column:host_code;size:64"`
Rate int `json:"rate" gorm:"column:rate"`
Limit int `json:"limit" gorm:"column:limit"`
LockIPMinutes int `json:"lock_ip_minutes" gorm:"column:lock_ip_minutes"`
LimitMode string `json:"limit_mode" gorm:"column:limit_mode"` // "rate" 或 "window"
IPMode string `json:"ip_mode" gorm:"column:ip_mode"` // "nic" 网卡模式 或 "proxy" 代理模式
Url string `json:"url"`
IsEnableRule bool `json:"is_enable_rule" gorm:"column:is_enable_rule"` //是否启动规则
RuleContent string `json:"rule_content" gorm:"column:rule_content"` //规则内容
SkipGlobalCC bool `json:"skip_global_cc" gorm:"column:skip_global_cc"` //命中局部CC规则后跳过全局CC检测
Remarks string `json:"remarks"` //备注
LimitMode string `json:"limit_mode" gorm:"column:limit_mode;size:20"` // "rate" 或 "window"
IPMode string `json:"ip_mode" gorm:"column:ip_mode;size:20"` // "nic" 网卡模式 或 "proxy" 代理模式
Url string `gorm:"type:text" json:"url"`
IsEnableRule bool `json:"is_enable_rule" gorm:"column:is_enable_rule"` //是否启动规则
RuleContent string `json:"rule_content" gorm:"column:rule_content;type:text"` //规则内容
SkipGlobalCC bool `json:"skip_global_cc" gorm:"column:skip_global_cc"` //命中局部CC规则后跳过全局CC检测
Remarks string `gorm:"size:500" json:"remarks"` //备注
}
-7
View File
@@ -1,7 +0,0 @@
package model
type Base_config struct {
Id int `gorm:"primary_key" json:" - "` //
Name string `json:"name"` //
Value string `json:"value"` //
}
+5 -5
View File
@@ -7,9 +7,9 @@ import "SamWaf/customtype"
base orm
*/
type BaseOrm struct {
Id string `gorm:"primary_key" json:"id"` //
USER_CODE string `json:"user_code"` // 用户码(主要键)
Tenant_ID string `json:"tenant_id"` //租户ID
CREATE_TIME customtype.JsonTime `json:"create_time"` //创建时间
UPDATE_TIME customtype.JsonTime `json:"update_time"` //更新时间
Id string `gorm:"primaryKey;size:64" json:"id"` //
USER_CODE string `gorm:"size:64" json:"user_code"` // 用户码(主要键)
Tenant_ID string `gorm:"size:64" json:"tenant_id"` //租户ID
CREATE_TIME customtype.JsonTime `json:"create_time"` //创建时间
UPDATE_TIME customtype.JsonTime `json:"update_time"` //更新时间
}
+9 -9
View File
@@ -5,13 +5,13 @@ import "SamWaf/model/baseorm"
// BatchTask 批量任务
type BatchTask struct {
baseorm.BaseOrm
BatchTaskName string `json:"batch_task_name"` //任务名
BatchType string `json:"batch_type"` //任务类型
BatchHostCode string `json:"batch_host_code"` //网站唯一码 是否绑定到某个主机上
BatchSourceType string `json:"batch_source_type"` //来源类型(local,url)
BatchTriggerType string `json:"batch_trigger_type"` //触发类型 定时任务 cron ,手动任务 manual
BatchSource string `json:"batch_source"` //来源内容 路径或者实际的url内容
BatchExecuteMethod string `json:"batch_execute_method"` //任务执行方式 追加,覆盖
BatchExtraConfig string `json:"batch_extra_config"` //额外配置字段(JSON字符串)
Remark string `json:"remark"` //备注
BatchTaskName string `gorm:"size:255" json:"batch_task_name"` //任务名
BatchType string `gorm:"size:50" json:"batch_type"` //任务类型
BatchHostCode string `gorm:"size:64" json:"batch_host_code"` //网站唯一码 是否绑定到某个主机上
BatchSourceType string `gorm:"size:50" json:"batch_source_type"` //来源类型(local,url)
BatchTriggerType string `gorm:"size:50" json:"batch_trigger_type"` //触发类型 定时任务 cron ,手动任务 manual
BatchSource string `gorm:"type:text" json:"batch_source"` //来源内容 路径或者实际的url内容
BatchExecuteMethod string `gorm:"size:100" json:"batch_execute_method"` //任务执行方式 追加,覆盖
BatchExtraConfig string `gorm:"type:text" json:"batch_extra_config"` //额外配置字段(JSON字符串)
Remark string `gorm:"size:500" json:"remark"` //备注
}
+7 -7
View File
@@ -5,11 +5,11 @@ import "SamWaf/model/baseorm"
// BlockingPage 自定义拦截模板界面
type BlockingPage struct {
baseorm.BaseOrm
BlockingPageName string `json:"blocking_page_name"` //自定义拦截模板页面名称
BlockingType string `json:"blocking_type"` //自定义类型 被拦截
AttackType string `json:"attack_type"` //攻击类型 如: cc_attack, sensitive_word, sql_injection等
HostCode string `json:"host_code"` //适用于某个网站唯一码
ResponseCode string `json:"response_code"` //响应代码 默认403
ResponseHeader string `json:"response_header"` //响应Header头信息(JSON)
ResponseContent string `json:"response_content"` //响应内容
BlockingPageName string `gorm:"size:255" json:"blocking_page_name"` //自定义拦截模板页面名称
BlockingType string `gorm:"size:50" json:"blocking_type"` //自定义类型 被拦截
AttackType string `gorm:"size:100" json:"attack_type"` //攻击类型 如: cc_attack, sensitive_word, sql_injection等
HostCode string `gorm:"size:64" json:"host_code"` //适用于某个网站唯一码
ResponseCode string `gorm:"size:10" json:"response_code"` //响应代码 默认403
ResponseHeader string `gorm:"type:text" json:"response_header"` //响应Header头信息(JSON)
ResponseContent string `gorm:"type:text" json:"response_content"` //响应内容
}
+7 -7
View File
@@ -6,15 +6,15 @@ import (
type IPBlockList struct {
baseorm.BaseOrm
HostCode string `json:"host_code"` //网站唯一码(主要键)
Ip string `json:"ip"` //限制ip
Remarks string `json:"remarks"` //备注
HostCode string `gorm:"size:64" json:"host_code"` //网站唯一码(主要键)
Ip string `gorm:"size:64" json:"ip"` //限制ip
Remarks string `gorm:"size:500" json:"remarks"` //备注
}
type URLBlockList struct {
baseorm.BaseOrm
HostCode string `json:"host_code"` //网站唯一码(主要键)
CompareType string `json:"compare_type" form:"compare_type"` //对比方式
Url string `json:"url"` //限制请求地址
Remarks string `json:"remarks"` //备注
HostCode string `gorm:"size:64" json:"host_code"` //网站唯一码(主要键)
CompareType string `gorm:"size:50" json:"compare_type" form:"compare_type"` //对比方式
Url string `gorm:"type:text" json:"url"` //限制请求地址
Remarks string `gorm:"size:500" json:"remarks"` //备注
}
+3 -3
View File
@@ -4,7 +4,7 @@ import "SamWaf/model/baseorm"
type CaServerInfo struct {
baseorm.BaseOrm
CaServerName string `json:"ca_server_name"` //CA服务器名称
CaServerAddress string `json:"ca_server_address"` //CA服务器地址
Remarks string `json:"remarks"` //备注
CaServerName string `gorm:"size:255" json:"ca_server_name"` //CA服务器名称
CaServerAddress string `gorm:"size:500" json:"ca_server_address"` //CA服务器地址
Remarks string `gorm:"size:500" json:"remarks"` //备注
}
+9 -9
View File
@@ -5,13 +5,13 @@ import "SamWaf/model/baseorm"
// 缓存规则
type CacheRule struct {
baseorm.BaseOrm
HostCode string `json:"host_code" gorm:"column:host_code"` // 主机代码
RuleName string `json:"rule_name" gorm:"column:rule_name"` // 规则名称
RuleType int `json:"rule_type" gorm:"column:rule_type"` // 规则类型:1-后缀匹配 ,2-指定目录,3-指定文件
RuleContent string `json:"rule_content" gorm:"column:rule_content"` // 规则内容
ParamType int `json:"param_type" gorm:"column:param_type"` // 参数处理:1-忽略参数,2-完整参数
CacheTime int `json:"cache_time" gorm:"column:cache_time"` // 缓存时间(秒):0-不缓存
Priority int `json:"priority" gorm:"column:priority"` // 优先级:数字越大优先级越高
RequestMethod string `json:"request_method" gorm:"column:request_method"` // 请求方式:GET;HEAD;POST等,多个用分号分隔
Remarks string `json:"remarks" gorm:"column:remarks"` // 备注
HostCode string `json:"host_code" gorm:"column:host_code;size:64"` // 主机代码
RuleName string `json:"rule_name" gorm:"column:rule_name;size:255"` // 规则名称
RuleType int `json:"rule_type" gorm:"column:rule_type"` // 规则类型:1-后缀匹配 ,2-指定目录,3-指定文件
RuleContent string `json:"rule_content" gorm:"column:rule_content;type:text"` // 规则内容
ParamType int `json:"param_type" gorm:"column:param_type"` // 参数处理:1-忽略参数,2-完整参数
CacheTime int `json:"cache_time" gorm:"column:cache_time"` // 缓存时间(秒):0-不缓存
Priority int `json:"priority" gorm:"column:priority"` // 优先级:数字越大优先级越高
RequestMethod string `json:"request_method" gorm:"column:request_method;size:20"` // 请求方式:GET;HEAD;POST等,多个用分号分隔
Remarks string `json:"remarks" gorm:"column:remarks;size:500"` // 备注
}
+12 -12
View File
@@ -11,16 +11,16 @@ import (
*/
type Center struct {
baseorm.BaseOrm
ClientServerName string `json:"client_server_name"` // 客户端-自定义名称
ClientUserCode string `json:"client_user_code"` // 客户端-用户码
ClientTenantId string `json:"client_tenant_id"` // 客户端-租户ID
ClientToken string `json:"client_token"` // 客户端-访问密钥
ClientSsl string `json:"client_ssl"` // 客户端-是否https
ClientIP string `json:"client_ip"` //客户端 ip
ClientPort string `json:"client_port"` //客户端 port
ClientNewVersion string `json:"client_new_version"` //客户端 版本号
ClientNewVersionDesc string `json:"client_new_version_desc"` //客户端 版本描述
ClientSystemType string `json:"client_system_type"` //操作系统类型
LastVisitTime customtype.JsonTime `json:"last_visit_time"` //上次访问时间
Remarks string `json:"remarks"` //备注
ClientServerName string `gorm:"size:255" json:"client_server_name"` // 客户端-自定义名称
ClientUserCode string `gorm:"size:64" json:"client_user_code"` // 客户端-用户码
ClientTenantId string `gorm:"size:64" json:"client_tenant_id"` // 客户端-租户ID
ClientToken string `gorm:"size:500" json:"client_token"` // 客户端-访问密钥
ClientSsl string `gorm:"size:10" json:"client_ssl"` // 客户端-是否https
ClientIP string `gorm:"size:64" json:"client_ip"` //客户端 ip
ClientPort string `gorm:"size:10" json:"client_port"` //客户端 port
ClientNewVersion string `gorm:"size:64" json:"client_new_version"` //客户端 版本号
ClientNewVersionDesc string `gorm:"type:text" json:"client_new_version_desc"` //客户端 版本描述
ClientSystemType string `gorm:"size:50" json:"client_system_type"` //操作系统类型
LastVisitTime customtype.JsonTime `json:"last_visit_time"` //上次访问时间
Remarks string `gorm:"size:500" json:"remarks"` //备注
}
+12 -12
View File
@@ -13,16 +13,16 @@ import (
// - "core" → GWAF_LOCAL_DB(核心库,预留)
type DataRetentionPolicy struct {
baseorm.BaseOrm
TableName string `json:"table_name"` // 目标表名,如 stats_ip_days
DbType string `json:"db_type"` // 归属库类型:stats / log / core
RetainDays int64 `json:"retain_days"` // 保留天数,0=不按天清理
RetainRows int64 `json:"retain_rows"` // 保留行数,0=不按行清理
DayField string `json:"day_field"` // 天数清理依赖的字段名,如 "day" 或 "create_time"
DayFieldType string `json:"day_field_type"` // 天数字段类型:int_day(YYYYMMDD整数) / datetime(时间类型)
RowOrderField string `json:"row_order_field"` // 行数清理的排序字段,如 "day"、"update_time"、"cnt"
RowOrderDir string `json:"row_order_dir"` // 排序方向:DESC=保留值最大的(最新),ASC=保留值最小的
CleanEnabled int64 `json:"clean_enabled"` // 1=启用 0=禁用
LastCleanTime customtype.JsonTime `json:"last_clean_time"` // 上次清理时间
LastCleanRows int64 `json:"last_clean_rows"` // 上次清理的行数
Remarks string `json:"remarks"` // 备注
TableName string `gorm:"size:100" json:"table_name"` // 目标表名,如 stats_ip_days
DbType string `gorm:"size:20" json:"db_type"` // 归属库类型:stats / log / core
RetainDays int64 `json:"retain_days"` // 保留天数,0=不按天清理
RetainRows int64 `json:"retain_rows"` // 保留行数,0=不按行清理
DayField string `gorm:"size:100" json:"day_field"` // 天数清理依赖的字段名,如 "day" 或 "create_time"
DayFieldType string `gorm:"size:50" json:"day_field_type"` // 天数字段类型:int_day(YYYYMMDD整数) / datetime(时间类型)
RowOrderField string `gorm:"size:100" json:"row_order_field"` // 行数清理的排序字段,如 "day"、"update_time"、"cnt"
RowOrderDir string `gorm:"size:20" json:"row_order_dir"` // 排序方向:DESC=保留值最大的(最新),ASC=保留值最小的
CleanEnabled int64 `json:"clean_enabled"` // 1=启用 0=禁用
LastCleanTime customtype.JsonTime `json:"last_clean_time"` // 上次清理时间
LastCleanRows int64 `json:"last_clean_rows"` // 上次清理的行数
Remarks string `gorm:"size:500" json:"remarks"` // 备注
}
+3 -3
View File
@@ -10,7 +10,7 @@ import (
*/
type DelayMsg struct {
baseorm.BaseOrm
DelayType string `json:"delay_type"` //操作类型
DelayTile string `json:"delay_title"` //操作标题
DelayContent string `json:"delay_content"` //操作内容
DelayType string `gorm:"size:50" json:"delay_type"` //操作类型
DelayTile string `gorm:"size:255" json:"delay_title"` //操作标题
DelayContent string `gorm:"type:text" json:"delay_content"` //操作内容
}
+7 -7
View File
@@ -7,13 +7,13 @@ import (
// FirewallIPBlock 防火墙IP封禁记录表(操作系统级别的防火墙封禁)
type FirewallIPBlock struct {
baseorm.BaseOrm
HostCode string `json:"host_code"` // 网站唯一码(主要键)
IP string `json:"ip"` // 被封禁的IP地址,支持单个IP或CIDR格式
Reason string `json:"reason"` // 封禁原因
BlockType string `json:"block_type"` // 封禁类型:manual-手动封禁, auto-自动封禁, temp-临时封禁
Status string `json:"status"` // 状态:active-已生效, inactive-已失效, pending-待生效
ExpireTime int64 `json:"expire_time"` // 过期时间(时间戳,0表示永久)
Remarks string `json:"remarks"` // 备注
HostCode string `gorm:"size:64" json:"host_code"` // 网站唯一码(主要键)
IP string `gorm:"size:64" json:"ip"` // 被封禁的IP地址,支持单个IP或CIDR格式
Reason string `gorm:"type:text" json:"reason"` // 封禁原因
BlockType string `gorm:"size:32" json:"block_type"` // 封禁类型:manual-手动封禁, auto-自动封禁, temp-临时封禁
Status string `gorm:"size:16" json:"status"` // 状态:active-已生效, inactive-已失效, pending-待生效
ExpireTime int64 `json:"expire_time"` // 过期时间(时间戳,0表示永久)
Remarks string `gorm:"size:500" json:"remarks"` // 备注
}
// TableName 表名
+42 -42
View File
@@ -7,48 +7,48 @@ import (
type Hosts struct {
baseorm.BaseOrm
Code string `json:"code"` //唯一码
Host string `json:"host"` //域名
Port int `json:"port"` //端口
Ssl int `json:"ssl"` //是否是ssl
GUARD_STATUS int `json:"guard_status"` //防御状态 1 是开启防御 0 是防御关闭
REMOTE_SYSTEM string `json:"remote_system"` //是宝塔 phpstudy等
REMOTE_APP string `json:"remote_app"` //是什么类型的应用
Remote_host string `json:"remote_host"` //远端域名
Remote_port int `json:"remote_port"` //远端端口
Remote_ip string `json:"remote_ip"` //远端指定IP
Certfile string `json:"certfile"` //证书文件
Keyfile string `json:"keyfile"` //密钥文件
REMARKS string `json:"remarks"` //备注
GLOBAL_HOST int `json:"global_host"` //默认全局 1 全局 0非全局
DEFENSE_JSON string `json:"defense_json"` //自身防御 json
START_STATUS int `json:"start_status"` //启动状态 如果是0 启动 ; 如果是1 不启动
EXCLUDE_URL_LOG string `json:"exclude_url_log"` //排除的url开头的数据 换行隔开
IsEnableLoadBalance int `json:"is_enable_load_balance"` //是否激活负载 1 激活 非1 没有激活
LoadBalanceStage int `json:"load_balance_stage"` //负载策略
UnrestrictedPort int `json:"unrestricted_port"` //不限来源匹配端口 0 限制 1,不限制
BindSslId string `json:"bind_ssl_id"` //绑定SSL的ID
AutoJumpHTTPS int `json:"auto_jump_https"` //是否自动跳转https 0 不自动 1 强制80跳转https
BindMoreHost string `json:"bind_more_host"` //绑定多域名
IsTransBackDomain int `json:"is_trans_back_domain"` //是否传递后端域名到后端服务器侧
BindMorePort string `json:"bind_more_port"` //是否绑定多个端口
IsEnableHttpAuthBase int `json:"is_enable_http_auth_base"` //是否 HTTPAuthBase 1 激活 非1 没有激活
HttpAuthBaseType string `json:"http_auth_base_type"` //认证类型 authorization(默认Basic Auth) custom(自定义页面)
HttpAuthPathPrefix string `json:"http_auth_path_prefix"` //HTTP认证路径前缀,用于隐藏系统特征,默认为随机生成
ResponseTimeOut int `json:"response_time_out"` //响应超时时间 默认60秒,为0则无限等待
HealthyJSON string `json:"healthy_json"` //后端健康度检测 json
InsecureSkipVerify int `json:"insecure_skip_verify"` //是否开启后端https证书有效性验证 默认 0 是校验 1 是不校验
CaptchaJSON string `json:"captcha_json"` //验证码配置 json
AntiLeechJSON string `json:"anti_leech_json"` //防盗链配置 json
CacheJSON string `json:"cache_json"` //缓存配置 json
StaticSiteJSON string `json:"static_site_json"` //静态站点配置 json
TransportJSON string `json:"transport_json"` //传输配置 json
DefaultEncoding string `json:"default_encoding"` //默认编码 utf-8 或者 gbk auto字符串自动选择
LogOnlyMode int `json:"log_only_mode"` //仅记录模式 1开启 0关闭
CustomHeadersJSON string `json:"custom_headers_json"` //自定义头信息配置 json
CustomResponseHeadersJSON string `json:"custom_response_headers_json"` //自定义响应头信息配置 json
ResponseCompressJSON string `json:"response_compress_json"` //响应压缩配置 json(Gzip/Brotli/Zstd)
IPMode string `json:"ip_mode"` //IP提取模式: "nic" 网卡模式 或 "proxy" 代理模式
Code string `gorm:"size:64" json:"code"` //唯一码
Host string `gorm:"size:255" json:"host"` //域名
Port int `json:"port"` //端口
Ssl int `json:"ssl"` //是否是ssl
GUARD_STATUS int `json:"guard_status"` //防御状态 1 是开启防御 0 是防御关闭
REMOTE_SYSTEM string `gorm:"size:50" json:"remote_system"` //是宝塔 phpstudy等
REMOTE_APP string `gorm:"size:100" json:"remote_app"` //是什么类型的应用
Remote_host string `gorm:"size:255" json:"remote_host"` //远端域名
Remote_port int `json:"remote_port"` //远端端口
Remote_ip string `gorm:"size:64" json:"remote_ip"` //远端指定IP
Certfile string `gorm:"type:text" json:"certfile"` //证书文件
Keyfile string `gorm:"type:text" json:"keyfile"` //密钥文件
REMARKS string `gorm:"size:500" json:"remarks"` //备注
GLOBAL_HOST int `json:"global_host"` //默认全局 1 全局 0非全局
DEFENSE_JSON string `gorm:"type:text" json:"defense_json"` //自身防御 json
START_STATUS int `json:"start_status"` //启动状态 如果是0 启动 ; 如果是1 不启动
EXCLUDE_URL_LOG string `gorm:"type:text" json:"exclude_url_log"` //排除的url开头的数据 换行隔开
IsEnableLoadBalance int `json:"is_enable_load_balance"` //是否激活负载 1 激活 非1 没有激活
LoadBalanceStage int `json:"load_balance_stage"` //负载策略
UnrestrictedPort int `json:"unrestricted_port"` //不限来源匹配端口 0 限制 1,不限制
BindSslId string `gorm:"size:64" json:"bind_ssl_id"` //绑定SSL的ID
AutoJumpHTTPS int `json:"auto_jump_https"` //是否自动跳转https 0 不自动 1 强制80跳转https
BindMoreHost string `gorm:"type:text" json:"bind_more_host"` //绑定多域名
IsTransBackDomain int `json:"is_trans_back_domain"` //是否传递后端域名到后端服务器侧
BindMorePort string `gorm:"size:255" json:"bind_more_port"` //是否绑定多个端口
IsEnableHttpAuthBase int `json:"is_enable_http_auth_base"` //是否 HTTPAuthBase 1 激活 非1 没有激活
HttpAuthBaseType string `gorm:"size:50" json:"http_auth_base_type"` //认证类型 authorization(默认Basic Auth) custom(自定义页面)
HttpAuthPathPrefix string `gorm:"size:255" json:"http_auth_path_prefix"` //HTTP认证路径前缀,用于隐藏系统特征,默认为随机生成
ResponseTimeOut int `json:"response_time_out"` //响应超时时间 默认60秒,为0则无限等待
HealthyJSON string `gorm:"type:text" json:"healthy_json"` //后端健康度检测 json
InsecureSkipVerify int `json:"insecure_skip_verify"` //是否开启后端https证书有效性验证 默认 0 是校验 1 是不校验
CaptchaJSON string `gorm:"type:text" json:"captcha_json"` //验证码配置 json
AntiLeechJSON string `gorm:"type:text" json:"anti_leech_json"` //防盗链配置 json
CacheJSON string `gorm:"type:text" json:"cache_json"` //缓存配置 json
StaticSiteJSON string `gorm:"type:text" json:"static_site_json"` //静态站点配置 json
TransportJSON string `gorm:"type:text" json:"transport_json"` //传输配置 json
DefaultEncoding string `gorm:"size:20" json:"default_encoding"` //默认编码 utf-8 或者 gbk auto字符串自动选择
LogOnlyMode int `json:"log_only_mode"` //仅记录模式 1开启 0关闭
CustomHeadersJSON string `gorm:"type:text" json:"custom_headers_json"` //自定义头信息配置 json
CustomResponseHeadersJSON string `gorm:"type:text" json:"custom_response_headers_json"` //自定义响应头信息配置 json
ResponseCompressJSON string `gorm:"type:text" json:"response_compress_json"` //响应压缩配置 json(Gzip/Brotli/Zstd)
IPMode string `gorm:"size:20" json:"ip_mode"` //IP提取模式: "nic" 网卡模式 或 "proxy" 代理模式
}
type HostsDefense struct {
+3 -3
View File
@@ -4,7 +4,7 @@ import "SamWaf/model/baseorm"
type HttpAuthBase struct {
baseorm.BaseOrm
HostCode string `json:"host_code"` //网站唯一码(主要键)
UserName string `json:"user_name"` // 用户名
Password string `json:"password"` // 访问密码
HostCode string `gorm:"size:64" json:"host_code"` //网站唯一码(主要键)
UserName string `gorm:"size:255" json:"user_name"` // 用户名
Password string `gorm:"size:255" json:"password"` // 访问密码
}
+4 -4
View File
@@ -9,9 +9,9 @@ import (
*/
type LDPUrl struct {
baseorm.BaseOrm
HostCode string `json:"host_code"` //网站唯一码(主要键)
HostCode string `gorm:"size:64" json:"host_code"` //网站唯一码(主要键)
//CompareCol string `json:"CompareCol"` //判断字段
CompareType string `json:"compare_type"` //判断类型,包含、开始、结束、完全匹配
Url string `json:"url"` //请求地址
Remarks string `json:"remarks"` //备注
CompareType string `gorm:"size:50" json:"compare_type"` //判断类型,包含、开始、结束、完全匹配
Url string `gorm:"type:text" json:"url"` //请求地址
Remarks string `gorm:"size:500" json:"remarks"` //备注
}
+5 -5
View File
@@ -7,9 +7,9 @@ import "SamWaf/model/baseorm"
*/
type LoadBalance struct {
baseorm.BaseOrm
HostCode string `json:"host_code"` //网站唯一码(主要键)
Remote_port int `json:"remote_port"` //远端端口
Remote_ip string `json:"remote_ip"` //远端指定IP
Weight int `json:"weight"` //权重
Remarks string `json:"remarks"` //备注
HostCode string `gorm:"size:64" json:"host_code"` //网站唯一码(主要键)
Remote_port int `json:"remote_port"` //远端端口
Remote_ip string `gorm:"size:64" json:"remote_ip"` //远端指定IP
Weight int `json:"weight"` //权重
Remarks string `gorm:"size:500" json:"remarks"` //备注
}
+8 -8
View File
@@ -10,12 +10,12 @@ import (
*/
type NotifyChannel struct {
baseorm.BaseOrm
Name string `json:"name"` // 渠道名称
Type string `json:"type"` // 渠道类型:dingtalk, feishu, wechat, email等
WebhookURL string `json:"webhook_url"` // Webhook地址
Secret string `json:"secret"` // 密钥(用于签名验证)
AccessToken string `json:"access_token"` // 访问令牌
ConfigJSON string `json:"config_json"` // 额外配置(JSON格式)
Status int `json:"status"` // 状态:1启用,0禁用
Remarks string `json:"remarks"` // 备注
Name string `gorm:"size:255" json:"name"` // 渠道名称
Type string `gorm:"size:50" json:"type"` // 渠道类型:dingtalk, feishu, wechat, email等
WebhookURL string `gorm:"size:500" json:"webhook_url"` // Webhook地址
Secret string `gorm:"size:255" json:"secret"` // 密钥(用于签名验证)
AccessToken string `gorm:"size:500" json:"access_token"` // 访问令牌
ConfigJSON string `gorm:"type:text" json:"config_json"` // 额外配置(JSON格式)
Status int `json:"status"` // 状态:1启用,0禁用
Remarks string `gorm:"size:500" json:"remarks"` // 备注
}
+10 -10
View File
@@ -10,14 +10,14 @@ import (
*/
type NotifyLog struct {
baseorm.BaseOrm
ChannelId string `json:"channel_id"` // 渠道ID
ChannelName string `json:"channel_name"` // 渠道名称
ChannelType string `json:"channel_type"` // 渠道类型
MessageType string `json:"message_type"` // 消息类型
MessageTitle string `json:"message_title"` // 消息标题
MessageContent string `json:"message_content"` // 消息内容
Recipients string `json:"recipients"` // 收件人(仅邮件类型)
Status int `json:"status"` // 发送状态:1成功,0失败
ErrorMsg string `json:"error_msg"` // 错误信息
SendTime string `json:"send_time"` // 发送时间
ChannelId string `gorm:"size:64" json:"channel_id"` // 渠道ID
ChannelName string `gorm:"size:255" json:"channel_name"` // 渠道名称
ChannelType string `gorm:"size:50" json:"channel_type"` // 渠道类型
MessageType string `gorm:"size:100" json:"message_type"` // 消息类型
MessageTitle string `gorm:"size:500" json:"message_title"` // 消息标题
MessageContent string `gorm:"type:text" json:"message_content"` // 消息内容
Recipients string `gorm:"type:text" json:"recipients"` // 收件人(仅邮件类型)
Status int `json:"status"` // 发送状态:1成功,0失败
ErrorMsg string `gorm:"type:text" json:"error_msg"` // 错误信息
SendTime string `gorm:"size:100" json:"send_time"` // 发送时间
}
+6 -6
View File
@@ -10,12 +10,12 @@ import (
*/
type NotifySubscription struct {
baseorm.BaseOrm
ChannelId string `json:"channel_id"` // 关联的渠道ID
MessageType string `json:"message_type"` // 消息类型:user_login, attack_info, weekly_report等
Recipients string `json:"recipients"` // 收件人列表(逗号分隔,主要用于邮件类型)留空则使用渠道默认收件人
Status int `json:"status"` // 状态:1启用,0禁用
FilterJSON string `json:"filter_json"` // 过滤条件(JSON格式)
Remarks string `json:"remarks"` // 备注
ChannelId string `gorm:"size:64" json:"channel_id"` // 关联的渠道ID
MessageType string `gorm:"size:100" json:"message_type"` // 消息类型:user_login, attack_info, weekly_report等
Recipients string `gorm:"type:text" json:"recipients"` // 收件人列表(逗号分隔,主要用于邮件类型)留空则使用渠道默认收件人
Status int `json:"status"` // 状态:1启用,0禁用
FilterJSON string `gorm:"type:text" json:"filter_json"` // 过滤条件(JSON格式)
Remarks string `gorm:"size:500" json:"remarks"` // 备注
}
// 消息类型常量
+6 -6
View File
@@ -8,10 +8,10 @@ import "SamWaf/model/baseorm"
*/
type OneKeyMod struct {
baseorm.BaseOrm
OpSystem string `json:"op_system"` //系统类型
FilePath string `json:"file_path"` //文件路径
BeforeContent string `json:"before_content"` //修改前内容
AfterContent string `json:"after_content"` //修改后内容
IsRestore int `json:"is_restore"` //是否还原
Remarks string `json:"remarks"` //备注
OpSystem string `gorm:"size:50" json:"op_system"` //系统类型
FilePath string `gorm:"size:500" json:"file_path"` //文件路径
BeforeContent string `gorm:"type:text" json:"before_content"` //修改前内容
AfterContent string `gorm:"type:text" json:"after_content"` //修改后内容
IsRestore int `json:"is_restore"` //是否还原
Remarks string `gorm:"size:500" json:"remarks"` //备注
}
+9 -9
View File
@@ -10,13 +10,13 @@ import (
*/
type OPlatformKey struct {
baseorm.BaseOrm
KeyName string `json:"key_name"` //Key 名称
ApiKey string `json:"api_key"` //API Key(自动生成,用于 X-API-Key 请求头鉴权)
Status int `json:"status"` //状态 1启用 0禁用
Remark string `json:"remark"` //备注
RateLimit int64 `json:"rate_limit"` //每分钟限流次数,0不限
IPWhitelist string `json:"ip_whitelist"` //IP白名单,逗号分隔,空不限
ExpireTime string `json:"expire_time"` //过期时间,空不过期
LastUseTime string `json:"last_use_time"` //最后使用时间
CallCount int64 `json:"call_count"` //累计调用次数
KeyName string `gorm:"size:255" json:"key_name"` //Key 名称
ApiKey string `gorm:"size:255" json:"api_key"` //API Key(自动生成,用于 X-API-Key 请求头鉴权)
Status int `json:"status"` //状态 1启用 0禁用
Remark string `gorm:"size:500" json:"remark"` //备注
RateLimit int64 `json:"rate_limit"` //每分钟限流次数,0不限
IPWhitelist string `gorm:"type:text" json:"ip_whitelist"` //IP白名单,逗号分隔,空不限
ExpireTime string `gorm:"size:50" json:"expire_time"` //过期时间,空不过期
LastUseTime string `gorm:"size:50" json:"last_use_time"` //最后使用时间
CallCount int64 `json:"call_count"` //累计调用次数
}
+10 -10
View File
@@ -10,14 +10,14 @@ import (
*/
type OPlatformLog struct {
baseorm.BaseOrm
ApiKeyId string `json:"api_key_id"` //关联的Key ID
KeyName string `json:"key_name"` //Key名称(冗余,方便查询)
RequestPath string `json:"request_path"` //请求路径
RequestMethod string `json:"request_method"` //请求方法
RequestBody string `json:"request_body"` //请求体(非文件类型记录)
ResponseBody string `json:"response_body"` //响应体
ClientIP string `json:"client_ip"` //调用者IP
StatusCode int `json:"status_code"` //响应状态码
Duration int64 `json:"duration"` //耗时(ms)
TimeStr string `json:"time_str"` //调用时间字符串
ApiKeyId string `gorm:"size:64" json:"api_key_id"` //关联的Key ID
KeyName string `gorm:"size:255" json:"key_name"` //Key名称(冗余,方便查询)
RequestPath string `gorm:"size:500" json:"request_path"` //请求路径
RequestMethod string `gorm:"size:20" json:"request_method"` //请求方法
RequestBody string `gorm:"type:text" json:"request_body"` //请求体(非文件类型记录)
ResponseBody string `gorm:"type:text" json:"response_body"` //响应体
ClientIP string `gorm:"size:64" json:"client_ip"` //调用者IP
StatusCode int `json:"status_code"` //响应状态码
Duration int64 `json:"duration"` //耗时(ms)
TimeStr string `gorm:"size:100" json:"time_str"` //调用时间字符串
}
+5 -5
View File
@@ -4,9 +4,9 @@ import "SamWaf/model/baseorm"
type Otp struct {
baseorm.BaseOrm
UserName string `json:"user_name"` //用户名
Url string `json:"url"` //URL
Secret string `json:"secret"` //密钥
Issuer string `json:"issuer"` //发行者标识
Remarks string `json:"remarks"` //备注
UserName string `gorm:"size:255" json:"user_name"` //用户名
Url string `gorm:"size:500" json:"url"` //URL
Secret string `gorm:"size:255" json:"secret"` //密钥
Issuer string `gorm:"size:255" json:"issuer"` //发行者标识
Remarks string `gorm:"size:500" json:"remarks"` //备注
}
+2 -2
View File
@@ -4,6 +4,6 @@ import "SamWaf/model/baseorm"
type PrivateGroup struct {
baseorm.BaseOrm
PrivateGroupName string `json:"private_group_name"` //分组名称
PrivateGroupBelongCloud string `json:"private_group_belong_cloud"` //分组归属云
PrivateGroupName string `gorm:"size:255" json:"private_group_name"` //分组名称
PrivateGroupBelongCloud string `gorm:"size:100" json:"private_group_belong_cloud"` //分组归属云
}
+5 -5
View File
@@ -4,9 +4,9 @@ import "SamWaf/model/baseorm"
type PrivateInfo struct {
baseorm.BaseOrm
PrivateKey string `json:"private_key"` //密钥key
PrivateValue string `json:"private_value"` //密钥值
PrivateGroupName string `json:"private_group_name"` //分组名称
PrivateGroupBelongCloud string `json:"private_group_belong_cloud"` //分组归属云
Remarks string `json:"remarks"` //备注
PrivateKey string `gorm:"size:255" json:"private_key"` //密钥key
PrivateValue string `gorm:"type:text" json:"private_value"` //密钥值
PrivateGroupName string `gorm:"size:255" json:"private_group_name"` //分组名称
PrivateGroupBelongCloud string `gorm:"size:100" json:"private_group_belong_cloud"` //分组归属云
Remarks string `gorm:"size:500" json:"remarks"` //备注
}
+10 -10
View File
@@ -6,14 +6,14 @@ import (
type Rules struct {
baseorm.BaseOrm
HostCode string `json:"host_code"` //主机唯一码
RuleCode string `json:"rule_code"` //规则的唯一码
RuleName string `json:"rule_name"` //规则名称
RuleContent string `json:"rule_content"` //规则内容
RuleContentJSON string `json:"rule_content_json"` //规则JSON内容
RuleVersionName string `json:"rule_version_name"` //规则版本名
RuleVersion int `json:"rule_version"` //规则版本号
IsPublicRule int `json:"is_public_rule"` //是否为公共规则
IsManualRule int `json:"is_manual_rule"` //是否为手工写规则 1:手工编写 0 :UI界面形式
RuleStatus int `json:"rule_status"` //规则是否开启 1,开启 0,关闭不生效 999 删除
HostCode string `gorm:"size:64" json:"host_code"` //主机唯一码
RuleCode string `gorm:"size:64" json:"rule_code"` //规则的唯一码
RuleName string `gorm:"size:255" json:"rule_name"` //规则名称
RuleContent string `gorm:"type:text" json:"rule_content"` //规则内容
RuleContentJSON string `gorm:"type:text" json:"rule_content_json"` //规则JSON内容
RuleVersionName string `gorm:"size:100" json:"rule_version_name"` //规则版本名
RuleVersion int `json:"rule_version"` //规则版本号
IsPublicRule int `json:"is_public_rule"` //是否为公共规则
IsManualRule int `json:"is_manual_rule"` //是否为手工写规则 1:手工编写 0 :UI界面形式
RuleStatus int `json:"rule_status"` //规则是否开启 1,开启 0,关闭不生效 999 删除
}
+4 -4
View File
@@ -4,8 +4,8 @@ import "SamWaf/model/baseorm"
type Sensitive struct {
baseorm.BaseOrm
CheckDirection string `json:"check_direction"` //敏感词检测方向 in ,out ,all
Action string `json:"action"` //敏感词检测后动作 deny,replace
Content string `json:"content"` //内容
Remarks string `json:"remarks"` //备注
CheckDirection string `gorm:"size:50" json:"check_direction"` //敏感词检测方向 in ,out ,all
Action string `gorm:"size:50" json:"action"` //敏感词检测后动作 deny,replace
Content string `gorm:"type:text" json:"content"` //内容
Remarks string `gorm:"size:500" json:"remarks"` //备注
}
+5 -5
View File
@@ -12,11 +12,11 @@ import (
*/
type ShareDb struct {
baseorm.BaseOrm
DbLogicType string `json:"db_logic_type"` //数据库逻辑类型 默认:"log"
StartTime customtype.JsonTime `json:"start_time"` //开始时间
EndTime customtype.JsonTime `json:"end_time"` //结束时间
FileName string `json:"file_name"` //文件名:SQLite 下为 .db 文件名;MySQL 下为表名(无后缀)
Cnt int64 `json:"cnt"` //当前数量
DbLogicType string `gorm:"size:50" json:"db_logic_type"` //数据库逻辑类型 默认:"log"
StartTime customtype.JsonTime `json:"start_time"` //开始时间
EndTime customtype.JsonTime `json:"end_time"` //结束时间
FileName string `gorm:"size:255" json:"file_name"` //文件名:SQLite 下为 .db 文件名;MySQL 下为表名(无后缀)
Cnt int64 `json:"cnt"` //当前数量
}
// IsTableShard 在 MySQL/SQL Server 模式下返回 true(FileName 存的是表名而非文件名)
+10 -10
View File
@@ -13,16 +13,16 @@ import (
type SslConfig struct {
baseorm.BaseOrm
CertContent string `json:"cert_content"` // 证书文件内容
KeyContent string `json:"key_content"` // 密钥文件内容
SerialNo string `json:"serial_no"` // 证书序列号
Subject string `json:"subject"` // 证书主题
Issuer string `json:"issuer"` // 颁发者
ValidFrom time.Time `json:"valid_from"` // 证书有效期开始时间
ValidTo time.Time `json:"valid_to"` // 证书有效期结束时间
Domains string `json:"domains"` // 证书适用的域名
KeyPath string `json:"key_path"` //密钥文件位置
CertPath string `json:"cert_path"` //crt文件配置
CertContent string `gorm:"type:text" json:"cert_content"` // 证书文件内容
KeyContent string `gorm:"type:text" json:"key_content"` // 密钥文件内容
SerialNo string `gorm:"size:255" json:"serial_no"` // 证书序列号
Subject string `gorm:"size:500" json:"subject"` // 证书主题
Issuer string `gorm:"size:500" json:"issuer"` // 颁发者
ValidFrom time.Time `json:"valid_from"` // 证书有效期开始时间
ValidTo time.Time `json:"valid_to"` // 证书有效期结束时间
Domains string `gorm:"type:text" json:"domains"` // 证书适用的域名
KeyPath string `gorm:"size:500" json:"key_path"` //密钥文件位置
CertPath string `gorm:"size:500" json:"cert_path"` //crt文件配置
}
// ExpirationMessage 获取到期提示信息
+6 -6
View File
@@ -8,12 +8,12 @@ import (
type SslExpire struct {
baseorm.BaseOrm
Domain string `json:"domain"` // 证书适用的域名
Port int `json:"port"` // 端口
ValidTo time.Time `json:"valid_to"` // 证书有效期结束时间
VisitLog string `json:"visit_log"` //访问日志
LastDetect time.Time `json:"last_detect"` // 最后检测时间
Status string `json:"status"` //状态
Domain string `gorm:"size:255" json:"domain"` // 证书适用的域名
Port int `json:"port"` // 端口
ValidTo time.Time `json:"valid_to"` // 证书有效期结束时间
VisitLog string `gorm:"size:500" json:"visit_log"` //访问日志
LastDetect time.Time `json:"last_detect"` // 最后检测时间
Status string `gorm:"size:50" json:"status"` //状态
}
// ExpirationMessage 获取到期提示信息
+20 -20
View File
@@ -10,26 +10,26 @@ import (
// SslOrder 证书申请
type SslOrder struct {
baseorm.BaseOrm
HostCode string `json:"host_code"` //网站唯一码(主要键)
ApplyPlatform string `json:"apply_platform"` //申请平台
ApplyMethod string `json:"apply_method"` //申请方式http01,dns
SkipDNSVerify int64 `json:"skip_dns_verify"` //dns01是否跳过本地DNS传播校验 0否 1是
ApplyDns string `json:"apply_dns"` //申请dns服务商
ApplyEmail string `json:"apply_email"` //申请邮箱
ApplyKey string `json:"apply_key"` //申请私钥
ApplyDomain string `json:"apply_domain"` //申请域名
ApplyStatus string `json:"apply_status"` //申请状态 提交,完成,已续签
ResultError string `json:"result_error"` //申请错误的详情
ResultDomain string `json:"result_domain"` //返回结果信息
ResultCertURL string `json:"result_cert_url"` //证书连接位置
ResultCertStableURL string `json:"result_cert_stable_url"` //证书固定连接位置
ResultPrivateKey []byte `json:"result_private_key"` //证书私钥信息
ResultCertificate []byte `json:"result_certificate"` //证书信息
ResultIssuerCertificate []byte `json:"result_issuer_certificate"` //证书所属信息
ResultValidTo time.Time `json:"result_valid_to"` //证书有效期结束时间
ResultCSR []byte `json:"result_csr"` //csr信息
Remarks string `json:"remarks"` //备注信息
PrivateGroupName string `json:"private_group_name"` //密钥分组名称DNS有效
HostCode string `gorm:"size:64" json:"host_code"` //网站唯一码(主要键)
ApplyPlatform string `gorm:"size:100" json:"apply_platform"` //申请平台
ApplyMethod string `gorm:"size:50" json:"apply_method"` //申请方式http01,dns
SkipDNSVerify int64 `json:"skip_dns_verify"` //dns01是否跳过本地DNS传播校验 0否 1是
ApplyDns string `gorm:"size:100" json:"apply_dns"` //申请dns服务商
ApplyEmail string `gorm:"size:255" json:"apply_email"` //申请邮箱
ApplyKey string `gorm:"size:255" json:"apply_key"` //申请私钥
ApplyDomain string `gorm:"size:255" json:"apply_domain"` //申请域名
ApplyStatus string `gorm:"size:50" json:"apply_status"` //申请状态 提交,完成,已续签
ResultError string `gorm:"type:text" json:"result_error"` //申请错误的详情
ResultDomain string `gorm:"size:255" json:"result_domain"` //返回结果信息
ResultCertURL string `gorm:"size:500" json:"result_cert_url"` //证书连接位置
ResultCertStableURL string `gorm:"size:500" json:"result_cert_stable_url"` //证书固定连接位置
ResultPrivateKey []byte `json:"result_private_key"` //证书私钥信息
ResultCertificate []byte `json:"result_certificate"` //证书信息
ResultIssuerCertificate []byte `json:"result_issuer_certificate"` //证书所属信息
ResultValidTo time.Time `json:"result_valid_to"` //证书有效期结束时间
ResultCSR []byte `json:"result_csr"` //csr信息
Remarks string `gorm:"size:500" json:"remarks"` //备注信息
PrivateGroupName string `gorm:"size:255" json:"private_group_name"` //密钥分组名称DNS有效
}
// ExpirationMessage 获取到期提示信息
+37 -37
View File
@@ -22,11 +22,11 @@ type StatsTotal struct {
*/
type StatsDay struct {
baseorm.BaseOrm
HostCode string `json:"host_code"` //网站唯一码(主要键)
Day int `json:"day"` //年月日(主要键)
Host string `json:"host"` //域名
Type string `json:"type"` //类型 放行,阻止
Count int `json:"count"` //数量
HostCode string `gorm:"size:64" json:"host_code"` //网站唯一码(主要键)
Day int `json:"day"` //年月日(主要键)
Host string `gorm:"size:255" json:"host"` //域名
Type string `gorm:"size:32" json:"type"` //类型 放行,阻止
Count int `json:"count"` //数量
}
/*
@@ -35,12 +35,12 @@ type StatsDay struct {
*/
type StatsIPDay struct {
baseorm.BaseOrm
HostCode string `json:"host_code"` //网站唯一码(主要键)
Day int `json:"day"` //年月日(主要键)
Host string `json:"host"` //域名
IP string `json:"ip"` //ip
Type string `json:"type"` //类型 放行,阻止
Count int `json:"count"` //数量
HostCode string `gorm:"size:64" json:"host_code"` //网站唯一码(主要键)
Day int `json:"day"` //年月日(主要键)
Host string `gorm:"size:255" json:"host"` //域名
IP string `gorm:"size:64" json:"ip"` //ip
Type string `gorm:"size:32" json:"type"` //类型 放行,阻止
Count int `json:"count"` //数量
}
/*
@@ -49,14 +49,14 @@ type StatsIPDay struct {
*/
type StatsIPCityDay struct {
baseorm.BaseOrm
HostCode string `json:"host_code"` //网站唯一码(主要键)
Day int `json:"day"` //年月日(主要键)
Host string `json:"host"` //域名
Country string `json:"country"` //国家
Province string `json:"province"` //省份
City string `json:"city"` //城市
Type string `json:"type"` //类型 放行,阻止
Count int `json:"count"` //数量
HostCode string `gorm:"size:64" json:"host_code"` //网站唯一码(主要键)
Day int `json:"day"` //年月日(主要键)
Host string `gorm:"size:255" json:"host"` //域名
Country string `gorm:"size:100" json:"country"` //国家
Province string `gorm:"size:100" json:"province"` //省份
City string `gorm:"size:100" json:"city"` //城市
Type string `gorm:"size:32" json:"type"` //类型 放行,阻止
Count int `json:"count"` //数量
}
/*
@@ -95,15 +95,15 @@ type StatsIPCountMore struct {
*/
type StatsSiteDay struct {
baseorm.BaseOrm
HostCode string `json:"host_code"` //网站唯一码(主要键)
Day int `json:"day"` //年月日(主要键)如 20260310
Host string `json:"host"` //域名
TotalCount int64 `json:"total_count"` //总请求数(PV)
AttackCount int64 `json:"attack_count"` //攻击拦截数
NormalCount int64 `json:"normal_count"` //正常放行数
TrafficIn int64 `json:"traffic_in"` //入站流量(bytes)
TrafficOut int64 `json:"traffic_out"` //出站流量(bytes)
TotalTimeSpent int64 `json:"total_time_spent"` //总响应时间(ms),用于计算平均值
HostCode string `gorm:"size:64" json:"host_code"` //网站唯一码(主要键)
Day int `json:"day"` //年月日(主要键)如 20260310
Host string `gorm:"size:255" json:"host"` //域名
TotalCount int64 `json:"total_count"` //总请求数(PV)
AttackCount int64 `json:"attack_count"` //攻击拦截数
NormalCount int64 `json:"normal_count"` //正常放行数
TrafficIn int64 `json:"traffic_in"` //入站流量(bytes)
TrafficOut int64 `json:"traffic_out"` //出站流量(bytes)
TotalTimeSpent int64 `json:"total_time_spent"` //总响应时间(ms),用于计算平均值
}
/*
@@ -112,15 +112,15 @@ type StatsSiteDay struct {
*/
type StatsSiteHour struct {
baseorm.BaseOrm
HostCode string `json:"host_code"` //网站唯一码(主要键)
HourTime int64 `json:"hour_time"` //整点unix时间戳(秒)(主要键)
Host string `json:"host"` //域名
TotalCount int64 `json:"total_count"` //总请求数
AttackCount int64 `json:"attack_count"` //攻击拦截数
NormalCount int64 `json:"normal_count"` //正常放行数
TrafficIn int64 `json:"traffic_in"` //入站流量(bytes)
TrafficOut int64 `json:"traffic_out"` //出站流量(bytes)
TotalTimeSpent int64 `json:"total_time_spent"` //总响应时间(ms)
HostCode string `gorm:"size:64" json:"host_code"` //网站唯一码(主要键)
HourTime int64 `json:"hour_time"` //整点unix时间戳(秒)(主要键)
Host string `gorm:"size:255" json:"host"` //域名
TotalCount int64 `json:"total_count"` //总请求数
AttackCount int64 `json:"attack_count"` //攻击拦截数
NormalCount int64 `json:"normal_count"` //正常放行数
TrafficIn int64 `json:"traffic_in"` //入站流量(bytes)
TrafficOut int64 `json:"traffic_out"` //出站流量(bytes)
TotalTimeSpent int64 `json:"total_time_spent"` //总响应时间(ms)
}
/*
+8 -8
View File
@@ -10,12 +10,12 @@ import (
*/
type SystemConfig struct {
baseorm.BaseOrm
IsSystem string `json:"is_system"` //是否是系统值
ItemClass string `json:"item_class"` //所属分类
Item string `json:"item"`
Value string `json:"value"`
ItemType string `json:"item_type"` //配置类型 普通字符串,可选项
Options string `json:"options"` //如果ItemType == 可选项 这个地方有数据
HashInfo string `json:"hash_info"`
Remarks string `json:"remarks"` //备注
IsSystem string `gorm:"size:10" json:"is_system"` //是否是系统值
ItemClass string `gorm:"size:100" json:"item_class"` //所属分类
Item string `gorm:"size:255" json:"item"`
Value string `gorm:"type:text" json:"value"`
ItemType string `gorm:"size:50" json:"item_type"` //配置类型 普通字符串,可选项
Options string `gorm:"type:text" json:"options"` //如果ItemType == 可选项 这个地方有数据
HashInfo string `gorm:"size:255" json:"hash_info"`
Remarks string `gorm:"size:500" json:"remarks"` //备注
}
+6 -6
View File
@@ -4,10 +4,10 @@ import "SamWaf/model/baseorm"
type Task struct {
baseorm.BaseOrm
TaskName string `json:"task_name"` // 任务名称
TaskUnit string `json:"task_unit"` // 单位
TaskValue int `json:"task_value"` // 值
TaskAt string `json:"task_at"` // 特定时间
TaskMethod string `json:"task_method"` //关联方法
TaskDaysOfTheWeek string `json:"task_days_of_the_week"` // 周几 在周级别的情况下才起作用
TaskName string `gorm:"size:255" json:"task_name"` // 任务名称
TaskUnit string `gorm:"size:50" json:"task_unit"` // 单位
TaskValue int `json:"task_value"` // 值
TaskAt string `gorm:"size:100" json:"task_at"` // 特定时间
TaskMethod string `gorm:"size:255" json:"task_method"` //关联方法
TaskDaysOfTheWeek string `gorm:"size:100" json:"task_days_of_the_week"` // 周几 在周级别的情况下才起作用
}
+21 -21
View File
@@ -7,25 +7,25 @@ import (
// Tunnel 隧道
type Tunnel struct {
baseorm.BaseOrm
Code string `json:"code"` //唯一码
Name string `json:"name"` //名称
Port string `json:"port"` //端口可以多个,号隔开
Protocol string `json:"protocol"` //协议 tcp,udp
RemotePort int `json:"remote_port"` //远端端口
RemoteIp string `json:"remote_ip"` //远端指定IP
AllowIp string `json:"allow_ip"` //允许访问的IP ,号隔开
DenyIp string `json:"deny_ip"` //拒绝访问的IP ,号隔开
StartStatus int `json:"start_status"` //启动状态 如果是1 启动 ; 如果是0 不启动
ConnTimeout int `json:"conn_timeout"` // 连接超时时间,单位秒 60s
ReadTimeout int `json:"read_timeout"` // 读取超时时间,单位秒 0 表示不限制
WriteTimeout int `json:"write_timeout"` // 写入超时时间,单位秒 0 表示不限制
MaxInConnect int `json:"max_in_connect"` // 最大入站连接数 0 表示不限制
MaxOutConnect int `json:"max_out_connect"` // 最大出站连接数 0 表示不限制
AllowedTimeRanges string `json:"allowed_time_ranges"` // 允许访问的时间段 格式: 08:00-10:00;11:00-12:00 空表示24小时可访问
IpVersion string `json:"ip_version"` // IP版本支持: "ipv4" 仅IPv4, "ipv6" 仅IPv6, "both" 同时支持IPv4和IPv6, 默认为"both"
Remark string `json:"remark"` //备注
SSLStatus int `json:"ssl_status"` // SSL开关 0关闭 1开启
SSLCertificate string `json:"ssl_certificate"` // SSL证书路径
SSLCertificateKey string `json:"ssl_certificate_key"` // SSL密钥路径
SSLProtocols string `json:"ssl_protocols"` // SSL协议版本 如 TLSv1.2 TLSv1.3
Code string `gorm:"size:64" json:"code"` //唯一码
Name string `gorm:"size:255" json:"name"` //名称
Port string `gorm:"size:50" json:"port"` //端口可以多个,号隔开
Protocol string `gorm:"size:20" json:"protocol"` //协议 tcp,udp
RemotePort int `json:"remote_port"` //远端端口
RemoteIp string `gorm:"size:64" json:"remote_ip"` //远端指定IP
AllowIp string `gorm:"type:text" json:"allow_ip"` //允许访问的IP ,号隔开
DenyIp string `gorm:"type:text" json:"deny_ip"` //拒绝访问的IP ,号隔开
StartStatus int `json:"start_status"` //启动状态 如果是1 启动 ; 如果是0 不启动
ConnTimeout int `json:"conn_timeout"` // 连接超时时间,单位秒 60s
ReadTimeout int `json:"read_timeout"` // 读取超时时间,单位秒 0 表示不限制
WriteTimeout int `json:"write_timeout"` // 写入超时时间,单位秒 0 表示不限制
MaxInConnect int `json:"max_in_connect"` // 最大入站连接数 0 表示不限制
MaxOutConnect int `json:"max_out_connect"` // 最大出站连接数 0 表示不限制
AllowedTimeRanges string `gorm:"size:255" json:"allowed_time_ranges"` // 允许访问的时间段 格式: 08:00-10:00;11:00-12:00 空表示24小时可访问
IpVersion string `gorm:"size:20" json:"ip_version"` // IP版本支持: "ipv4" 仅IPv4, "ipv6" 仅IPv6, "both" 同时支持IPv4和IPv6, 默认为"both"
Remark string `gorm:"size:500" json:"remark"` //备注
SSLStatus int `json:"ssl_status"` // SSL开关 0关闭 1开启
SSLCertificate string `gorm:"size:500" json:"ssl_certificate"` // SSL证书路径
SSLCertificateKey string `gorm:"size:500" json:"ssl_certificate_key"` // SSL密钥路径
SSLProtocols string `gorm:"size:100" json:"ssl_protocols"` // SSL协议版本 如 TLSv1.2 TLSv1.3
}
+2 -2
View File
@@ -6,6 +6,6 @@ import (
type WafSysLog struct {
baseorm.BaseOrm
OpType string `json:"op_type"` //操作类型
OpContent string `json:"op_content"` //操作内容
OpType string `gorm:"size:100" json:"op_type"` //操作类型
OpContent string `gorm:"type:text" json:"op_content"` //操作内容
}
+66 -2
View File
@@ -210,13 +210,77 @@ func LoadAndInitConfig() {
return
}
fmt.Printf("%s\tINFO\t config updated\n", currentTime)
configChanged = false
}
fmt.Printf("%s\tINFO\tuser_code:%s ,soft_id:%s\n",
currentTime, global.GWAF_USER_CODE, global.GWAF_TENANT_ID)
// 注册数据库方言(默认 SQLite;Milestone 2 起按 GWAF_DB_DRIVER 分流)
dialect.Register(&dialect.SQLiteDialect{})
// ── 数据库 driver 配置 ──────────────────────────────────────────────
if config.IsSet("database.driver") {
global.GWAF_DB_DRIVER = config.GetString("database.driver")
} else {
config.Set("database.driver", global.GWAF_DB_DRIVER) // 写入默认值 sqlite
configChanged = true
}
if global.GWAF_DB_DRIVER == "mysql" {
// MySQL 连接参数(有默认值,仅在 config.yml 有对应键时才覆盖)
if config.IsSet("database.mysql.host") {
global.GWAF_MYSQL_HOST = config.GetString("database.mysql.host")
}
if config.IsSet("database.mysql.port") {
global.GWAF_MYSQL_PORT = config.GetInt("database.mysql.port")
}
if config.IsSet("database.mysql.user") {
global.GWAF_MYSQL_USER = config.GetString("database.mysql.user")
}
if config.IsSet("database.mysql.password") {
global.GWAF_MYSQL_PASSWORD = config.GetString("database.mysql.password")
}
if config.IsSet("database.mysql.charset") {
global.GWAF_MYSQL_CHARSET = config.GetString("database.mysql.charset")
}
if config.IsSet("database.mysql.core_db") {
global.GWAF_MYSQL_CORE_DB = config.GetString("database.mysql.core_db")
}
if config.IsSet("database.mysql.log_db") {
global.GWAF_MYSQL_LOG_DB = config.GetString("database.mysql.log_db")
}
if config.IsSet("database.mysql.stats_db") {
global.GWAF_MYSQL_STATS_DB = config.GetString("database.mysql.stats_db")
}
if config.IsSet("database.mysql.max_open_conns") {
global.GWAF_MYSQL_MAX_OPEN_CONNS = config.GetInt("database.mysql.max_open_conns")
}
if config.IsSet("database.mysql.max_idle_conns") {
global.GWAF_MYSQL_MAX_IDLE_CONNS = config.GetInt("database.mysql.max_idle_conns")
}
if config.IsSet("database.mysql.conn_max_lifetime_minutes") {
global.GWAF_MYSQL_CONN_MAX_LIFETIME_MINUTES = config.GetInt("database.mysql.conn_max_lifetime_minutes")
}
}
// 只有在配置发生变化时才写入文件(提前写,dialect 注册在下方)
if configChanged {
err := config.WriteConfig()
if err != nil {
fmt.Printf("%s\tERROR\twrite config failed:%v\n", currentTime, err)
return
}
fmt.Printf("%s\tINFO\t config updated\n", currentTime)
configChanged = false // 已写,重置标志
}
// 注册数据库方言
switch global.GWAF_DB_DRIVER {
case "mysql":
dialect.Register(&dialect.MySQLDialect{})
fmt.Printf("%s\tINFO\t数据库驱动: MySQL (%s:%d)\n",
currentTime, global.GWAF_MYSQL_HOST, global.GWAF_MYSQL_PORT)
default:
dialect.Register(&dialect.SQLiteDialect{})
}
}
// UpdateIpWhitelist 更新IP白名单配置
+169
View File
@@ -0,0 +1,169 @@
package dialect
import (
"fmt"
"strings"
"time"
"gorm.io/gorm"
)
// MySQLDialect implements DBDialect for MySQL 8.0+ databases.
type MySQLDialect struct{}
func (d *MySQLDialect) Name() string { return "mysql" }
func (d *MySQLDialect) IsFileBased() bool { return false }
func (d *MySQLDialect) SupportsBackup() bool { return false }
func (d *MySQLDialect) SupportsRepair() bool { return false }
func (d *MySQLDialect) ForceIndexClause(table, idx string) string {
return fmt.Sprintf("%s FORCE INDEX (%s)", table, idx)
}
func (d *MySQLDialect) FormatTimeWithOffset(colExpr string, offsetMin int) string {
sign := "+"
h := offsetMin / 60
m := offsetMin % 60
if offsetMin < 0 {
sign = "-"
h = -h
m = -m
}
tz := fmt.Sprintf("%s%02d:%02d", sign, h, m)
return fmt.Sprintf("DATE_FORMAT(CONVERT_TZ(%s,'+00:00','%s'),'%%Y-%%m-%%d %%H:%%i:%%s')", colExpr, tz)
}
func (d *MySQLDialect) RenameTable(db *gorm.DB, src, dst string) error {
return db.Exec(fmt.Sprintf("RENAME TABLE `%s` TO `%s`", src, dst)).Error
}
func (d *MySQLDialect) ListTables(db *gorm.DB) ([]string, error) {
var tables []string
err := db.Raw(
"SELECT TABLE_NAME FROM information_schema.TABLES " +
"WHERE TABLE_SCHEMA = DATABASE() AND TABLE_TYPE = 'BASE TABLE' ORDER BY TABLE_NAME",
).Scan(&tables).Error
return tables, err
}
func (d *MySQLDialect) TableExists(db *gorm.DB, name string) bool {
var count int64
db.Raw(
"SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ?", name,
).Scan(&count)
return count > 0
}
func (d *MySQLDialect) ColumnInfo(db *gorm.DB, table string) ([]ColumnMeta, error) {
rows, err := db.Raw(
"SELECT ORDINAL_POSITION - 1, COLUMN_NAME, DATA_TYPE, "+
"IF(IS_NULLABLE='NO',1,0), IFNULL(COLUMN_DEFAULT,''), IF(COLUMN_KEY='PRI',1,0) "+
"FROM information_schema.COLUMNS "+
"WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ? ORDER BY ORDINAL_POSITION",
table,
).Rows()
if err != nil {
return nil, err
}
defer rows.Close()
var cols []ColumnMeta
for rows.Next() {
var c ColumnMeta
var notNull, pk int
if err := rows.Scan(&c.Cid, &c.Name, &c.Type, &notNull, &c.DefaultVal, &pk); err == nil {
c.NotNull = notNull == 1
c.PrimaryKey = pk > 0
cols = append(cols, c)
}
}
return cols, nil
}
func (d *MySQLDialect) IndexInfo(db *gorm.DB, table string) ([]IndexMeta, error) {
rows, err := db.Raw(
"SELECT INDEX_NAME, NON_UNIQUE, SEQ_IN_INDEX - 1, COLUMN_NAME "+
"FROM information_schema.STATISTICS "+
"WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ? "+
"ORDER BY INDEX_NAME, SEQ_IN_INDEX",
table,
).Rows()
if err != nil {
return nil, err
}
defer rows.Close()
idxMap := make(map[string]*IndexMeta)
var order []string
for rows.Next() {
var idxName, colName string
var nonUnique, seqNo int
if err := rows.Scan(&idxName, &nonUnique, &seqNo, &colName); err != nil {
continue
}
if _, ok := idxMap[idxName]; !ok {
idxMap[idxName] = &IndexMeta{Name: idxName, Unique: nonUnique == 0}
order = append(order, idxName)
}
idxMap[idxName].Columns = append(idxMap[idxName].Columns, IndexColumnMeta{SeqNo: seqNo, Name: colName})
}
result := make([]IndexMeta, 0, len(order))
for _, name := range order {
result = append(result, *idxMap[name])
}
return result, nil
}
func (d *MySQLDialect) CollectMetrics(db *gorm.DB, name, path string) (*DBMetrics, error) {
if db == nil {
return nil, fmt.Errorf("database connection is nil: %s", name)
}
m := &DBMetrics{
DatabaseName: name,
Timestamp: time.Now(),
}
sqlDB, err := db.DB()
if err != nil {
return nil, fmt.Errorf("get sql.DB failed: %v", err)
}
stats := sqlDB.Stats()
m.ConnectionCount = stats.OpenConnections
m.MaxConnections = stats.MaxOpenConnections
m.IdleConnections = stats.Idle
m.InUseConnections = stats.InUse
db.Raw("SELECT @@VERSION").Scan(&m.EngineVersion)
// Data and index size from information_schema
row := db.Raw(
"SELECT COALESCE(SUM(DATA_LENGTH),0)/1024/1024, COALESCE(SUM(INDEX_LENGTH),0)/1024/1024 " +
"FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE()",
).Row()
_ = row.Scan(&m.DataSizeMB, &m.IndexSizeMB)
return m, nil
}
// BuildMySQLDSN returns a MySQL DSN string. Always includes parseTime=True and loc=Local
// so customtype.JsonTime deserialises correctly.
func BuildMySQLDSN(host string, port int, user, password, dbName, charset string) string {
return fmt.Sprintf(
"%s:%s@tcp(%s:%d)/%s?charset=%s&parseTime=True&loc=Local&timeout=10s&readTimeout=30s&writeTimeout=30s",
user, password, host, port, dbName, charset,
)
}
// BuildMySQLRootDSN returns a DSN that connects to the server without selecting a database,
// used for CREATE DATABASE IF NOT EXISTS.
func BuildMySQLRootDSN(host string, port int, user, password, charset string) string {
return fmt.Sprintf(
"%s:%s@tcp(%s:%d)/?charset=%s&parseTime=True&loc=Local&timeout=10s&readTimeout=30s&writeTimeout=30s",
user, password, host, port, charset,
)
}
// mysqlQuote wraps a MySQL identifier in back-ticks.
func mysqlQuote(name string) string {
return "`" + strings.ReplaceAll(name, "`", "``") + "`"
}
+13
View File
@@ -31,6 +31,9 @@ import (
)
func InitCoreDb(currentDir string) (bool, error) {
if dialect.Get().Name() == "mysql" {
return InitCoreDbMySQL()
}
if currentDir == "" {
currentDir = utils.GetCurrentDir()
}
@@ -120,6 +123,9 @@ func InitCoreDb(currentDir string) (bool, error) {
}
func InitLogDb(currentDir string) (bool, error) {
if dialect.Get().Name() == "mysql" {
return InitLogDbMySQL()
}
if currentDir == "" {
currentDir = utils.GetCurrentDir()
}
@@ -201,6 +207,10 @@ func InitLogDb(currentDir string) (bool, error) {
// 手工切换日志数据源
func InitManaulLogDb(currentDir string, custFileName string) {
if dialect.Get().Name() != "sqlite" {
// MySQL 模式下所有日志写入同一个库,无需手动切换分库
return
}
if currentDir == "" {
currentDir = utils.GetCurrentDir()
}
@@ -248,6 +258,9 @@ func InitManaulLogDb(currentDir string, custFileName string) {
}
func InitStatsDb(currentDir string) (bool, error) {
if dialect.Get().Name() == "mysql" {
return InitStatsDbMySQL()
}
if currentDir == "" {
currentDir = utils.GetCurrentDir()
}
+31 -13
View File
@@ -152,6 +152,22 @@ func RunCoreDBMigrations(db *gorm.DB) error {
)
},
},
// 迁移1b: 同步 MySQL ip_tags 表结构(补全缺失列 + 修复 longtext → varchar,支持建索引)
{
ID: "202511140001b_fix_mysql_varchar",
Migrate: func(tx *gorm.DB) error {
if tx.Dialector.Name() != "mysql" {
return nil
}
zlog.Info("迁移 202511140001b: 同步 ip_tags 表结构")
if err := tx.AutoMigrate(&model.IPTag{}); err != nil {
return fmt.Errorf("同步 ip_tags 表结构失败: %w", err)
}
zlog.Info("迁移 202511140001b: ip_tags 表结构同步完成")
return nil
},
Rollback: func(tx *gorm.DB) error { return nil },
},
// 迁移2: 创建索引(幂等操作)
{
ID: "202511140002_create_core_indexes",
@@ -204,13 +220,16 @@ func RunCoreDBMigrations(db *gorm.DB) error {
}
// 创建索引
if err := tx.Exec("CREATE INDEX IF NOT EXISTS idx_firewall_ip_block_ip ON firewall_ip_block(ip)").Error; err != nil {
if err := safeCreateIndex(tx, "firewall_ip_block", "idx_firewall_ip_block_ip",
"CREATE INDEX IF NOT EXISTS idx_firewall_ip_block_ip ON firewall_ip_block(ip)"); err != nil {
zlog.Warn("创建索引 idx_firewall_ip_block_ip 失败", "error", err.Error())
}
if err := tx.Exec("CREATE INDEX IF NOT EXISTS idx_firewall_ip_block_status ON firewall_ip_block(status)").Error; err != nil {
if err := safeCreateIndex(tx, "firewall_ip_block", "idx_firewall_ip_block_status",
"CREATE INDEX IF NOT EXISTS idx_firewall_ip_block_status ON firewall_ip_block(status)"); err != nil {
zlog.Warn("创建索引 idx_firewall_ip_block_status 失败", "error", err.Error())
}
if err := tx.Exec("CREATE INDEX IF NOT EXISTS idx_firewall_ip_block_expire_time ON firewall_ip_block(expire_time)").Error; err != nil {
if err := safeCreateIndex(tx, "firewall_ip_block", "idx_firewall_ip_block_expire_time",
"CREATE INDEX IF NOT EXISTS idx_firewall_ip_block_expire_time ON firewall_ip_block(expire_time)"); err != nil {
zlog.Warn("创建索引 idx_firewall_ip_block_expire_time 失败", "error", err.Error())
}
@@ -955,8 +974,7 @@ func createCoreIndexes(tx *gorm.DB) error {
zlog.Info("开始创建索引", "index", idx.Name, "sql", idx.SQL)
indexStartTime := time.Now()
if err := tx.Exec(idx.SQL).Error; err != nil {
// 记录详细的错误信息
if err := safeCreateIndex(tx, "ip_tags", idx.Name, idx.SQL); err != nil {
errMsg := fmt.Sprintf("创建索引失败 %s: %v (错误类型: %T)", idx.Name, err, err)
zlog.Error("索引创建失败详情", "index", idx.Name, "error", err.Error(), "sql", idx.SQL)
return fmt.Errorf("%s", errMsg)
@@ -983,7 +1001,7 @@ func cleanupDuplicateIPTags(tx *gorm.DB) error {
FROM ip_tags
GROUP BY user_code, tenant_id, ip, ip_tag
HAVING cnt > 1
)
) AS t
`).Scan(&duplicateCount).Error
if err != nil {
@@ -1019,16 +1037,16 @@ func cleanupDuplicateIPTags(tx *gorm.DB) error {
func dropCoreIndexes(tx *gorm.DB) error {
zlog.Info("开始删除core索引")
indexes := []string{
"uni_iptags_full",
"idx_iptag_ip",
indexes := []struct{ table, name string }{
{"ip_tags", "uni_iptags_full"},
{"ip_tags", "idx_iptag_ip"},
}
for _, indexName := range indexes {
if err := tx.Exec(fmt.Sprintf("DROP INDEX IF EXISTS %s", indexName)).Error; err != nil {
zlog.Warn("删除索引失败(可能不存在)", "index", indexName, "error", err)
for _, idx := range indexes {
if err := safeDropIndex(tx, idx.table, idx.name); err != nil {
zlog.Warn("删除索引失败(可能不存在)", "index", idx.name, "error", err)
} else {
zlog.Info("索引删除成功", "index", indexName)
zlog.Info("索引删除成功", "index", idx.name)
}
}
+42
View File
@@ -0,0 +1,42 @@
package wafdb
import (
"SamWaf/wafdb/dialect"
"fmt"
"strings"
"gorm.io/gorm"
)
// checkIndexExists reports whether indexName exists on tableName.
// Uses GORM's dialect-aware Migrator so it works on SQLite, MySQL, and SQL Server.
func checkIndexExists(db *gorm.DB, tableName, indexName string) bool {
return db.Migrator().HasIndex(tableName, indexName)
}
// safeCreateIndex creates the named index only when it does not already exist.
// createSQL must be a SQLite-style "CREATE [UNIQUE] INDEX IF NOT EXISTS ..." statement;
// for MySQL the "IF NOT EXISTS" clause is stripped before execution because MySQL
// does not support it on CREATE INDEX.
func safeCreateIndex(tx *gorm.DB, table, indexName, createSQL string) error {
if tx.Migrator().HasIndex(table, indexName) {
return nil
}
execSQL := createSQL
if dialect.Get().Name() == "mysql" {
execSQL = strings.Replace(createSQL, "IF NOT EXISTS ", "", 1)
}
return tx.Exec(execSQL).Error
}
// safeDropIndex drops the named index only when it exists.
// MySQL requires the table name in the DROP statement; SQLite uses IF NOT EXISTS.
func safeDropIndex(tx *gorm.DB, table, indexName string) error {
if !tx.Migrator().HasIndex(table, indexName) {
return nil
}
if dialect.Get().Name() == "mysql" {
return tx.Exec(fmt.Sprintf("DROP INDEX `%s` ON `%s`", indexName, table)).Error
}
return tx.Exec(fmt.Sprintf("DROP INDEX IF EXISTS %s", indexName)).Error
}
+45 -4
View File
@@ -71,6 +71,48 @@ func RunLogDBMigrations(db *gorm.DB) error {
)
},
},
// 迁移1b: 同步 MySQL web_logs 表结构(补全缺失列 + 修复 longtext → varchar,支持建索引)
{
ID: "202511140001b_fix_mysql_varchar",
Migrate: func(tx *gorm.DB) error {
if tx.Dialector.Name() != "mysql" {
return nil
}
zlog.Info("迁移 202511140001b: 同步 web_logs 表结构")
if err := tx.AutoMigrate(&innerbean.WebLog{}); err != nil {
return fmt.Errorf("同步 web_logs 表结构失败: %w", err)
}
zlog.Info("迁移 202511140001b: web_logs 表结构同步完成")
return nil
},
Rollback: func(tx *gorm.DB) error { return nil },
},
// 迁移1c: 确保 guest_identification 列存在(兼容旧表列名拼写错误 guest_id_entification)
{
ID: "202511140001c_fix_guest_identification_col",
Migrate: func(tx *gorm.DB) error {
if tx.Dialector.Name() != "mysql" {
return nil
}
// 检查目标列是否已存在
var newColCount int64
tx.Raw("SELECT COUNT(*) FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'web_logs' AND column_name = 'guest_identification'").Scan(&newColCount)
if newColCount > 0 {
zlog.Info("迁移 202511140001c: guest_identification 列已存在,跳过")
return nil
}
// 检查旧拼写错误列 guest_id_entification 是否存在
var oldColCount int64
tx.Raw("SELECT COUNT(*) FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'web_logs' AND column_name = 'guest_id_entification'").Scan(&oldColCount)
if oldColCount > 0 {
zlog.Info("迁移 202511140001c: 将 guest_id_entification 重命名为 guest_identification(保留数据)")
return tx.Exec("ALTER TABLE `web_logs` CHANGE COLUMN `guest_id_entification` `guest_identification` VARCHAR(191) NOT NULL DEFAULT ''").Error
}
zlog.Info("迁移 202511140001c: 添加 guest_identification 列")
return tx.Exec("ALTER TABLE `web_logs` ADD COLUMN `guest_identification` VARCHAR(191) NOT NULL DEFAULT ''").Error
},
Rollback: func(tx *gorm.DB) error { return nil },
},
// 迁移2: 创建索引(幂等操作)
{
ID: "202511140002_create_log_indexes",
@@ -278,7 +320,7 @@ func createLogIndexes(tx *gorm.DB) error {
},
{
Name: "idx_web_guest_id_entification",
SQL: "CREATE INDEX IF NOT EXISTS idx_web_guest_id_entification ON web_logs (guest_id_entification, day, is_bot, host_code)",
SQL: "CREATE INDEX IF NOT EXISTS idx_web_guest_id_entification ON web_logs (guest_identification, day, is_bot, host_code)",
},
}
@@ -286,8 +328,7 @@ func createLogIndexes(tx *gorm.DB) error {
zlog.Info("开始创建索引", "index", idx.Name, "sql", idx.SQL)
indexStartTime := time.Now()
if err := tx.Exec(idx.SQL).Error; err != nil {
// 记录详细的错误信息
if err := safeCreateIndex(tx, "web_logs", idx.Name, idx.SQL); err != nil {
errMsg := fmt.Sprintf("创建索引失败 %s: %v (错误类型: %T)", idx.Name, err, err)
zlog.Error("索引创建失败详情", "index", idx.Name, "error", err.Error(), "sql", idx.SQL)
return fmt.Errorf("%s", errMsg)
@@ -317,7 +358,7 @@ func dropLogIndexes(tx *gorm.DB) error {
}
for _, indexName := range indexes {
if err := tx.Exec(fmt.Sprintf("DROP INDEX IF EXISTS %s", indexName)).Error; err != nil {
if err := safeDropIndex(tx, "web_logs", indexName); err != nil {
zlog.Warn("删除索引失败(可能不存在)", "index", indexName, "error", err)
} else {
zlog.Info("索引删除成功", "index", indexName)
+70 -51
View File
@@ -74,6 +74,27 @@ func RunStatsDBMigrations(db *gorm.DB) error {
)
},
},
// 迁移1b: 同步 MySQL stats 各表结构(补全缺失列 + 修复 longtext → varchar,支持建索引)
{
ID: "202511110001b_fix_mysql_varchar",
Migrate: func(tx *gorm.DB) error {
if tx.Dialector.Name() != "mysql" {
return nil
}
zlog.Info("迁移 202511110001b: 同步 stats 各表结构")
if err := tx.AutoMigrate(
&model.StatsDay{},
&model.StatsIPDay{},
&model.StatsIPCityDay{},
&model.IPTag{},
); err != nil {
return fmt.Errorf("同步 stats 表结构失败: %w", err)
}
zlog.Info("迁移 202511110001b: stats 表结构同步完成")
return nil
},
Rollback: func(tx *gorm.DB) error { return nil },
},
// 迁移2: 创建索引(幂等操作)
{
ID: "202511110002_create_indexes",
@@ -102,12 +123,18 @@ func RunStatsDBMigrations(db *gorm.DB) error {
return fmt.Errorf("创建站点统计表失败: %w", err)
}
zlog.Info("迁移 202603100001: 创建站点统计索引")
sqls := []string{
"CREATE INDEX IF NOT EXISTS idx_stats_site_days_lookup ON stats_site_days (host_code, day)",
"CREATE INDEX IF NOT EXISTS idx_stats_site_hours_lookup ON stats_site_hours (host_code, hour_time)",
siteIdxs := []struct{ name, table, sql string }{
{
"idx_stats_site_days_lookup", "stats_site_days",
"CREATE INDEX IF NOT EXISTS idx_stats_site_days_lookup ON stats_site_days (host_code, day)",
},
{
"idx_stats_site_hours_lookup", "stats_site_hours",
"CREATE INDEX IF NOT EXISTS idx_stats_site_hours_lookup ON stats_site_hours (host_code, hour_time)",
},
}
for _, s := range sqls {
if err := tx.Exec(s).Error; err != nil {
for _, idx := range siteIdxs {
if err := safeCreateIndex(tx, idx.table, idx.name, idx.sql); err != nil {
return fmt.Errorf("创建站点统计索引失败: %w", err)
}
}
@@ -134,19 +161,20 @@ func RunStatsDBMigrations(db *gorm.DB) error {
})
indexes := []struct {
name string
sql string
name string
table string
sql string
}{
{"idx_stats_ip_days_day", "CREATE INDEX IF NOT EXISTS idx_stats_ip_days_day ON stats_ip_days(day)"},
{"idx_stats_ip_city_days_day", "CREATE INDEX IF NOT EXISTS idx_stats_ip_city_days_day ON stats_ip_city_days(day)"},
{"idx_ip_tags_create_time", "CREATE INDEX IF NOT EXISTS idx_ip_tags_create_time ON ip_tags(create_time)"},
{"idx_ip_tags_update_time", "CREATE INDEX IF NOT EXISTS idx_ip_tags_update_time ON ip_tags(update_time)"},
{"idx_stats_ip_days_day", "stats_ip_days", "CREATE INDEX IF NOT EXISTS idx_stats_ip_days_day ON stats_ip_days(day)"},
{"idx_stats_ip_city_days_day", "stats_ip_city_days", "CREATE INDEX IF NOT EXISTS idx_stats_ip_city_days_day ON stats_ip_city_days(day)"},
{"idx_ip_tags_create_time", "ip_tags", "CREATE INDEX IF NOT EXISTS idx_ip_tags_create_time ON ip_tags(create_time)"},
{"idx_ip_tags_update_time", "ip_tags", "CREATE INDEX IF NOT EXISTS idx_ip_tags_update_time ON ip_tags(update_time)"},
}
for _, idx := range indexes {
zlog.Info("创建索引中...", "index", idx.name)
start := time.Now()
if err := ddlDB.Exec(idx.sql).Error; err != nil {
if err := safeCreateIndex(ddlDB, idx.table, idx.name, idx.sql); err != nil {
return fmt.Errorf("创建清理索引 %s 失败: %w", idx.name, err)
}
zlog.Info("索引创建完成", "index", idx.name, "耗时", time.Since(start).String())
@@ -218,20 +246,6 @@ func checkStatsIndexesExist(db *gorm.DB) bool {
return true
}
// checkIndexExists 检查指定表的索引是否存在(SQLite)
func checkIndexExists(db *gorm.DB, tableName, indexName string) bool {
var count int64
// SQLite 查询索引的方法
err := db.Raw("SELECT COUNT(*) FROM sqlite_master WHERE type='index' AND name=? AND tbl_name=?",
indexName, tableName).Scan(&count).Error
if err != nil {
zlog.Error("检查索引失败", "error", err, "table", tableName, "index", indexName)
return false
}
return count > 0
}
// createStatsIndexes 创建所有stats索引(幂等操作)
func createStatsIndexes(tx *gorm.DB) error {
zlog.Info("开始创建stats索引(可能需要几分钟)...")
@@ -243,28 +257,34 @@ func createStatsIndexes(tx *gorm.DB) error {
}
indexes := []struct {
Name string
SQL string
Table string
Name string
SQL string
}{
{
Name: "idx_stats_days_lookup",
SQL: "CREATE INDEX IF NOT EXISTS idx_stats_days_lookup ON stats_days (tenant_id, user_code, host_code, type, day)",
Table: "stats_days",
Name: "idx_stats_days_lookup",
SQL: "CREATE INDEX IF NOT EXISTS idx_stats_days_lookup ON stats_days (tenant_id, user_code, host_code, type, day)",
},
{
Name: "idx_stats_ip_days_lookup",
SQL: "CREATE INDEX IF NOT EXISTS idx_stats_ip_days_lookup ON stats_ip_days (tenant_id, user_code, host_code, ip, type, day)",
Table: "stats_ip_days",
Name: "idx_stats_ip_days_lookup",
SQL: "CREATE INDEX IF NOT EXISTS idx_stats_ip_days_lookup ON stats_ip_days (tenant_id, user_code, host_code, ip, type, day)",
},
{
Name: "idx_stats_ip_city_days_lookup",
SQL: "CREATE INDEX IF NOT EXISTS idx_stats_ip_city_days_lookup ON stats_ip_city_days (tenant_id, user_code, host_code, country, province, city, type, day)",
Table: "stats_ip_city_days",
Name: "idx_stats_ip_city_days_lookup",
SQL: "CREATE INDEX IF NOT EXISTS idx_stats_ip_city_days_lookup ON stats_ip_city_days (tenant_id, user_code, host_code, country, province, city, type, day)",
},
{
Name: "uni_iptags_full",
SQL: "CREATE UNIQUE INDEX IF NOT EXISTS uni_iptags_full ON ip_tags (user_code, tenant_id, ip, ip_tag)",
Table: "ip_tags",
Name: "uni_iptags_full",
SQL: "CREATE UNIQUE INDEX IF NOT EXISTS uni_iptags_full ON ip_tags (user_code, tenant_id, ip, ip_tag)",
},
{
Name: "idx_iptag_ip",
SQL: "CREATE INDEX IF NOT EXISTS idx_iptag_ip ON ip_tags (user_code, tenant_id, ip)",
Table: "ip_tags",
Name: "idx_iptag_ip",
SQL: "CREATE INDEX IF NOT EXISTS idx_iptag_ip ON ip_tags (user_code, tenant_id, ip)",
},
}
@@ -272,8 +292,7 @@ func createStatsIndexes(tx *gorm.DB) error {
zlog.Info("开始创建索引", "index", idx.Name, "sql", idx.SQL)
indexStartTime := time.Now()
if err := tx.Exec(idx.SQL).Error; err != nil {
// 记录详细的错误信息
if err := safeCreateIndex(tx, idx.Table, idx.Name, idx.SQL); err != nil {
errMsg := fmt.Sprintf("创建索引失败 %s: %v (错误类型: %T)", idx.Name, err, err)
zlog.Error("索引创建失败详情", "index", idx.Name, "error", err.Error(), "sql", idx.SQL)
return fmt.Errorf("%s", errMsg)
@@ -300,7 +319,7 @@ func cleanupDuplicateIPTagsInStats(tx *gorm.DB) error {
FROM ip_tags
GROUP BY user_code, tenant_id, ip, ip_tag
HAVING cnt > 1
)
) AS t
`).Scan(&duplicateCount).Error
if err != nil {
@@ -336,19 +355,19 @@ func cleanupDuplicateIPTagsInStats(tx *gorm.DB) error {
func dropStatsIndexes(tx *gorm.DB) error {
zlog.Info("开始删除stats索引")
indexes := []string{
"idx_stats_days_lookup",
"idx_stats_ip_days_lookup",
"idx_stats_ip_city_days_lookup",
"uni_iptags_full",
"idx_iptag_ip",
indexes := []struct{ table, name string }{
{"stats_days", "idx_stats_days_lookup"},
{"stats_ip_days", "idx_stats_ip_days_lookup"},
{"stats_ip_city_days", "idx_stats_ip_city_days_lookup"},
{"ip_tags", "uni_iptags_full"},
{"ip_tags", "idx_iptag_ip"},
}
for _, indexName := range indexes {
if err := tx.Exec(fmt.Sprintf("DROP INDEX IF EXISTS %s", indexName)).Error; err != nil {
zlog.Warn("删除索引失败(可能不存在)", "index", indexName, "error", err)
for _, idx := range indexes {
if err := safeDropIndex(tx, idx.table, idx.name); err != nil {
zlog.Warn("删除索引失败(可能不存在)", "index", idx.name, "error", err)
} else {
zlog.Info("索引删除成功", "index", indexName)
zlog.Info("索引删除成功", "index", idx.name)
}
}
+325
View File
@@ -0,0 +1,325 @@
package wafdb
// MySQL-specific database initialisation.
// This file is compiled for all platforms; the build tag for MySQL driver
// activation is handled via the go.mod dependency on gorm.io/driver/mysql.
import (
"SamWaf/common/uuid"
"SamWaf/common/zlog"
"SamWaf/customtype"
"SamWaf/global"
"SamWaf/innerbean"
"SamWaf/model"
"SamWaf/model/baseorm"
"SamWaf/wafdb/dialect"
"fmt"
"strconv"
"strings"
"time"
mysqldriver "gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)
// mysqlServerInfo holds a parsed MySQL / MariaDB version string.
type mysqlServerInfo struct {
Major int
Minor int
Patch int
IsMariaDB bool
Raw string
}
// detectServerInfo queries @@VERSION from an open GORM connection.
func detectServerInfo(db *gorm.DB) mysqlServerInfo {
var raw string
db.Raw("SELECT @@VERSION").Scan(&raw)
return parseMySQLVersion(raw)
}
// parseMySQLVersion parses strings like:
//
// "8.0.32" → MySQL 8.0
// "5.7.44-log" → MySQL 5.7
// "10.6.12-MariaDB" → MariaDB 10.6
func parseMySQLVersion(version string) mysqlServerInfo {
info := mysqlServerInfo{Raw: version}
info.IsMariaDB = strings.Contains(strings.ToLower(version), "mariadb")
parts := strings.SplitN(version, ".", 3)
if len(parts) >= 1 {
info.Major, _ = strconv.Atoi(leadingDigits(parts[0]))
}
if len(parts) >= 2 {
info.Minor, _ = strconv.Atoi(leadingDigits(parts[1]))
}
if len(parts) >= 3 {
info.Patch, _ = strconv.Atoi(leadingDigits(parts[2]))
}
return info
}
// leadingDigits returns the leading ASCII digit sequence in s.
func leadingDigits(s string) string {
for i := 0; i < len(s); i++ {
if s[i] < '0' || s[i] > '9' {
return s[:i]
}
}
return s
}
// bestCollation returns the most appropriate utf8mb4 collation for this server:
//
// MySQL 8.0+ → utf8mb4_0900_ai_ci (default; best Unicode support)
// MySQL 5.x / MariaDB → utf8mb4_general_ci (universally available)
func (v mysqlServerInfo) bestCollation() string {
if !v.IsMariaDB && v.Major >= 8 {
return "utf8mb4_0900_ai_ci"
}
return "utf8mb4_general_ci"
}
// label returns a human-readable flavour string for log messages.
func (v mysqlServerInfo) label() string {
if v.IsMariaDB {
return fmt.Sprintf("MariaDB %d.%d.%d", v.Major, v.Minor, v.Patch)
}
return fmt.Sprintf("MySQL %d.%d.%d", v.Major, v.Minor, v.Patch)
}
// ensureMySQLDatabase makes sure dbName exists before we open a GORM connection to it.
//
// Strategy:
// 1. Connect to the server without selecting a database, detect the server
// version, pick the best collation for that version, then issue
// CREATE DATABASE IF NOT EXISTS. Succeeds when the user has CREATE privilege.
// 2. If step 1 fails (permission denied, or server refuses the collation for any
// reason), try connecting directly to the target database.
// • Direct connection OK → database already exists; user just lacks CREATE
// privilege — proceed normally.
// • Direct connection fails → database does not exist and cannot be created;
// log a clear hint with the exact SQL the user must run manually.
func ensureMySQLDatabase(dbName string) error {
rootDSN := dialect.BuildMySQLRootDSN(
global.GWAF_MYSQL_HOST, global.GWAF_MYSQL_PORT,
global.GWAF_MYSQL_USER, global.GWAF_MYSQL_PASSWORD,
global.GWAF_MYSQL_CHARSET,
)
// Step 1: connect without a database, detect version, auto-create.
rootDB, connErr := gorm.Open(mysqldriver.Open(rootDSN), &gorm.Config{
Logger: logger.Default.LogMode(logger.Silent),
})
if connErr == nil {
sqlDB, _ := rootDB.DB()
defer sqlDB.Close()
info := detectServerInfo(rootDB)
collation := info.bestCollation()
createSQL := fmt.Sprintf(
"CREATE DATABASE IF NOT EXISTS `%s` CHARACTER SET utf8mb4 COLLATE %s",
dbName, collation,
)
zlog.Info("mysql: 服务器信息", "version", info.label(), "collation", collation)
if createErr := rootDB.Exec(createSQL).Error; createErr == nil {
zlog.Info("mysql: 数据库已就绪(新建或已存在)", "db", dbName)
return nil
} else {
zlog.Warn("mysql: 自动建库失败,尝试直连确认库是否已存在",
"db", dbName, "error", createErr)
}
} else {
zlog.Warn("mysql: 无 database 根连接失败,尝试直连目标库",
"db", dbName, "error", connErr)
}
// Step 2: fall back — try connecting directly to the target database.
directDSN := dialect.BuildMySQLDSN(
global.GWAF_MYSQL_HOST, global.GWAF_MYSQL_PORT,
global.GWAF_MYSQL_USER, global.GWAF_MYSQL_PASSWORD,
dbName, global.GWAF_MYSQL_CHARSET,
)
directDB, directErr := gorm.Open(mysqldriver.Open(directDSN), &gorm.Config{
Logger: logger.Default.LogMode(logger.Silent),
})
if directErr == nil {
directSQLDB, _ := directDB.DB()
directSQLDB.Close()
zlog.Info("mysql: 数据库已存在,跳过自动建库", "db", dbName)
return nil
}
// Step 3: nothing worked — emit actionable guidance and abort.
// Use utf8mb4_general_ci in the hint: it works on all MySQL/MariaDB versions.
hintSQL := fmt.Sprintf(
"CREATE DATABASE IF NOT EXISTS `%s` CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci",
dbName,
)
hint := fmt.Sprintf(
"\n========================================================\n"+
" 数据库 `%s` 不存在,且当前账户无权自动创建。\n"+
" 请用 MySQL root 账户执行以下 SQL,然后重启 SamWaf:\n\n"+
" %s;\n"+
" GRANT ALL PRIVILEGES ON `%s`.* TO '%s'@'%%';\n"+
" FLUSH PRIVILEGES;\n"+
"========================================================",
dbName, hintSQL, dbName, global.GWAF_MYSQL_USER,
)
fmt.Println(hint) // also print to stdout so it's visible even without log viewer
zlog.Error("mysql: 无法连接或创建数据库", "db", dbName, "hint", hint)
return fmt.Errorf("mysql: 数据库 `%s` 不存在且权限不足,请手动建库后重启(详见日志)", dbName)
}
// openMySQLDB opens a GORM connection to the named MySQL database,
// applying connection pool settings from global config.
func openMySQLDB(dbName string) (*gorm.DB, error) {
if err := ensureMySQLDatabase(dbName); err != nil {
return nil, err
}
dsn := dialect.BuildMySQLDSN(
global.GWAF_MYSQL_HOST, global.GWAF_MYSQL_PORT,
global.GWAF_MYSQL_USER, global.GWAF_MYSQL_PASSWORD,
dbName, global.GWAF_MYSQL_CHARSET,
)
db, err := gorm.Open(mysqldriver.Open(dsn), &gorm.Config{})
if err != nil {
return nil, fmt.Errorf("mysql: 打开数据库 %s 失败: %w", dbName, err)
}
sqlDB, _ := db.DB()
sqlDB.SetMaxOpenConns(global.GWAF_MYSQL_MAX_OPEN_CONNS)
sqlDB.SetMaxIdleConns(global.GWAF_MYSQL_MAX_IDLE_CONNS)
sqlDB.SetConnMaxLifetime(time.Duration(global.GWAF_MYSQL_CONN_MAX_LIFETIME_MINUTES) * time.Minute)
// 空闲连接最多存活 3 分钟,远小于 MySQL 默认 wait_timeout(8h),
// 避免 MySQL 主动关闭后 Go 连接池仍持有死连接,导致 "bad connection"
sqlDB.SetConnMaxIdleTime(3 * time.Minute)
return db, nil
}
// applyGormLogger attaches the custom GormZLogger (and debug mode) to db.
func applyGormLogger(db *gorm.DB) *gorm.DB {
gormLogger := NewGormZLogger()
if global.GWAF_LOG_DEBUG_DB_ENABLE {
gormLogger = gormLogger.LogMode(logger.Info).(*GormZLogger)
db = db.Session(&gorm.Session{Logger: gormLogger})
}
return db
}
// InitCoreDbMySQL initialises the MySQL core database.
func InitCoreDbMySQL() (bool, error) {
if global.GWAF_LOCAL_DB != nil {
return false, nil
}
dbName := global.GWAF_MYSQL_CORE_DB
zlog.Info("mysql: 初始化核心数据库", "db", dbName)
db, err := openMySQLDB(dbName)
if err != nil {
return false, err
}
db = applyGormLogger(db)
global.GWAF_LOCAL_DB = db
zlog.Info("开始执行core数据库迁移...")
if err := RunCoreDBMigrations(db); err != nil {
panic("core database migration failed: " + err.Error())
}
zlog.Info("开始执行任务初始化迁移...")
if err := RunTaskInitMigrations(db); err != nil {
panic("task initialization migration failed: " + err.Error())
}
global.GWAF_LOCAL_DB.Callback().Query().Before("gorm:query").Register("tenant_plugin:before_query", before_query)
global.GWAF_LOCAL_DB.Callback().Query().Before("gorm:update").Register("tenant_plugin:before_update", before_update)
db.Where("user_code = ? and rule_status = 999", global.GWAF_USER_CODE).Delete(model.Rules{})
pathCoreSql(db)
return false, nil
}
// InitLogDbMySQL initialises the MySQL log database.
func InitLogDbMySQL() (bool, error) {
if global.GWAF_LOCAL_LOG_DB != nil {
return false, nil
}
dbName := global.GWAF_MYSQL_LOG_DB
zlog.Info("mysql: 初始化日志数据库", "db", dbName)
db, err := openMySQLDB(dbName)
if err != nil {
return false, err
}
db = applyGormLogger(db)
global.GWAF_LOCAL_LOG_DB = db
zlog.Info("开始执行log数据库迁移...")
if err := RunLogDBMigrations(db); err != nil {
panic("log database migration failed: " + err.Error())
}
global.GWAF_LOCAL_LOG_DB.Callback().Query().Before("gorm:query").Register("tenant_plugin:before_query", before_query)
global.GWAF_LOCAL_LOG_DB.Callback().Query().Before("gorm:update").Register("tenant_plugin:before_update", before_update)
pathLogSql(db)
// Ensure a ShareDb entry exists for the MySQL log database.
var total int64
global.GWAF_LOCAL_DB.Model(&model.ShareDb{}).Count(&total)
if total == 0 {
var logTotal int64
global.GWAF_LOCAL_LOG_DB.Model(&innerbean.WebLog{}).Count(&logTotal)
sharDbBean := model.ShareDb{
BaseOrm: baseorm.BaseOrm{
Id: uuid.GenUUID(),
USER_CODE: global.GWAF_USER_CODE,
Tenant_ID: global.GWAF_TENANT_ID,
CREATE_TIME: customtype.JsonTime(time.Now()),
UPDATE_TIME: customtype.JsonTime(time.Now()),
},
DbLogicType: "log",
StartTime: customtype.JsonTime(time.Now()),
EndTime: customtype.JsonTime(time.Now()),
FileName: dbName, // MySQL DB name (no .db extension)
Cnt: logTotal,
}
global.GWAF_LOCAL_DB.Create(sharDbBean)
}
return false, nil
}
// InitStatsDbMySQL initialises the MySQL statistics database.
func InitStatsDbMySQL() (bool, error) {
if global.GWAF_LOCAL_STATS_DB != nil {
return false, nil
}
dbName := global.GWAF_MYSQL_STATS_DB
zlog.Info("mysql: 初始化统计数据库", "db", dbName)
db, err := openMySQLDB(dbName)
if err != nil {
return false, err
}
db = applyGormLogger(db)
global.GWAF_LOCAL_STATS_DB = db
zlog.Info("开始执行stats数据库迁移...")
if err := RunStatsDBMigrations(db); err != nil {
panic("stats database migration failed: " + err.Error())
}
global.GWAF_LOCAL_STATS_DB.Callback().Query().Before("gorm:query").Register("tenant_plugin:before_query", before_query)
global.GWAF_LOCAL_STATS_DB.Callback().Query().Before("gorm:update").Register("tenant_plugin:before_update", before_update)
pathStatsSql(db)
return false, nil
}