feat(maintenance): add policy executor and config provider
ConfigProvider, PolicyExecutor, DBStatsProvider, scheduler и safety; зависимость robfig/cron/v3. Co-authored-by: Cursor <[email protected]>
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
package maintenance
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultBatchRows = 10000
|
||||
MaxBatchRows = 100000
|
||||
DefaultStatementTimeoutSec = 30
|
||||
)
|
||||
|
||||
var (
|
||||
blockedTableNames = map[string]struct{}{
|
||||
"schema_migrations": {},
|
||||
"tenant": {},
|
||||
"maintenance_policy": {},
|
||||
"maintenance_policy_config_audit": {},
|
||||
}
|
||||
|
||||
sqlForbidden = regexp.MustCompile(`(?i)(;|--|/\*|\b(drop|truncate|insert|update|alter|create|grant|revoke|copy)\b)`)
|
||||
)
|
||||
|
||||
// ValidateTableName ensures table is a safe identifier and not blocked.
|
||||
func ValidateTableName(name string) error {
|
||||
name = strings.TrimSpace(name)
|
||||
if name == "" || !isSafeIdent(name) {
|
||||
return fmt.Errorf("maintenance: invalid table name")
|
||||
}
|
||||
if _, blocked := blockedTableNames[strings.ToLower(name)]; blocked {
|
||||
return fmt.Errorf("maintenance: table %q is not allowed", name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateCondition ensures the WHERE fragment is safe for parameterized cleanup.
|
||||
func ValidateCondition(condition string) error {
|
||||
c := strings.TrimSpace(condition)
|
||||
if c == "" {
|
||||
return nil
|
||||
}
|
||||
if sqlForbidden.MatchString(c) {
|
||||
return fmt.Errorf("maintenance: unsafe condition")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NormalizeBatchLimit clamps delete batch size.
|
||||
func NormalizeBatchLimit(maxRows *int) int {
|
||||
if maxRows == nil || *maxRows <= 0 {
|
||||
return DefaultBatchRows
|
||||
}
|
||||
if *maxRows > MaxBatchRows {
|
||||
return MaxBatchRows
|
||||
}
|
||||
return *maxRows
|
||||
}
|
||||
|
||||
func isSafeIdent(name string) bool {
|
||||
if name == "" {
|
||||
return false
|
||||
}
|
||||
for _, r := range name {
|
||||
if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9') || r == '_' {
|
||||
continue
|
||||
}
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
Reference in New Issue
Block a user