validation.go 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  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. // GetApiInfo 获取 API 信息列表
  91. func GetApiInfo() []map[string]interface{} {
  92. apiInfoStr := GetConsoleSetting().ApiInfo
  93. if apiInfoStr == "" {
  94. return []map[string]interface{}{}
  95. }
  96. var apiInfo []map[string]interface{}
  97. if err := json.Unmarshal([]byte(apiInfoStr), &apiInfo); err != nil {
  98. return []map[string]interface{}{}
  99. }
  100. return apiInfo
  101. }
  102. // ----------------- 公告 / FAQ -----------------
  103. func validateAnnouncements(announcementsStr string) error {
  104. var list []map[string]interface{}
  105. if err := json.Unmarshal([]byte(announcementsStr), &list); err != nil {
  106. return fmt.Errorf("系统公告格式错误:%s", err.Error())
  107. }
  108. if len(list) > 100 {
  109. return fmt.Errorf("系统公告数量不能超过100个")
  110. }
  111. validTypes := map[string]bool{
  112. "default": true, "ongoing": true, "success": true, "warning": true, "error": true,
  113. }
  114. for i, ann := range list {
  115. content, ok := ann["content"].(string)
  116. if !ok || content == "" {
  117. return fmt.Errorf("第%d个公告缺少内容字段", i+1)
  118. }
  119. publishDateAny, exists := ann["publishDate"]
  120. if !exists {
  121. return fmt.Errorf("第%d个公告缺少发布日期字段", i+1)
  122. }
  123. publishDateStr, ok := publishDateAny.(string)
  124. if !ok || publishDateStr == "" {
  125. return fmt.Errorf("第%d个公告的发布日期不能为空", i+1)
  126. }
  127. if _, err := time.Parse(time.RFC3339, publishDateStr); err != nil {
  128. return fmt.Errorf("第%d个公告的发布日期格式错误", i+1)
  129. }
  130. if t, exists := ann["type"]; exists {
  131. if typeStr, ok := t.(string); ok {
  132. if !validTypes[typeStr] {
  133. return fmt.Errorf("第%d个公告的类型值不合法", i+1)
  134. }
  135. }
  136. }
  137. if len(content) > 500 {
  138. return fmt.Errorf("第%d个公告的内容长度不能超过500字符", i+1)
  139. }
  140. if extra, exists := ann["extra"]; exists {
  141. if extraStr, ok := extra.(string); ok && len(extraStr) > 200 {
  142. return fmt.Errorf("第%d个公告的说明长度不能超过200字符", i+1)
  143. }
  144. }
  145. }
  146. return nil
  147. }
  148. func validateFAQ(faqStr string) error {
  149. var list []map[string]interface{}
  150. if err := json.Unmarshal([]byte(faqStr), &list); err != nil {
  151. return fmt.Errorf("FAQ信息格式错误:%s", err.Error())
  152. }
  153. if len(list) > 100 {
  154. return fmt.Errorf("FAQ数量不能超过100个")
  155. }
  156. for i, faq := range list {
  157. question, ok := faq["question"].(string)
  158. if !ok || question == "" {
  159. return fmt.Errorf("第%d个FAQ缺少问题字段", i+1)
  160. }
  161. answer, ok := faq["answer"].(string)
  162. if !ok || answer == "" {
  163. return fmt.Errorf("第%d个FAQ缺少答案字段", i+1)
  164. }
  165. if len(question) > 200 {
  166. return fmt.Errorf("第%d个FAQ的问题长度不能超过200字符", i+1)
  167. }
  168. if len(answer) > 1000 {
  169. return fmt.Errorf("第%d个FAQ的答案长度不能超过1000字符", i+1)
  170. }
  171. }
  172. return nil
  173. }
  174. // GetAnnouncements 获取系统公告
  175. func GetAnnouncements() []map[string]interface{} {
  176. annStr := GetConsoleSetting().Announcements
  177. if annStr == "" {
  178. return []map[string]interface{}{}
  179. }
  180. var ann []map[string]interface{}
  181. _ = json.Unmarshal([]byte(annStr), &ann)
  182. return ann
  183. }
  184. // GetFAQ 获取常见问题
  185. func GetFAQ() []map[string]interface{} {
  186. faqStr := GetConsoleSetting().FAQ
  187. if faqStr == "" {
  188. return []map[string]interface{}{}
  189. }
  190. var faq []map[string]interface{}
  191. _ = json.Unmarshal([]byte(faqStr), &faq)
  192. return faq
  193. }