validation.go 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. package console_setting
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "net/url"
  6. "regexp"
  7. "strings"
  8. "time"
  9. )
  10. // ValidateConsoleSettings 验证控制台设置信息格式
  11. func ValidateConsoleSettings(settingsStr string, settingType string) error {
  12. if settingsStr == "" {
  13. return nil // 空字符串是合法的
  14. }
  15. switch settingType {
  16. case "ApiInfo":
  17. return validateApiInfo(settingsStr)
  18. case "Announcements":
  19. return validateAnnouncements(settingsStr)
  20. case "FAQ":
  21. return validateFAQ(settingsStr)
  22. default:
  23. return fmt.Errorf("未知的设置类型:%s", settingType)
  24. }
  25. }
  26. // validateApiInfo 验证API信息格式
  27. func validateApiInfo(apiInfoStr string) error {
  28. var apiInfoList []map[string]interface{}
  29. if err := json.Unmarshal([]byte(apiInfoStr), &apiInfoList); err != nil {
  30. return fmt.Errorf("API信息格式错误:%s", err.Error())
  31. }
  32. // 验证数组长度
  33. if len(apiInfoList) > 50 {
  34. return fmt.Errorf("API信息数量不能超过50个")
  35. }
  36. // 允许的颜色值
  37. validColors := map[string]bool{
  38. "blue": true, "green": true, "cyan": true, "purple": true, "pink": true,
  39. "red": true, "orange": true, "amber": true, "yellow": true, "lime": true,
  40. "light-green": true, "teal": true, "light-blue": true, "indigo": true,
  41. "violet": true, "grey": true,
  42. }
  43. // URL 正则,支持域名 / IP
  44. urlRegex := regexp.MustCompile(`^https?://(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)*[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?|(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))(?:\:[0-9]{1,5})?(?:/.*)?$`)
  45. for i, apiInfo := range apiInfoList {
  46. urlStr, ok := apiInfo["url"].(string)
  47. if !ok || urlStr == "" {
  48. return fmt.Errorf("第%d个API信息缺少URL字段", i+1)
  49. }
  50. route, ok := apiInfo["route"].(string)
  51. if !ok || route == "" {
  52. return fmt.Errorf("第%d个API信息缺少线路描述字段", i+1)
  53. }
  54. description, ok := apiInfo["description"].(string)
  55. if !ok || description == "" {
  56. return fmt.Errorf("第%d个API信息缺少说明字段", i+1)
  57. }
  58. color, ok := apiInfo["color"].(string)
  59. if !ok || color == "" {
  60. return fmt.Errorf("第%d个API信息缺少颜色字段", i+1)
  61. }
  62. if !urlRegex.MatchString(urlStr) {
  63. return fmt.Errorf("第%d个API信息的URL格式不正确", i+1)
  64. }
  65. if _, err := url.Parse(urlStr); err != nil {
  66. return fmt.Errorf("第%d个API信息的URL无法解析:%s", i+1, err.Error())
  67. }
  68. if len(urlStr) > 500 {
  69. return fmt.Errorf("第%d个API信息的URL长度不能超过500字符", i+1)
  70. }
  71. if len(route) > 100 {
  72. return fmt.Errorf("第%d个API信息的线路描述长度不能超过100字符", i+1)
  73. }
  74. if len(description) > 200 {
  75. return fmt.Errorf("第%d个API信息的说明长度不能超过200字符", i+1)
  76. }
  77. if !validColors[color] {
  78. return fmt.Errorf("第%d个API信息的颜色值不合法", i+1)
  79. }
  80. dangerousChars := []string{"<script", "<iframe", "javascript:", "onload=", "onerror=", "onclick="}
  81. for _, d := range dangerousChars {
  82. lower := strings.ToLower(description)
  83. if strings.Contains(lower, d) || strings.Contains(strings.ToLower(route), d) {
  84. return fmt.Errorf("第%d个API信息包含不允许的内容", i+1)
  85. }
  86. }
  87. }
  88. return nil
  89. }
  90. // ValidateApiInfo 保持向后兼容
  91. func ValidateApiInfo(apiInfoStr string) error {
  92. return validateApiInfo(apiInfoStr)
  93. }
  94. // GetApiInfo 获取 API 信息列表
  95. func GetApiInfo() []map[string]interface{} {
  96. apiInfoStr := GetConsoleSetting().ApiInfo
  97. if apiInfoStr == "" {
  98. return []map[string]interface{}{}
  99. }
  100. var apiInfo []map[string]interface{}
  101. if err := json.Unmarshal([]byte(apiInfoStr), &apiInfo); err != nil {
  102. return []map[string]interface{}{}
  103. }
  104. return apiInfo
  105. }
  106. // ----------------- 公告 / FAQ -----------------
  107. func validateAnnouncements(announcementsStr string) error {
  108. var list []map[string]interface{}
  109. if err := json.Unmarshal([]byte(announcementsStr), &list); err != nil {
  110. return fmt.Errorf("系统公告格式错误:%s", err.Error())
  111. }
  112. if len(list) > 100 {
  113. return fmt.Errorf("系统公告数量不能超过100个")
  114. }
  115. validTypes := map[string]bool{
  116. "default": true, "ongoing": true, "success": true, "warning": true, "error": true,
  117. }
  118. for i, ann := range list {
  119. content, ok := ann["content"].(string)
  120. if !ok || content == "" {
  121. return fmt.Errorf("第%d个公告缺少内容字段", i+1)
  122. }
  123. publishDateAny, exists := ann["publishDate"]
  124. if !exists {
  125. return fmt.Errorf("第%d个公告缺少发布日期字段", i+1)
  126. }
  127. publishDateStr, ok := publishDateAny.(string)
  128. if !ok || publishDateStr == "" {
  129. return fmt.Errorf("第%d个公告的发布日期不能为空", i+1)
  130. }
  131. if _, err := time.Parse(time.RFC3339, publishDateStr); err != nil {
  132. return fmt.Errorf("第%d个公告的发布日期格式错误", i+1)
  133. }
  134. if t, exists := ann["type"]; exists {
  135. if typeStr, ok := t.(string); ok {
  136. if !validTypes[typeStr] {
  137. return fmt.Errorf("第%d个公告的类型值不合法", i+1)
  138. }
  139. }
  140. }
  141. if len(content) > 500 {
  142. return fmt.Errorf("第%d个公告的内容长度不能超过500字符", i+1)
  143. }
  144. if extra, exists := ann["extra"]; exists {
  145. if extraStr, ok := extra.(string); ok && len(extraStr) > 200 {
  146. return fmt.Errorf("第%d个公告的说明长度不能超过200字符", i+1)
  147. }
  148. }
  149. }
  150. return nil
  151. }
  152. func validateFAQ(faqStr string) error {
  153. var list []map[string]interface{}
  154. if err := json.Unmarshal([]byte(faqStr), &list); err != nil {
  155. return fmt.Errorf("FAQ信息格式错误:%s", err.Error())
  156. }
  157. if len(list) > 100 {
  158. return fmt.Errorf("FAQ数量不能超过100个")
  159. }
  160. for i, faq := range list {
  161. question, ok := faq["question"].(string)
  162. if !ok || question == "" {
  163. return fmt.Errorf("第%d个FAQ缺少问题字段", i+1)
  164. }
  165. answer, ok := faq["answer"].(string)
  166. if !ok || answer == "" {
  167. return fmt.Errorf("第%d个FAQ缺少答案字段", i+1)
  168. }
  169. if len(question) > 200 {
  170. return fmt.Errorf("第%d个FAQ的问题长度不能超过200字符", i+1)
  171. }
  172. if len(answer) > 1000 {
  173. return fmt.Errorf("第%d个FAQ的答案长度不能超过1000字符", i+1)
  174. }
  175. }
  176. return nil
  177. }
  178. // GetAnnouncements 获取系统公告
  179. func GetAnnouncements() []map[string]interface{} {
  180. annStr := GetConsoleSetting().Announcements
  181. if annStr == "" {
  182. return []map[string]interface{}{}
  183. }
  184. var ann []map[string]interface{}
  185. _ = json.Unmarshal([]byte(annStr), &ann)
  186. return ann
  187. }
  188. // GetFAQ 获取常见问题
  189. func GetFAQ() []map[string]interface{} {
  190. faqStr := GetConsoleSetting().FAQ
  191. if faqStr == "" {
  192. return []map[string]interface{}{}
  193. }
  194. var faq []map[string]interface{}
  195. _ = json.Unmarshal([]byte(faqStr), &faq)
  196. return faq
  197. }