github.com/TeaOSLab/EdgeNode@v1.3.8/internal/waf/captcha_validator.go (about)

     1  package waf
     2  
     3  import (
     4  	"crypto/hmac"
     5  	"crypto/sha256"
     6  	"encoding/hex"
     7  	"encoding/json"
     8  	"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
     9  	"github.com/TeaOSLab/EdgeNode/internal/compressions"
    10  	"github.com/TeaOSLab/EdgeNode/internal/ttlcache"
    11  	"github.com/TeaOSLab/EdgeNode/internal/utils"
    12  	"github.com/TeaOSLab/EdgeNode/internal/utils/fasttime"
    13  	"github.com/TeaOSLab/EdgeNode/internal/waf/requests"
    14  	wafutils "github.com/TeaOSLab/EdgeNode/internal/waf/utils"
    15  	"github.com/iwind/TeaGo/logs"
    16  	"github.com/iwind/TeaGo/maps"
    17  	"github.com/iwind/TeaGo/rands"
    18  	"github.com/iwind/TeaGo/types"
    19  	stringutil "github.com/iwind/TeaGo/utils/string"
    20  	"io"
    21  	"net/http"
    22  	"net/url"
    23  	"strconv"
    24  	"strings"
    25  	"time"
    26  )
    27  
    28  const captchaIdName = "GOEDGE_WAF_CAPTCHA_ID"
    29  const captchaCookiePrefix = "ge_wc" // 'wc' stands for "WAF Captcha"
    30  
    31  var captchaValidator = NewCaptchaValidator()
    32  var captchaGenerator = NewCaptchaGenerator()
    33  
    34  type CaptchaValidator struct {
    35  }
    36  
    37  func NewCaptchaValidator() *CaptchaValidator {
    38  	return &CaptchaValidator{}
    39  }
    40  
    41  func (this *CaptchaValidator) Run(req requests.Request, writer http.ResponseWriter, defaultCaptchaType firewallconfigs.ServerCaptchaType) {
    42  	var realURL string
    43  	var urlObj = req.WAFRaw().URL
    44  	if urlObj != nil {
    45  		realURL = urlObj.Query().Get("from")
    46  	}
    47  
    48  	var info = req.WAFRaw().URL.Query().Get("info")
    49  	if len(info) == 0 {
    50  		if len(realURL) > 0 {
    51  			req.ProcessResponseHeaders(writer.Header(), http.StatusTemporaryRedirect)
    52  			http.Redirect(writer, req.WAFRaw(), realURL, http.StatusTemporaryRedirect)
    53  		} else {
    54  			req.ProcessResponseHeaders(writer.Header(), http.StatusBadRequest)
    55  			writer.WriteHeader(http.StatusBadRequest)
    56  			_, _ = writer.Write([]byte("invalid request (001)"))
    57  		}
    58  		return
    59  	}
    60  
    61  	var success bool
    62  	var actionId int64
    63  	var setId int64
    64  	var originURL string
    65  	var policyId int64
    66  	var groupId int64
    67  	var useLocalFirewall bool
    68  	var timestamp int64
    69  
    70  	var infoArg = &InfoArg{}
    71  	decodeErr := infoArg.Decode(info)
    72  	if decodeErr == nil && infoArg.IsValid() {
    73  		success = true
    74  
    75  		actionId = infoArg.ActionId
    76  		setId = infoArg.SetId
    77  		originURL = infoArg.URL
    78  		policyId = infoArg.PolicyId
    79  		groupId = infoArg.GroupId
    80  		useLocalFirewall = infoArg.UseLocalFirewall
    81  		timestamp = infoArg.Timestamp
    82  	} else {
    83  		// 兼容老版本
    84  		m, decodeMapErr := utils.SimpleDecryptMap(info)
    85  		if decodeMapErr == nil {
    86  			success = true
    87  
    88  			actionId = m.GetInt64("actionId")
    89  			setId = m.GetInt64("setId")
    90  			originURL = m.GetString("url")
    91  			policyId = m.GetInt64("policyId")
    92  			groupId = m.GetInt64("groupId")
    93  			useLocalFirewall = m.GetBool("useLocalFirewall")
    94  			timestamp = m.GetInt64("timestamp")
    95  		}
    96  	}
    97  
    98  	if !success {
    99  		if len(realURL) > 0 {
   100  			req.ProcessResponseHeaders(writer.Header(), http.StatusTemporaryRedirect)
   101  			http.Redirect(writer, req.WAFRaw(), realURL, http.StatusTemporaryRedirect)
   102  		} else {
   103  			req.ProcessResponseHeaders(writer.Header(), http.StatusBadRequest)
   104  			writer.WriteHeader(http.StatusBadRequest)
   105  			_, _ = writer.Write([]byte("invalid request (005)"))
   106  		}
   107  		return
   108  	}
   109  
   110  	if timestamp < fasttime.Now().Unix()-600 { // 10分钟之后信息过期
   111  		req.ProcessResponseHeaders(writer.Header(), http.StatusTemporaryRedirect)
   112  		http.Redirect(writer, req.WAFRaw(), originURL, http.StatusTemporaryRedirect)
   113  		return
   114  	}
   115  
   116  	var waf = SharedWAFManager.FindWAF(policyId)
   117  	if waf == nil {
   118  		req.ProcessResponseHeaders(writer.Header(), http.StatusTemporaryRedirect)
   119  		http.Redirect(writer, req.WAFRaw(), originURL, http.StatusTemporaryRedirect)
   120  		return
   121  	}
   122  	var actionConfig = waf.FindAction(actionId)
   123  	if actionConfig == nil {
   124  		req.ProcessResponseHeaders(writer.Header(), http.StatusTemporaryRedirect)
   125  		http.Redirect(writer, req.WAFRaw(), originURL, http.StatusTemporaryRedirect)
   126  		return
   127  	}
   128  	captchaActionConfig, ok := actionConfig.(*CaptchaAction)
   129  	if !ok {
   130  		req.ProcessResponseHeaders(writer.Header(), http.StatusTemporaryRedirect)
   131  		http.Redirect(writer, req.WAFRaw(), originURL, http.StatusTemporaryRedirect)
   132  		return
   133  	}
   134  
   135  	var captchaType = captchaActionConfig.CaptchaType
   136  	if len(defaultCaptchaType) > 0 && defaultCaptchaType != firewallconfigs.ServerCaptchaTypeNone {
   137  		captchaType = defaultCaptchaType
   138  	}
   139  
   140  	// check geetest
   141  	if captchaType == firewallconfigs.CaptchaTypeGeeTest {
   142  		if waf.DefaultCaptchaAction.GeeTestConfig == nil || !waf.DefaultCaptchaAction.GeeTestConfig.IsOn {
   143  			captchaType = firewallconfigs.CaptchaTypeDefault
   144  		} else if captchaActionConfig.GeeTestConfig == nil {
   145  			captchaActionConfig.GeeTestConfig = waf.DefaultCaptchaAction.GeeTestConfig
   146  		}
   147  	}
   148  
   149  	if req.WAFRaw().Method == http.MethodPost && len(req.WAFRaw().FormValue(captchaIdName)) > 0 {
   150  		switch captchaType {
   151  		case firewallconfigs.CaptchaTypeOneClick:
   152  			this.validateOneClickForm(captchaActionConfig, policyId, groupId, setId, originURL, req, writer, useLocalFirewall)
   153  		case firewallconfigs.CaptchaTypeSlide:
   154  			this.validateSlideForm(captchaActionConfig, policyId, groupId, setId, originURL, req, writer, useLocalFirewall)
   155  		case firewallconfigs.CaptchaTypeGeeTest:
   156  			this.validateGeeTestForm(captchaActionConfig, policyId, groupId, setId, originURL, req, writer, useLocalFirewall)
   157  		default:
   158  			this.validateVerifyCodeForm(captchaActionConfig, policyId, groupId, setId, originURL, req, writer, useLocalFirewall)
   159  		}
   160  	} else {
   161  		var captchaId = req.WAFRaw().URL.Query().Get(captchaIdName)
   162  		if len(captchaId) > 0 {
   163  			// 增加计数
   164  			CaptchaIncreaseFails(req, captchaActionConfig, policyId, groupId, setId, CaptchaPageCodeImage, useLocalFirewall)
   165  			this.showImage(captchaActionConfig, req, writer, captchaType)
   166  		} else {
   167  			// 增加计数
   168  			CaptchaIncreaseFails(req, captchaActionConfig, policyId, groupId, setId, CaptchaPageCodeShow, useLocalFirewall)
   169  			this.show(captchaActionConfig, setId, originURL, req, writer, captchaType)
   170  		}
   171  	}
   172  }
   173  
   174  func (this *CaptchaValidator) show(actionConfig *CaptchaAction, setId int64, originURL string, req requests.Request, writer http.ResponseWriter, captchaType firewallconfigs.ServerCaptchaType) {
   175  	// validated yet?
   176  	if SharedIPWhiteList.Contains(wafutils.ComposeIPType(setId, req), actionConfig.Scope, req.WAFServerId(), req.WAFRemoteIP()) {
   177  		http.Redirect(writer, req.WAFRaw(), originURL, http.StatusSeeOther)
   178  		return
   179  	}
   180  
   181  	switch captchaType {
   182  	case firewallconfigs.CaptchaTypeOneClick:
   183  		this.showOneClickForm(actionConfig, req, writer)
   184  	case firewallconfigs.CaptchaTypeSlide:
   185  		this.showSlideForm(actionConfig, req, writer)
   186  	case firewallconfigs.CaptchaTypeGeeTest:
   187  		this.showGeeTestForm(actionConfig, req, writer, originURL)
   188  	default:
   189  		this.showVerifyCodesForm(actionConfig, req, writer)
   190  	}
   191  }
   192  
   193  func (this *CaptchaValidator) showImage(actionConfig *CaptchaAction, req requests.Request, writer http.ResponseWriter, captchaType firewallconfigs.ServerCaptchaType) {
   194  	switch captchaType {
   195  	case firewallconfigs.CaptchaTypeOneClick:
   196  		// stub
   197  	case firewallconfigs.CaptchaTypeSlide:
   198  		// stub
   199  	case firewallconfigs.CaptchaTypeGeeTest:
   200  		// stub
   201  	default:
   202  		this.showVerifyImage(actionConfig, req, writer)
   203  	}
   204  }
   205  
   206  func (this *CaptchaValidator) showVerifyCodesForm(actionConfig *CaptchaAction, req requests.Request, writer http.ResponseWriter) {
   207  	// show captcha
   208  	var countLetters = 6
   209  	if actionConfig.CountLetters > 0 && actionConfig.CountLetters <= 10 {
   210  		countLetters = int(actionConfig.CountLetters)
   211  	}
   212  	var captchaId = captchaGenerator.NewCaptcha(countLetters)
   213  
   214  	var lang = actionConfig.Lang
   215  	if len(lang) == 0 {
   216  		var acceptLanguage = req.WAFRaw().Header.Get("Accept-Language")
   217  		if len(acceptLanguage) > 0 {
   218  			langIndex := strings.Index(acceptLanguage, ",")
   219  			if langIndex > 0 {
   220  				lang = acceptLanguage[:langIndex]
   221  			}
   222  		}
   223  	}
   224  	if len(lang) == 0 {
   225  		lang = "en-US"
   226  	}
   227  
   228  	var msgTitle string
   229  	var msgPrompt string
   230  	var msgButtonTitle string
   231  	var msgRequestId string
   232  	var msgPlaceholder string
   233  
   234  	switch lang {
   235  	case "en-US":
   236  		msgTitle = "Verify Yourself"
   237  		msgPrompt = "Input verify code above:"
   238  		msgButtonTitle = "Verify Yourself"
   239  		msgRequestId = "Request ID"
   240  		msgPlaceholder = ""
   241  	case "zh-CN":
   242  		msgTitle = "身份验证"
   243  		msgPrompt = "请输入上面的验证码"
   244  		msgButtonTitle = "提交验证"
   245  		msgRequestId = "请求ID"
   246  		msgPlaceholder = "点此输入"
   247  	case "zh-TW":
   248  		msgTitle = "身份驗證"
   249  		msgPrompt = "請輸入上面的驗證碼"
   250  		msgButtonTitle = "提交驗證"
   251  		msgRequestId = "請求ID"
   252  		msgPlaceholder = "點此輸入"
   253  	default:
   254  		msgTitle = "Verify Yourself"
   255  		msgPrompt = "Input verify code above:"
   256  		msgButtonTitle = "Verify Yourself"
   257  		msgRequestId = "Request ID"
   258  	}
   259  
   260  	var msgCss = ""
   261  	var requestIdBox = `<address>` + msgRequestId + `: ` + req.Format("${requestId}") + `</address>`
   262  	var msgFooter = ""
   263  
   264  	// 默认设置
   265  	if actionConfig.UIIsOn {
   266  		if len(actionConfig.UIPrompt) > 0 {
   267  			msgPrompt = actionConfig.UIPrompt
   268  		}
   269  		if len(actionConfig.UIButtonTitle) > 0 {
   270  			msgButtonTitle = actionConfig.UIButtonTitle
   271  		}
   272  		if len(actionConfig.UITitle) > 0 {
   273  			msgTitle = actionConfig.UITitle
   274  		}
   275  		if len(actionConfig.UICss) > 0 {
   276  			msgCss = actionConfig.UICss
   277  		}
   278  		if !actionConfig.UIShowRequestId {
   279  			requestIdBox = ""
   280  		}
   281  		if len(actionConfig.UIFooter) > 0 {
   282  			msgFooter = actionConfig.UIFooter
   283  		}
   284  	}
   285  
   286  	var body = `<form method="POST" id="captcha-form">
   287  	<input type="hidden" name="` + captchaIdName + `" value="` + captchaId + `"/>
   288  	<div class="ui-image">
   289  		<p id="ui-captcha-image-prompt">loading ...</p>
   290  		<img id="ui-captcha-image" src="` + req.WAFRaw().URL.String() + `&` + captchaIdName + `=` + captchaId + `" alt=""/>
   291  	</div>
   292  	<div class="ui-input">
   293  		<p class="ui-prompt">` + msgPrompt + `</p>
   294  		<input type="text" name="GOEDGE_WAF_CAPTCHA_CODE" id="GOEDGE_WAF_CAPTCHA_CODE" size="` + types.String(countLetters*17/10) + `" maxlength="` + types.String(countLetters) + `" autocomplete="off" z-index="1" class="input" placeholder="` + msgPlaceholder + `"/>
   295  	</div>
   296  	<div class="ui-button">
   297  		<button type="submit" style="line-height:24px;margin-top:10px">` + msgButtonTitle + `</button>
   298  	</div>
   299  </form>
   300  ` + requestIdBox + `
   301  ` + msgFooter
   302  
   303  	// Body
   304  	if actionConfig.UIIsOn {
   305  		if len(actionConfig.UIBody) > 0 {
   306  			var index = strings.Index(actionConfig.UIBody, "${body}")
   307  			if index < 0 {
   308  				body = actionConfig.UIBody + body
   309  			} else {
   310  				body = actionConfig.UIBody[:index] + body + actionConfig.UIBody[index+7:] // 7是"${body}"的长度
   311  			}
   312  		}
   313  	}
   314  
   315  	var msgHTML = `<!DOCTYPE html>
   316  <html lang="` + lang + `">
   317  <head>
   318  	<title>` + msgTitle + `</title>
   319  	<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=0">
   320  	<meta charset="UTF-8"/>
   321  	<script type="text/javascript">
   322  	var isValidated=!1;window.addEventListener("pageshow",function(){isValidated&&window.location.reload()}),null!=window.addEventListener&&(document.addEventListener("DOMContentLoaded",function(){document.getElementById("ui-captcha-image").addEventListener("load",function(){var e=document.getElementById("ui-captcha-image-prompt");e.parentNode.removeChild(e)})}),window.addEventListener("load",function(){document.getElementById("GOEDGE_WAF_CAPTCHA_CODE").focus();var e=document.getElementById("captcha-form");null!=e&&e.addEventListener("submit",function(){isValidated=!0})}));
   323  	</script>
   324  	<style type="text/css">
   325  	* { font-size: 13px; }
   326  	form { max-width: 20em; margin: 0 auto; text-align: center; font-family: Roboto,"Helvetica Neue Light","Helvetica Neue",Helvetica,Arial,"Lucida Grande",sans-serif; }
   327  	.ui-prompt { font-size: 1.2rem; }
   328  	.input { font-size:16px;line-height:24px; letter-spacing:0.2em; min-width: 5em; text-align: center; background: #fff; border: 1px solid rgba(0, 0, 0, 0.38); color: rgba(0, 0, 0, 0.87); outline: none; border-radius: 4px; padding: 0.75rem 0.75rem; }
   329  	.input:focus { border: 1px #3f51b5 solid; outline: none; }
   330  	address { margin-top: 1em; padding-top: 0.5em; border-top: 1px #ccc solid; text-align: center; }
   331  	button { background: #3f51b5; color: #fff; cursor: pointer; padding: 0.571rem 0.75rem; min-width: 8rem; font-size: 1rem; border: 0 none; border-radius: 4px; }
   332  ` + msgCss + `
   333  	</style>
   334  </head>
   335  <body>` + body + `
   336  </body>
   337  </html>`
   338  
   339  	req.ProcessResponseHeaders(writer.Header(), http.StatusOK)
   340  	writer.Header().Set("Content-Type", "text/html; charset=utf-8")
   341  	writer.Header().Set("Content-Length", types.String(len(msgHTML)))
   342  	writer.WriteHeader(http.StatusOK)
   343  	_, _ = writer.Write([]byte(msgHTML))
   344  }
   345  
   346  func (this *CaptchaValidator) showVerifyImage(actionConfig *CaptchaAction, req requests.Request, writer http.ResponseWriter) {
   347  	var captchaId = req.WAFRaw().URL.Query().Get(captchaIdName)
   348  	if len(captchaId) == 0 {
   349  		return
   350  	}
   351  
   352  	writer.Header().Set("Content-Type", "image/png")
   353  	err := captchaGenerator.WriteImage(writer, captchaId, 200, 100)
   354  	if err != nil {
   355  		logs.Error(err)
   356  		return
   357  	}
   358  }
   359  
   360  func (this *CaptchaValidator) validateVerifyCodeForm(actionConfig *CaptchaAction, policyId int64, groupId int64, setId int64, originURL string, req requests.Request, writer http.ResponseWriter, useLocalFirewall bool) (allow bool) {
   361  	var captchaId = req.WAFRaw().FormValue(captchaIdName)
   362  	if len(captchaId) > 0 {
   363  		var captchaCode = req.WAFRaw().FormValue("GOEDGE_WAF_CAPTCHA_CODE")
   364  		if captchaGenerator.Verify(captchaId, captchaCode) {
   365  			// 清除计数
   366  			CaptchaDeleteCacheKey(req)
   367  
   368  			var life = CaptchaSeconds
   369  			if actionConfig.Life > 0 {
   370  				life = types.Int(actionConfig.Life)
   371  			}
   372  
   373  			// 加入到白名单
   374  			SharedIPWhiteList.RecordIP(wafutils.ComposeIPType(setId, req), actionConfig.Scope, req.WAFServerId(), req.WAFRemoteIP(), time.Now().Unix()+int64(life), policyId, false, groupId, setId, "")
   375  
   376  			req.ProcessResponseHeaders(writer.Header(), http.StatusSeeOther)
   377  
   378  			// 记录到Cookie
   379  			this.setCookie(writer, setId, life)
   380  
   381  			http.Redirect(writer, req.WAFRaw(), originURL, http.StatusSeeOther)
   382  
   383  			return false
   384  		} else {
   385  			// 增加计数
   386  			if !CaptchaIncreaseFails(req, actionConfig, policyId, groupId, setId, CaptchaPageCodeSubmit, useLocalFirewall) {
   387  				return false
   388  			}
   389  
   390  			req.ProcessResponseHeaders(writer.Header(), http.StatusSeeOther)
   391  			http.Redirect(writer, req.WAFRaw(), req.WAFRaw().URL.String(), http.StatusSeeOther)
   392  		}
   393  	}
   394  
   395  	return true
   396  }
   397  
   398  func (this *CaptchaValidator) showOneClickForm(actionConfig *CaptchaAction, req requests.Request, writer http.ResponseWriter) {
   399  	var lang = actionConfig.Lang
   400  	if len(lang) == 0 {
   401  		var acceptLanguage = req.WAFRaw().Header.Get("Accept-Language")
   402  		if len(acceptLanguage) > 0 {
   403  			langIndex := strings.Index(acceptLanguage, ",")
   404  			if langIndex > 0 {
   405  				lang = acceptLanguage[:langIndex]
   406  			}
   407  		}
   408  	}
   409  	if len(lang) == 0 {
   410  		lang = "en-US"
   411  	}
   412  
   413  	var msgTitle string
   414  	var msgPrompt string
   415  	var msgRequestId string
   416  
   417  	switch lang {
   418  	case "zh-CN":
   419  		msgTitle = "身份验证"
   420  		msgPrompt = "我不是机器人"
   421  		msgRequestId = "请求ID"
   422  	case "zh-TW":
   423  		msgTitle = "身份驗證"
   424  		msgPrompt = "我不是機器人"
   425  		msgRequestId = "請求ID"
   426  	default:
   427  		msgTitle = "Verify Yourself"
   428  		msgPrompt = "I'm not a robot"
   429  		msgRequestId = "Request ID"
   430  	}
   431  
   432  	var msgCss = ""
   433  	var requestIdBox = `<address>` + msgRequestId + `: ` + req.Format("${requestId}") + `</address>`
   434  	var msgFooter = ""
   435  
   436  	// 默认设置
   437  	if actionConfig.OneClickUIIsOn {
   438  		if len(actionConfig.OneClickUIPrompt) > 0 {
   439  			msgPrompt = actionConfig.OneClickUIPrompt
   440  		}
   441  		if len(actionConfig.OneClickUITitle) > 0 {
   442  			msgTitle = actionConfig.OneClickUITitle
   443  		}
   444  		if len(actionConfig.OneClickUICss) > 0 {
   445  			msgCss = actionConfig.OneClickUICss
   446  		}
   447  		if !actionConfig.OneClickUIShowRequestId {
   448  			requestIdBox = ""
   449  		}
   450  		if len(actionConfig.OneClickUIFooter) > 0 {
   451  			msgFooter = actionConfig.OneClickUIFooter
   452  		}
   453  	}
   454  
   455  	var captchaId = stringutil.Md5(req.WAFRemoteIP() + "@" + stringutil.Rand(32))
   456  	var nonce = rands.Int64()
   457  	if !ttlcache.SharedInt64Cache.Write("WAF_CAPTCHA:"+captchaId, nonce, fasttime.Now().Unix()+600) {
   458  		return
   459  	}
   460  
   461  	var body = `<form method="POST" id="ui-form">
   462  	<input type="hidden" name="` + captchaIdName + `" value="` + captchaId + `"/>
   463  	<div class="ui-input">
   464  		<div class="ui-checkbox" id="checkbox"></div>
   465  		<p class="ui-prompt">` + msgPrompt + `</p>
   466  	</div>
   467  </form>
   468  ` + requestIdBox + `
   469  ` + msgFooter
   470  
   471  	// Body
   472  	if actionConfig.OneClickUIIsOn {
   473  		if len(actionConfig.OneClickUIBody) > 0 {
   474  			var index = strings.Index(actionConfig.OneClickUIBody, "${body}")
   475  			if index < 0 {
   476  				body = actionConfig.OneClickUIBody + body
   477  			} else {
   478  				body = actionConfig.OneClickUIBody[:index] + body + actionConfig.OneClickUIBody[index+7:] // 7是"${body}"的长度
   479  			}
   480  		}
   481  	}
   482  
   483  	var msgHTML = `<!DOCTYPE html>
   484  <html>
   485  <head>
   486  	<title>` + msgTitle + `</title>
   487  	<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=0">
   488  	<meta charset="UTF-8"/>
   489  	<script type="text/javascript">
   490  	var isValidated=!1;window.addEventListener("pageshow",function(){isValidated&&window.location.reload()}),window.addEventListener("load",function(){var t=document.getElementById("checkbox"),n=!1;t.addEventListener("click",function(){var e;t.className="ui-checkbox checked",n||(isValidated=n=!0,(e=document.createElement("input")).setAttribute("name","nonce"),e.setAttribute("type","hidden"),e.setAttribute("value","` + types.String(nonce) + `"),document.getElementById("ui-form").appendChild(e),document.getElementById("ui-form").submit())})});
   491  	</script>
   492  	<style type="text/css">
   493  	form { max-width: 20em; margin: 0 auto; text-align: center; font-family: Roboto,"Helvetica Neue Light","Helvetica Neue",Helvetica,Arial,"Lucida Grande",sans-serif; }
   494      .ui-input { position: relative; padding-top: 1em; height: 2.2em; background: #eee; }
   495      .ui-checkbox { width: 16px; height: 16px; border: 1px #999 solid; float: left; margin-left: 1em; cursor: pointer; }
   496      .ui-checkbox.checked { background: #276AC6; }
   497      .ui-prompt { float: left; margin: 0; margin-left: 0.5em; padding: 0; line-height: 1.2; }
   498  	address { margin-top: 1em; padding-top: 0.5em; border-top: 1px #ccc solid; text-align: center; clear: both; }
   499  ` + msgCss + `
   500  	</style>
   501  </head>
   502  <body>` + body + `
   503  </body>
   504  </html>`
   505  
   506  	req.ProcessResponseHeaders(writer.Header(), http.StatusOK)
   507  	writer.Header().Set("Content-Type", "text/html; charset=utf-8")
   508  	writer.Header().Set("Content-Length", types.String(len(msgHTML)))
   509  	writer.WriteHeader(http.StatusOK)
   510  	_, _ = writer.Write([]byte(msgHTML))
   511  }
   512  
   513  func (this *CaptchaValidator) validateOneClickForm(actionConfig *CaptchaAction, policyId int64, groupId int64, setId int64, originURL string, req requests.Request, writer http.ResponseWriter, useLocalFirewall bool) (allow bool) {
   514  	var captchaId = req.WAFRaw().FormValue(captchaIdName)
   515  	var nonce = req.WAFRaw().FormValue("nonce")
   516  	if len(captchaId) > 0 {
   517  		var key = "WAF_CAPTCHA:" + captchaId
   518  		var cacheItem = ttlcache.SharedInt64Cache.Read(key)
   519  		ttlcache.SharedInt64Cache.Delete(key)
   520  		if cacheItem != nil {
   521  			// 清除计数
   522  			CaptchaDeleteCacheKey(req)
   523  
   524  			if cacheItem.Value == types.Int64(nonce) {
   525  				var life = CaptchaSeconds
   526  				if actionConfig.Life > 0 {
   527  					life = types.Int(actionConfig.Life)
   528  				}
   529  
   530  				// 加入到白名单
   531  				SharedIPWhiteList.RecordIP(wafutils.ComposeIPType(setId, req), actionConfig.Scope, req.WAFServerId(), req.WAFRemoteIP(), time.Now().Unix()+int64(life), policyId, false, groupId, setId, "")
   532  
   533  				req.ProcessResponseHeaders(writer.Header(), http.StatusSeeOther)
   534  
   535  				// 记录到Cookie
   536  				this.setCookie(writer, setId, life)
   537  
   538  				http.Redirect(writer, req.WAFRaw(), originURL, http.StatusSeeOther)
   539  
   540  				return false
   541  			}
   542  		} else {
   543  			// 增加计数
   544  			if !CaptchaIncreaseFails(req, actionConfig, policyId, groupId, setId, CaptchaPageCodeSubmit, useLocalFirewall) {
   545  				return false
   546  			}
   547  
   548  			req.ProcessResponseHeaders(writer.Header(), http.StatusSeeOther)
   549  			http.Redirect(writer, req.WAFRaw(), req.WAFRaw().URL.String(), http.StatusSeeOther)
   550  		}
   551  	}
   552  
   553  	return true
   554  }
   555  
   556  func (this *CaptchaValidator) showSlideForm(actionConfig *CaptchaAction, req requests.Request, writer http.ResponseWriter) {
   557  	var lang = actionConfig.Lang
   558  	if len(lang) == 0 {
   559  		var acceptLanguage = req.WAFRaw().Header.Get("Accept-Language")
   560  		if len(acceptLanguage) > 0 {
   561  			langIndex := strings.Index(acceptLanguage, ",")
   562  			if langIndex > 0 {
   563  				lang = acceptLanguage[:langIndex]
   564  			}
   565  		}
   566  	}
   567  	if len(lang) == 0 {
   568  		lang = "en-US"
   569  	}
   570  
   571  	var msgTitle string
   572  	var msgPrompt string
   573  	var msgRequestId string
   574  
   575  	switch lang {
   576  	case "zh-CN":
   577  		msgTitle = "身份验证"
   578  		msgPrompt = "滑动上面方块到右侧解锁"
   579  		msgRequestId = "请求ID"
   580  	case "zh-TW":
   581  		msgTitle = "身份驗證"
   582  		msgPrompt = "滑動上面方塊到右側解鎖"
   583  		msgRequestId = "請求ID"
   584  	default:
   585  		msgTitle = "Verify Yourself"
   586  		msgPrompt = "Slide to Unlock"
   587  		msgRequestId = "Request ID"
   588  	}
   589  
   590  	var msgCss = ""
   591  	var requestIdBox = `<address>` + msgRequestId + `: ` + req.Format("${requestId}") + `</address>`
   592  	var msgFooter = ""
   593  
   594  	// 默认设置
   595  	if actionConfig.OneClickUIIsOn {
   596  		if len(actionConfig.OneClickUIPrompt) > 0 {
   597  			msgPrompt = actionConfig.OneClickUIPrompt
   598  		}
   599  		if len(actionConfig.OneClickUITitle) > 0 {
   600  			msgTitle = actionConfig.OneClickUITitle
   601  		}
   602  		if len(actionConfig.OneClickUICss) > 0 {
   603  			msgCss = actionConfig.OneClickUICss
   604  		}
   605  		if !actionConfig.OneClickUIShowRequestId {
   606  			requestIdBox = ""
   607  		}
   608  		if len(actionConfig.OneClickUIFooter) > 0 {
   609  			msgFooter = actionConfig.OneClickUIFooter
   610  		}
   611  	}
   612  
   613  	var captchaId = stringutil.Md5(req.WAFRemoteIP() + "@" + stringutil.Rand(32))
   614  	var nonce = rands.Int64()
   615  	if !ttlcache.SharedInt64Cache.Write("WAF_CAPTCHA:"+captchaId, nonce, fasttime.Now().Unix()+600) {
   616  		return
   617  	}
   618  
   619  	var body = `<form method="POST" id="ui-form">
   620  	<input type="hidden" name="` + captchaIdName + `" value="` + captchaId + `"/>
   621  	 <div class="ui-input" id="input">
   622        	<div class="ui-progress-bar" id="progress-bar"></div>
   623          <div class="ui-handler" id="handler"></div>
   624  		<div class="ui-handler-placeholder" id="handler-placeholder"></div>
   625   		<img alt="" src="data:image/jpeg;charset=utf-8;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAACXBIWXMAAAsTAAALEwEAmpwYAAACHklEQVR4nO3cu4oUQRgF4EI3UhDXRNRQUFnBC97XwIcRfQf3ZfYdTAxMFMwMzUw18q6JCOpaxayBTItdDd3V9v99cOKt5pyZnWlmJiUAAAAAAAAAAAAAAAAAAAAAgOgO5pzLOdn6IEzvXs6bnL39PM+53PRETKaUv9eRLzm3G56LCWykPx/5XSPYbnY6Rncm/b383/mcc6vVARnXqfTvARjBwpUXfH1HcLPRGRlRebVf/tf3GcGnZASLVF7olUe4Z4LAakZQnglutDkmYzICjAAjINWP4HqbYzImI8AIqBvBx2QEi1Q7gmttjsmYjAAjoH4EV9sckzEZAUaAEZDqRvAh50qbYzKm5iMoH3F+kPNi/w/I9PmW+g2g5G3Ohc4mBziQ87jij8s8Ur6TcLqjz2r3Z3AxMixP1uus93AGFyLD8jPn6HqldR7N4EJk+AA21yutszODC5FhedbRZ7UjOS9ncDFSl/LO4WxHn4Mcy9nN+TqDC5N+5Y9yQ6j80sWmTJ47qe5GkFvCC3IxrW7s9CnfR8YWRvmBKT8w5Qem/MAuJeWHVcp/l5QfkvIDU35gyg+spnzfDF4Y5QdWfjtQ+UEpPzDlB1bKf5+UH5LyAzufVu/f+77P90mehSmfylV+UIdzfiRP+2GdSB754b1Oyg/tblJ+eGUEr9Kq+O85T3O2mp6IJo7nHGp9CAAAAAAAAAAAAAAAAAAAAADgf/ILsUB70laSdmQAAAAASUVORK5CYII="/>
   626      </div>
   627  	<p class="ui-prompt">` + msgPrompt + `</p>
   628  </form>
   629  ` + requestIdBox + `
   630  ` + msgFooter
   631  
   632  	// Body
   633  	if actionConfig.OneClickUIIsOn {
   634  		if len(actionConfig.OneClickUIBody) > 0 {
   635  			var index = strings.Index(actionConfig.OneClickUIBody, "${body}")
   636  			if index < 0 {
   637  				body = actionConfig.OneClickUIBody + body
   638  			} else {
   639  				body = actionConfig.OneClickUIBody[:index] + body + actionConfig.OneClickUIBody[index+7:] // 7是"${body}"的长度
   640  			}
   641  		}
   642  	}
   643  
   644  	var msgHTML = `<!DOCTYPE html>
   645  <html>
   646  <head>
   647  	<title>` + msgTitle + `</title>
   648  	<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=0">
   649  	<meta charset="UTF-8"/>
   650  	<script type="text/javascript">
   651  	var isValidated=!1;window.addEventListener("pageshow",function(){isValidated&&window.location.reload()}),window.addEventListener("load",function(){var n=document.getElementById("input"),s=document.getElementById("handler"),d=document.getElementById("progress-bar"),o=!1,i=0,t=n.offsetLeft,u=s.offsetLeft,c=n.offsetWidth-s.offsetWidth-s.offsetLeft,a=!1;function e(e){e.preventDefault(),o=!0,i=null!=e.touches&&0<e.touches.length?e.touches[0].clientX-t:e.offsetX}function f(e){var t;o&&(t=e.x,null!=e.touches&&0<e.touches.length&&(t=e.touches[0].clientX),(t=t-n.offsetLeft-i)<u?t=u:c<t&&(t=c),s.style.cssText="margin-left: "+t+"px",0<t&&(d.style.cssText="width: "+(t+s.offsetWidth+4)+"px"))}function l(e){var t;o=o&&!1,s.offsetLeft<c-4?(s.style.cssText="margin-left: "+u+"px",n.style.cssText="background: #eee",d.style.cssText="width: 0px"):(s.style.cssText="margin-left: "+c+"px",n.style.cssText="background: #a5dc86",a||(isValidated=a=!0,(t=document.createElement("input")).setAttribute("name","nonce"),t.setAttribute("type","hidden"),t.setAttribute("value","` + types.String(nonce) + `"),document.getElementById("ui-form").appendChild(t),document.getElementById("ui-form").submit()))}void 0!==document.ontouchstart?(s.addEventListener("touchstart",e),document.addEventListener("touchmove",f),document.addEventListener("touchend",l)):(s.addEventListener("mousedown",e),window.addEventListener("mousemove",f),window.addEventListener("mouseup",l))});
   652  	</script>
   653  	<style type="text/css">
   654  	form { max-width: 20em; margin: 5em auto; text-align: center; font-family: Roboto,"Helvetica Neue Light","Helvetica Neue",Helvetica,Arial,"Lucida Grande",sans-serif; }
   655   		.ui-input {
   656              height: 4em;
   657              background: #eee;
   658  			border: 1px #ccc solid;
   659              text-align: left;
   660              position: relative;
   661          }
   662  
   663          .ui-input .ui-progress-bar {
   664              background: #689F38;
   665              position: absolute;
   666              top: 0;
   667              left: 0;
   668              bottom: 0;
   669          }
   670  
   671          .ui-handler, .ui-handler-placeholder {
   672              width: 3.6em;
   673              height: 3.6em;
   674              margin: 0.2em;
   675              background: #3f51b5;
   676              border-radius: 0.6em;
   677              display: inline-block;
   678              cursor: pointer;
   679              z-index: 10;
   680              position: relative;
   681          }
   682  
   683          .ui-handler-placeholder {
   684              background: none;
   685              border: 1px #ccc dashed;
   686              position: absolute;
   687              right: -1px;
   688              top: 0;
   689              bottom: 0;
   690          }
   691  
   692          .ui-input img {
   693              position: absolute;
   694              top: 0;
   695              bottom: 0;
   696              height: 100%;
   697              left: 8em;
   698              opacity: 5%;
   699          }
   700      .ui-prompt { float: left; margin: 1em 0; padding: 0; line-height: 1.2; }
   701  	address { margin-top: 1em; padding-top: 0.5em; border-top: 1px #ccc solid; text-align: center; clear: both; }
   702  ` + msgCss + `
   703  	</style>
   704  </head>
   705  <body>` + body + `
   706  </body>
   707  </html>`
   708  
   709  	req.ProcessResponseHeaders(writer.Header(), http.StatusOK)
   710  	writer.Header().Set("Content-Type", "text/html; charset=utf-8")
   711  	writer.Header().Set("Content-Length", types.String(len(msgHTML)))
   712  	writer.WriteHeader(http.StatusOK)
   713  	_, _ = writer.Write([]byte(msgHTML))
   714  }
   715  
   716  func (this *CaptchaValidator) validateSlideForm(actionConfig *CaptchaAction, policyId int64, groupId int64, setId int64, originURL string, req requests.Request, writer http.ResponseWriter, useLocalFirewall bool) (allow bool) {
   717  	var captchaId = req.WAFRaw().FormValue(captchaIdName)
   718  	var nonce = req.WAFRaw().FormValue("nonce")
   719  	if len(captchaId) > 0 {
   720  		var key = "WAF_CAPTCHA:" + captchaId
   721  		var cacheItem = ttlcache.SharedInt64Cache.Read(key)
   722  		ttlcache.SharedInt64Cache.Delete(key)
   723  		if cacheItem != nil {
   724  			// 清除计数
   725  			CaptchaDeleteCacheKey(req)
   726  
   727  			if cacheItem.Value == types.Int64(nonce) {
   728  				var life = CaptchaSeconds
   729  				if actionConfig.Life > 0 {
   730  					life = types.Int(actionConfig.Life)
   731  				}
   732  
   733  				// 加入到白名单
   734  				SharedIPWhiteList.RecordIP(wafutils.ComposeIPType(setId, req), actionConfig.Scope, req.WAFServerId(), req.WAFRemoteIP(), time.Now().Unix()+int64(life), policyId, false, groupId, setId, "")
   735  
   736  				req.ProcessResponseHeaders(writer.Header(), http.StatusSeeOther)
   737  
   738  				// 记录到Cookie
   739  				this.setCookie(writer, setId, life)
   740  
   741  				http.Redirect(writer, req.WAFRaw(), originURL, http.StatusSeeOther)
   742  
   743  				return false
   744  			}
   745  		} else {
   746  			// 增加计数
   747  			if !CaptchaIncreaseFails(req, actionConfig, policyId, groupId, setId, CaptchaPageCodeSubmit, useLocalFirewall) {
   748  				return false
   749  			}
   750  
   751  			req.ProcessResponseHeaders(writer.Header(), http.StatusSeeOther)
   752  			http.Redirect(writer, req.WAFRaw(), req.WAFRaw().URL.String(), http.StatusSeeOther)
   753  		}
   754  	}
   755  
   756  	return true
   757  }
   758  
   759  func (this *CaptchaValidator) validateGeeTestForm(actionConfig *CaptchaAction, policyId int64, groupId int64, setId int64, originURL string, req requests.Request, writer http.ResponseWriter, useLocalFirewall bool) (allow bool) {
   760  	var geeTestConfig = actionConfig.GeeTestConfig
   761  	if geeTestConfig == nil || !geeTestConfig.IsOn {
   762  		return
   763  	}
   764  
   765  	defer func() {
   766  		if allow {
   767  			// 清除计数
   768  			CaptchaDeleteCacheKey(req)
   769  
   770  			var life = CaptchaSeconds
   771  			if actionConfig.Life > 0 {
   772  				life = types.Int(actionConfig.Life)
   773  			}
   774  
   775  			// 加入到白名单
   776  			SharedIPWhiteList.RecordIP(wafutils.ComposeIPType(setId, req), actionConfig.Scope, req.WAFServerId(), req.WAFRemoteIP(), time.Now().Unix()+int64(life), policyId, false, groupId, setId, "")
   777  
   778  			// 记录到Cookie
   779  			this.setCookie(writer, setId, life)
   780  
   781  			writer.WriteHeader(http.StatusOK)
   782  		} else {
   783  			// 增加计数
   784  			CaptchaIncreaseFails(req, actionConfig, policyId, groupId, setId, CaptchaPageCodeSubmit, useLocalFirewall)
   785  
   786  			writer.WriteHeader(http.StatusBadRequest)
   787  		}
   788  	}()
   789  
   790  	if req.WAFRaw().Body == nil || req.WAFRaw().ContentLength <= 0 || req.WAFRaw().ContentLength > 2048 {
   791  		return false
   792  	}
   793  
   794  	data, err := io.ReadAll(req.WAFRaw().Body)
   795  	if err != nil {
   796  		return false
   797  	}
   798  
   799  	var m = maps.Map{}
   800  	err = json.Unmarshal(data, &m)
   801  	if err != nil {
   802  		return false
   803  	}
   804  
   805  	const GeeTestAPIServer = "https://gcaptcha4.geetest.com"
   806  	var GeeTestAPIURL = GeeTestAPIServer + "/validate" + "?captcha_id=" + geeTestConfig.CaptchaId
   807  
   808  	var lotNumber = m.GetString("lot_number")
   809  
   810  	var hash = hmac.New(sha256.New, []byte(geeTestConfig.CaptchaKey))
   811  	hash.Write([]byte(lotNumber))
   812  	var signToken = hex.EncodeToString(hash.Sum(nil))
   813  
   814  	var query = url.Values{
   815  		"lot_number":     []string{lotNumber},
   816  		"captcha_output": []string{m.GetString("captcha_output")},
   817  		"pass_token":     []string{m.GetString("pass_token")},
   818  		"gen_time":       []string{m.GetString("gen_time")},
   819  		"sign_token":     []string{signToken},
   820  	}
   821  
   822  	resp, err := geeTestHTTPClient.PostForm(GeeTestAPIURL, query)
   823  	defer func() {
   824  		if resp != nil && resp.Body != nil {
   825  			_ = resp.Body.Close()
   826  		}
   827  	}()
   828  	if err != nil || resp.StatusCode != http.StatusOK {
   829  		// 放行,避免阻塞业务
   830  		allow = true
   831  		return
   832  	}
   833  
   834  	data, err = io.ReadAll(resp.Body)
   835  	if err != nil {
   836  		// 放行,避免阻塞业务
   837  		allow = true
   838  		return
   839  	}
   840  
   841  	var resultMap = maps.Map{}
   842  	err = json.Unmarshal(data, &resultMap)
   843  	if err != nil {
   844  		// 放行,避免阻塞业务
   845  		allow = true
   846  		return
   847  	}
   848  
   849  	allow = resultMap.GetString("result") == "success"
   850  
   851  	return allow
   852  }
   853  
   854  func (this *CaptchaValidator) showGeeTestForm(actionConfig *CaptchaAction, req requests.Request, writer http.ResponseWriter, originURL string) {
   855  	var geeTestConfig = actionConfig.GeeTestConfig
   856  	if geeTestConfig == nil || !geeTestConfig.IsOn {
   857  		return
   858  	}
   859  
   860  	var lang = actionConfig.Lang
   861  	if len(lang) == 0 {
   862  		var acceptLanguage = req.WAFRaw().Header.Get("Accept-Language")
   863  		if len(acceptLanguage) > 0 {
   864  			langIndex := strings.Index(acceptLanguage, ",")
   865  			if langIndex > 0 {
   866  				lang = acceptLanguage[:langIndex]
   867  			}
   868  		}
   869  	}
   870  	if len(lang) == 0 {
   871  		lang = "en-US"
   872  	}
   873  
   874  	var msgTitle string
   875  
   876  	switch lang {
   877  	case "zh-CN":
   878  		msgTitle = "身份验证"
   879  	case "zh-TW":
   880  		msgTitle = "身份驗證"
   881  	default:
   882  		msgTitle = "Verify Yourself"
   883  	}
   884  
   885  	var msgHTML = `<!DOCTYPE html>
   886  <html>
   887  <head>
   888  	<title>` + msgTitle + `</title>
   889  	<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=0">
   890  	<meta charset="UTF-8"/>
   891  	<script type="text/javascript" src="//static.geetest.com/v4/gt4.js"></script>
   892  	<script type="text/javascript">` + axiosJavascript + `</script>
   893  </head>
   894  <body>
   895  <script>
   896  var originURL = ` + strconv.Quote(originURL) + `;
   897  initGeetest4({
   898  	captchaId: ` + strconv.Quote(geeTestConfig.CaptchaId) + `,
   899  	product: "bind",
   900  }, function (gt) {
   901  	gt.onSuccess(function () {
   902  	 	var result = gt.getValidate();
   903  		axios.post("` + req.WAFRaw().URL.String() + "&" + captchaIdName + `=none", result, {
   904  			"Content-Type": "application/json"
   905  		}).then(function (resp) {
   906  			if (resp.status == 200) {
   907  				window.location = originURL;
   908  			}
   909  		});
   910  	})
   911  	gt.showCaptcha();
   912  });
   913  </script>
   914  </body>
   915  </html>`
   916  
   917  	req.ProcessResponseHeaders(writer.Header(), http.StatusOK)
   918  	writer.Header().Set("Content-Type", "text/html; charset=utf-8")
   919  	this.compressWrite(req.WAFRaw(), writer, []byte(msgHTML))
   920  }
   921  
   922  func (this *CaptchaValidator) compressWrite(req *http.Request, writer http.ResponseWriter, htmlContent []byte) {
   923  	var acceptEncoding = req.Header.Get("Accept-Encoding")
   924  	if strings.Contains(acceptEncoding, "gzip") {
   925  		this.compressGzip(writer, htmlContent)
   926  	} else if strings.Contains(acceptEncoding, "br") {
   927  		this.compressBR(writer, htmlContent)
   928  	} else {
   929  		writer.Header().Set("Content-Length", types.String(len(htmlContent)))
   930  		writer.WriteHeader(http.StatusOK)
   931  		_, _ = writer.Write(htmlContent)
   932  	}
   933  }
   934  
   935  func (this *CaptchaValidator) compressBR(writer http.ResponseWriter, htmlContent []byte) {
   936  	compressWriter, err := compressions.NewBrotliWriter(writer, 0)
   937  	if err != nil {
   938  		writer.Header().Set("Content-Length", types.String(len(htmlContent)))
   939  		writer.WriteHeader(http.StatusOK)
   940  		_, _ = writer.Write(htmlContent)
   941  		return
   942  	}
   943  
   944  	writer.Header().Set("Content-Encoding", "br")
   945  	writer.WriteHeader(http.StatusOK)
   946  	_, _ = compressWriter.Write(htmlContent)
   947  	_ = compressWriter.Close()
   948  }
   949  
   950  func (this *CaptchaValidator) compressGzip(writer http.ResponseWriter, htmlContent []byte) {
   951  	compressWriter, err := compressions.NewGzipWriter(writer, 0)
   952  	if err != nil {
   953  		writer.Header().Set("Content-Length", types.String(len(htmlContent)))
   954  		writer.WriteHeader(http.StatusOK)
   955  		_, _ = writer.Write(htmlContent)
   956  		return
   957  	}
   958  
   959  	writer.Header().Set("Content-Encoding", "gzip")
   960  	writer.WriteHeader(http.StatusOK)
   961  	_, _ = compressWriter.Write(htmlContent)
   962  	_ = compressWriter.Close()
   963  }
   964  
   965  func (this *CaptchaValidator) setCookie(writer http.ResponseWriter, setId int64, life int) {
   966  	if life < 1 {
   967  		return
   968  	}
   969  
   970  	infoString, err := (&AllowCookieInfo{
   971  		SetId:     setId,
   972  		ExpiresAt: time.Now().Unix() + int64(life),
   973  	}).Encode()
   974  	if err != nil {
   975  		return
   976  	}
   977  
   978  	http.SetCookie(writer, &http.Cookie{
   979  		Name:   captchaCookiePrefix + "_" + types.String(setId),
   980  		Value:  infoString,
   981  		MaxAge: life,
   982  		Path:   "/",
   983  	})
   984  }
   985  
   986  const axiosJavascript = "!function(e,t){\"object\"==typeof exports&&\"undefined\"!=typeof module?module.exports=t():\"function\"==typeof define&&define.amd?define(t):(e=\"undefined\"!=typeof globalThis?globalThis:e||self).axios=t()}(this,(function(){\"use strict\";function e(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function t(t){for(var n=1;n<arguments.length;n++){var r=null!=arguments[n]?arguments[n]:{};n%2?e(Object(r),!0).forEach((function(e){a(t,e,r[e])})):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(r)):e(Object(r)).forEach((function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(r,e))}))}return t}function n(e){return n=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&\"function\"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?\"symbol\":typeof e},n(e)}function r(e,t){if(!(e instanceof t))throw new TypeError(\"Cannot call a class as a function\")}function o(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,\"value\"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}function i(e,t,n){return t&&o(e.prototype,t),n&&o(e,n),Object.defineProperty(e,\"prototype\",{writable:!1}),e}function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function s(e,t){return c(e)||function(e,t){var n=null==e?null:\"undefined\"!=typeof Symbol&&e[Symbol.iterator]||e[\"@@iterator\"];if(null==n)return;var r,o,i=[],a=!0,s=!1;try{for(n=n.call(e);!(a=(r=n.next()).done)&&(i.push(r.value),!t||i.length!==t);a=!0);}catch(e){s=!0,o=e}finally{try{a||null==n.return||n.return()}finally{if(s)throw o}}return i}(e,t)||l(e,t)||p()}function u(e){return function(e){if(Array.isArray(e))return d(e)}(e)||f(e)||l(e)||function(){throw new TypeError(\"Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.\")}()}function c(e){if(Array.isArray(e))return e}function f(e){if(\"undefined\"!=typeof Symbol&&null!=e[Symbol.iterator]||null!=e[\"@@iterator\"])return Array.from(e)}function l(e,t){if(e){if(\"string\"==typeof e)return d(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);return\"Object\"===n&&e.constructor&&(n=e.constructor.name),\"Map\"===n||\"Set\"===n?Array.from(e):\"Arguments\"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?d(e,t):void 0}}function d(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n<t;n++)r[n]=e[n];return r}function p(){throw new TypeError(\"Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.\")}function h(e,t){return function(){return e.apply(t,arguments)}}var m,y=Object.prototype.toString,v=Object.getPrototypeOf,b=(m=Object.create(null),function(e){var t=y.call(e);return m[t]||(m[t]=t.slice(8,-1).toLowerCase())}),g=function(e){return e=e.toLowerCase(),function(t){return b(t)===e}},w=function(e){return function(t){return n(t)===e}},O=Array.isArray,E=w(\"undefined\");var S=g(\"ArrayBuffer\");var R=w(\"string\"),A=w(\"function\"),j=w(\"number\"),T=function(e){return null!==e&&\"object\"===n(e)},P=function(e){if(\"object\"!==b(e))return!1;var t=v(e);return!(null!==t&&t!==Object.prototype&&null!==Object.getPrototypeOf(t)||Symbol.toStringTag in e||Symbol.iterator in e)},N=g(\"Date\"),x=g(\"File\"),C=g(\"Blob\"),k=g(\"FileList\"),_=g(\"URLSearchParams\");function F(e,t){var r,o,i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},a=i.allOwnKeys,s=void 0!==a&&a;if(null!=e)if(\"object\"!==n(e)&&(e=[e]),O(e))for(r=0,o=e.length;r<o;r++)t.call(null,e[r],r,e);else{var u,c=s?Object.getOwnPropertyNames(e):Object.keys(e),f=c.length;for(r=0;r<f;r++)u=c[r],t.call(null,e[u],u,e)}}function U(e,t){t=t.toLowerCase();for(var n,r=Object.keys(e),o=r.length;o-- >0;)if(t===(n=r[o]).toLowerCase())return n;return null}var D=\"undefined\"!=typeof globalThis?globalThis:\"undefined\"!=typeof self?self:\"undefined\"!=typeof window?window:global,B=function(e){return!E(e)&&e!==D};var L,I=(L=\"undefined\"!=typeof Uint8Array&&v(Uint8Array),function(e){return L&&e instanceof L}),q=g(\"HTMLFormElement\"),z=function(e){var t=Object.prototype.hasOwnProperty;return function(e,n){return t.call(e,n)}}(),M=g(\"RegExp\"),H=function(e,t){var n=Object.getOwnPropertyDescriptors(e),r={};F(n,(function(n,o){var i;!1!==(i=t(n,o,e))&&(r[o]=i||n)})),Object.defineProperties(e,r)},J=\"abcdefghijklmnopqrstuvwxyz\",W=\"0123456789\",K={DIGIT:W,ALPHA:J,ALPHA_DIGIT:J+J.toUpperCase()+W};var V=g(\"AsyncFunction\"),G={isArray:O,isArrayBuffer:S,isBuffer:function(e){return null!==e&&!E(e)&&null!==e.constructor&&!E(e.constructor)&&A(e.constructor.isBuffer)&&e.constructor.isBuffer(e)},isFormData:function(e){var t;return e&&(\"function\"==typeof FormData&&e instanceof FormData||A(e.append)&&(\"formdata\"===(t=b(e))||\"object\"===t&&A(e.toString)&&\"[object FormData]\"===e.toString()))},isArrayBufferView:function(e){return\"undefined\"!=typeof ArrayBuffer&&ArrayBuffer.isView?ArrayBuffer.isView(e):e&&e.buffer&&S(e.buffer)},isString:R,isNumber:j,isBoolean:function(e){return!0===e||!1===e},isObject:T,isPlainObject:P,isUndefined:E,isDate:N,isFile:x,isBlob:C,isRegExp:M,isFunction:A,isStream:function(e){return T(e)&&A(e.pipe)},isURLSearchParams:_,isTypedArray:I,isFileList:k,forEach:F,merge:function e(){for(var t=B(this)&&this||{},n=t.caseless,r={},o=function(t,o){var i=n&&U(r,o)||o;P(r[i])&&P(t)?r[i]=e(r[i],t):P(t)?r[i]=e({},t):O(t)?r[i]=t.slice():r[i]=t},i=0,a=arguments.length;i<a;i++)arguments[i]&&F(arguments[i],o);return r},extend:function(e,t,n){var r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{},o=r.allOwnKeys;return F(t,(function(t,r){n&&A(t)?e[r]=h(t,n):e[r]=t}),{allOwnKeys:o}),e},trim:function(e){return e.trim?e.trim():e.replace(/^[\\s\\uFEFF\\xA0]+|[\\s\\uFEFF\\xA0]+$/g,\"\")},stripBOM:function(e){return 65279===e.charCodeAt(0)&&(e=e.slice(1)),e},inherits:function(e,t,n,r){e.prototype=Object.create(t.prototype,r),e.prototype.constructor=e,Object.defineProperty(e,\"super\",{value:t.prototype}),n&&Object.assign(e.prototype,n)},toFlatObject:function(e,t,n,r){var o,i,a,s={};if(t=t||{},null==e)return t;do{for(i=(o=Object.getOwnPropertyNames(e)).length;i-- >0;)a=o[i],r&&!r(a,e,t)||s[a]||(t[a]=e[a],s[a]=!0);e=!1!==n&&v(e)}while(e&&(!n||n(e,t))&&e!==Object.prototype);return t},kindOf:b,kindOfTest:g,endsWith:function(e,t,n){e=String(e),(void 0===n||n>e.length)&&(n=e.length),n-=t.length;var r=e.indexOf(t,n);return-1!==r&&r===n},toArray:function(e){if(!e)return null;if(O(e))return e;var t=e.length;if(!j(t))return null;for(var n=new Array(t);t-- >0;)n[t]=e[t];return n},forEachEntry:function(e,t){for(var n,r=(e&&e[Symbol.iterator]).call(e);(n=r.next())&&!n.done;){var o=n.value;t.call(e,o[0],o[1])}},matchAll:function(e,t){for(var n,r=[];null!==(n=e.exec(t));)r.push(n);return r},isHTMLForm:q,hasOwnProperty:z,hasOwnProp:z,reduceDescriptors:H,freezeMethods:function(e){H(e,(function(t,n){if(A(e)&&-1!==[\"arguments\",\"caller\",\"callee\"].indexOf(n))return!1;var r=e[n];A(r)&&(t.enumerable=!1,\"writable\"in t?t.writable=!1:t.set||(t.set=function(){throw Error(\"Can not rewrite read-only method '\"+n+\"'\")}))}))},toObjectSet:function(e,t){var n={},r=function(e){e.forEach((function(e){n[e]=!0}))};return O(e)?r(e):r(String(e).split(t)),n},toCamelCase:function(e){return e.toLowerCase().replace(/[-_\\s]([a-z\\d])(\\w*)/g,(function(e,t,n){return t.toUpperCase()+n}))},noop:function(){},toFiniteNumber:function(e,t){return e=+e,Number.isFinite(e)?e:t},findKey:U,global:D,isContextDefined:B,ALPHABET:K,generateString:function(){for(var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:16,t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:K.ALPHA_DIGIT,n=\"\",r=t.length;e--;)n+=t[Math.random()*r|0];return n},isSpecCompliantForm:function(e){return!!(e&&A(e.append)&&\"FormData\"===e[Symbol.toStringTag]&&e[Symbol.iterator])},toJSONObject:function(e){var t=new Array(10);return function e(n,r){if(T(n)){if(t.indexOf(n)>=0)return;if(!(\"toJSON\"in n)){t[r]=n;var o=O(n)?[]:{};return F(n,(function(t,n){var i=e(t,r+1);!E(i)&&(o[n]=i)})),t[r]=void 0,o}}return n}(e,0)},isAsyncFn:V,isThenable:function(e){return e&&(T(e)||A(e))&&A(e.then)&&A(e.catch)}};function X(e,t,n,r,o){Error.call(this),Error.captureStackTrace?Error.captureStackTrace(this,this.constructor):this.stack=(new Error).stack,this.message=e,this.name=\"AxiosError\",t&&(this.code=t),n&&(this.config=n),r&&(this.request=r),o&&(this.response=o)}G.inherits(X,Error,{toJSON:function(){return{message:this.message,name:this.name,description:this.description,number:this.number,fileName:this.fileName,lineNumber:this.lineNumber,columnNumber:this.columnNumber,stack:this.stack,config:G.toJSONObject(this.config),code:this.code,status:this.response&&this.response.status?this.response.status:null}}});var $=X.prototype,Q={};[\"ERR_BAD_OPTION_VALUE\",\"ERR_BAD_OPTION\",\"ECONNABORTED\",\"ETIMEDOUT\",\"ERR_NETWORK\",\"ERR_FR_TOO_MANY_REDIRECTS\",\"ERR_DEPRECATED\",\"ERR_BAD_RESPONSE\",\"ERR_BAD_REQUEST\",\"ERR_CANCELED\",\"ERR_NOT_SUPPORT\",\"ERR_INVALID_URL\"].forEach((function(e){Q[e]={value:e}})),Object.defineProperties(X,Q),Object.defineProperty($,\"isAxiosError\",{value:!0}),X.from=function(e,t,n,r,o,i){var a=Object.create($);return G.toFlatObject(e,a,(function(e){return e!==Error.prototype}),(function(e){return\"isAxiosError\"!==e})),X.call(a,e.message,t,n,r,o),a.cause=e,a.name=e.name,i&&Object.assign(a,i),a};function Z(e){return G.isPlainObject(e)||G.isArray(e)}function Y(e){return G.endsWith(e,\"[]\")?e.slice(0,-2):e}function ee(e,t,n){return e?e.concat(t).map((function(e,t){return e=Y(e),!n&&t?\"[\"+e+\"]\":e})).join(n?\".\":\"\"):t}var te=G.toFlatObject(G,{},null,(function(e){return/^is[A-Z]/.test(e)}));function ne(e,t,r){if(!G.isObject(e))throw new TypeError(\"target must be an object\");t=t||new FormData;var o=(r=G.toFlatObject(r,{metaTokens:!0,dots:!1,indexes:!1},!1,(function(e,t){return!G.isUndefined(t[e])}))).metaTokens,i=r.visitor||f,a=r.dots,s=r.indexes,u=(r.Blob||\"undefined\"!=typeof Blob&&Blob)&&G.isSpecCompliantForm(t);if(!G.isFunction(i))throw new TypeError(\"visitor must be a function\");function c(e){if(null===e)return\"\";if(G.isDate(e))return e.toISOString();if(!u&&G.isBlob(e))throw new X(\"Blob is not supported. Use a Buffer instead.\");return G.isArrayBuffer(e)||G.isTypedArray(e)?u&&\"function\"==typeof Blob?new Blob([e]):Buffer.from(e):e}function f(e,r,i){var u=e;if(e&&!i&&\"object\"===n(e))if(G.endsWith(r,\"{}\"))r=o?r:r.slice(0,-2),e=JSON.stringify(e);else if(G.isArray(e)&&function(e){return G.isArray(e)&&!e.some(Z)}(e)||(G.isFileList(e)||G.endsWith(r,\"[]\"))&&(u=G.toArray(e)))return r=Y(r),u.forEach((function(e,n){!G.isUndefined(e)&&null!==e&&t.append(!0===s?ee([r],n,a):null===s?r:r+\"[]\",c(e))})),!1;return!!Z(e)||(t.append(ee(i,r,a),c(e)),!1)}var l=[],d=Object.assign(te,{defaultVisitor:f,convertValue:c,isVisitable:Z});if(!G.isObject(e))throw new TypeError(\"data must be an object\");return function e(n,r){if(!G.isUndefined(n)){if(-1!==l.indexOf(n))throw Error(\"Circular reference detected in \"+r.join(\".\"));l.push(n),G.forEach(n,(function(n,o){!0===(!(G.isUndefined(n)||null===n)&&i.call(t,n,G.isString(o)?o.trim():o,r,d))&&e(n,r?r.concat(o):[o])})),l.pop()}}(e),t}function re(e){var t={\"!\":\"%21\",\"'\":\"%27\",\"(\":\"%28\",\")\":\"%29\",\"~\":\"%7E\",\"%20\":\"+\",\"%00\":\"\\0\"};return encodeURIComponent(e).replace(/[!'()~]|%20|%00/g,(function(e){return t[e]}))}function oe(e,t){this._pairs=[],e&&ne(e,this,t)}var ie=oe.prototype;function ae(e){return encodeURIComponent(e).replace(/%3A/gi,\":\").replace(/%24/g,\"$\").replace(/%2C/gi,\",\").replace(/%20/g,\"+\").replace(/%5B/gi,\"[\").replace(/%5D/gi,\"]\")}function se(e,t,n){if(!t)return e;var r,o=n&&n.encode||ae,i=n&&n.serialize;if(r=i?i(t,n):G.isURLSearchParams(t)?t.toString():new oe(t,n).toString(o)){var a=e.indexOf(\"#\");-1!==a&&(e=e.slice(0,a)),e+=(-1===e.indexOf(\"?\")?\"?\":\"&\")+r}return e}ie.append=function(e,t){this._pairs.push([e,t])},ie.toString=function(e){var t=e?function(t){return e.call(this,t,re)}:re;return this._pairs.map((function(e){return t(e[0])+\"=\"+t(e[1])}),\"\").join(\"&\")};var ue,ce=function(){function e(){r(this,e),this.handlers=[]}return i(e,[{key:\"use\",value:function(e,t,n){return this.handlers.push({fulfilled:e,rejected:t,synchronous:!!n&&n.synchronous,runWhen:n?n.runWhen:null}),this.handlers.length-1}},{key:\"eject\",value:function(e){this.handlers[e]&&(this.handlers[e]=null)}},{key:\"clear\",value:function(){this.handlers&&(this.handlers=[])}},{key:\"forEach\",value:function(e){G.forEach(this.handlers,(function(t){null!==t&&e(t)}))}}]),e}(),fe={silentJSONParsing:!0,forcedJSONParsing:!0,clarifyTimeoutError:!1},le={isBrowser:!0,classes:{URLSearchParams:\"undefined\"!=typeof URLSearchParams?URLSearchParams:oe,FormData:\"undefined\"!=typeof FormData?FormData:null,Blob:\"undefined\"!=typeof Blob?Blob:null},protocols:[\"http\",\"https\",\"file\",\"blob\",\"url\",\"data\"]},de=\"undefined\"!=typeof window&&\"undefined\"!=typeof document,pe=(ue=\"undefined\"!=typeof navigator&&navigator.product,de&&[\"ReactNative\",\"NativeScript\",\"NS\"].indexOf(ue)<0),he=\"undefined\"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope&&\"function\"==typeof self.importScripts,me=t(t({},Object.freeze({__proto__:null,hasBrowserEnv:de,hasStandardBrowserWebWorkerEnv:he,hasStandardBrowserEnv:pe})),le);function ye(e){function t(e,n,r,o){var i=e[o++],a=Number.isFinite(+i),s=o>=e.length;return i=!i&&G.isArray(r)?r.length:i,s?(G.hasOwnProp(r,i)?r[i]=[r[i],n]:r[i]=n,!a):(r[i]&&G.isObject(r[i])||(r[i]=[]),t(e,n,r[i],o)&&G.isArray(r[i])&&(r[i]=function(e){var t,n,r={},o=Object.keys(e),i=o.length;for(t=0;t<i;t++)r[n=o[t]]=e[n];return r}(r[i])),!a)}if(G.isFormData(e)&&G.isFunction(e.entries)){var n={};return G.forEachEntry(e,(function(e,r){t(function(e){return G.matchAll(/\\w+|\\[(\\w*)]/g,e).map((function(e){return\"[]\"===e[0]?\"\":e[1]||e[0]}))}(e),r,n,0)})),n}return null}var ve={transitional:fe,adapter:[\"xhr\",\"http\"],transformRequest:[function(e,t){var n,r=t.getContentType()||\"\",o=r.indexOf(\"application/json\")>-1,i=G.isObject(e);if(i&&G.isHTMLForm(e)&&(e=new FormData(e)),G.isFormData(e))return o&&o?JSON.stringify(ye(e)):e;if(G.isArrayBuffer(e)||G.isBuffer(e)||G.isStream(e)||G.isFile(e)||G.isBlob(e))return e;if(G.isArrayBufferView(e))return e.buffer;if(G.isURLSearchParams(e))return t.setContentType(\"application/x-www-form-urlencoded;charset=utf-8\",!1),e.toString();if(i){if(r.indexOf(\"application/x-www-form-urlencoded\")>-1)return function(e,t){return ne(e,new me.classes.URLSearchParams,Object.assign({visitor:function(e,t,n,r){return me.isNode&&G.isBuffer(e)?(this.append(t,e.toString(\"base64\")),!1):r.defaultVisitor.apply(this,arguments)}},t))}(e,this.formSerializer).toString();if((n=G.isFileList(e))||r.indexOf(\"multipart/form-data\")>-1){var a=this.env&&this.env.FormData;return ne(n?{\"files[]\":e}:e,a&&new a,this.formSerializer)}}return i||o?(t.setContentType(\"application/json\",!1),function(e,t,n){if(G.isString(e))try{return(t||JSON.parse)(e),G.trim(e)}catch(e){if(\"SyntaxError\"!==e.name)throw e}return(n||JSON.stringify)(e)}(e)):e}],transformResponse:[function(e){var t=this.transitional||ve.transitional,n=t&&t.forcedJSONParsing,r=\"json\"===this.responseType;if(e&&G.isString(e)&&(n&&!this.responseType||r)){var o=!(t&&t.silentJSONParsing)&&r;try{return JSON.parse(e)}catch(e){if(o){if(\"SyntaxError\"===e.name)throw X.from(e,X.ERR_BAD_RESPONSE,this,null,this.response);throw e}}}return e}],timeout:0,xsrfCookieName:\"XSRF-TOKEN\",xsrfHeaderName:\"X-XSRF-TOKEN\",maxContentLength:-1,maxBodyLength:-1,env:{FormData:me.classes.FormData,Blob:me.classes.Blob},validateStatus:function(e){return e>=200&&e<300},headers:{common:{Accept:\"application/json, text/plain, */*\",\"Content-Type\":void 0}}};G.forEach([\"delete\",\"get\",\"head\",\"post\",\"put\",\"patch\"],(function(e){ve.headers[e]={}}));var be=ve,ge=G.toObjectSet([\"age\",\"authorization\",\"content-length\",\"content-type\",\"etag\",\"expires\",\"from\",\"host\",\"if-modified-since\",\"if-unmodified-since\",\"last-modified\",\"location\",\"max-forwards\",\"proxy-authorization\",\"referer\",\"retry-after\",\"user-agent\"]),we=Symbol(\"internals\");function Oe(e){return e&&String(e).trim().toLowerCase()}function Ee(e){return!1===e||null==e?e:G.isArray(e)?e.map(Ee):String(e)}function Se(e,t,n,r,o){return G.isFunction(r)?r.call(this,t,n):(o&&(t=n),G.isString(t)?G.isString(r)?-1!==t.indexOf(r):G.isRegExp(r)?r.test(t):void 0:void 0)}var Re=function(e,t){function n(e){r(this,n),e&&this.set(e)}return i(n,[{key:\"set\",value:function(e,t,n){var r=this;function o(e,t,n){var o=Oe(t);if(!o)throw new Error(\"header name must be a non-empty string\");var i=G.findKey(r,o);(!i||void 0===r[i]||!0===n||void 0===n&&!1!==r[i])&&(r[i||t]=Ee(e))}var i,a,s,u,c,f=function(e,t){return G.forEach(e,(function(e,n){return o(e,n,t)}))};return G.isPlainObject(e)||e instanceof this.constructor?f(e,t):G.isString(e)&&(e=e.trim())&&!/^[-_a-zA-Z0-9^`|~,!#$%&'*+.]+$/.test(e.trim())?f((c={},(i=e)&&i.split(\"\\n\").forEach((function(e){u=e.indexOf(\":\"),a=e.substring(0,u).trim().toLowerCase(),s=e.substring(u+1).trim(),!a||c[a]&&ge[a]||(\"set-cookie\"===a?c[a]?c[a].push(s):c[a]=[s]:c[a]=c[a]?c[a]+\", \"+s:s)})),c),t):null!=e&&o(t,e,n),this}},{key:\"get\",value:function(e,t){if(e=Oe(e)){var n=G.findKey(this,e);if(n){var r=this[n];if(!t)return r;if(!0===t)return function(e){for(var t,n=Object.create(null),r=/([^\\s,;=]+)\\s*(?:=\\s*([^,;]+))?/g;t=r.exec(e);)n[t[1]]=t[2];return n}(r);if(G.isFunction(t))return t.call(this,r,n);if(G.isRegExp(t))return t.exec(r);throw new TypeError(\"parser must be boolean|regexp|function\")}}}},{key:\"has\",value:function(e,t){if(e=Oe(e)){var n=G.findKey(this,e);return!(!n||void 0===this[n]||t&&!Se(0,this[n],n,t))}return!1}},{key:\"delete\",value:function(e,t){var n=this,r=!1;function o(e){if(e=Oe(e)){var o=G.findKey(n,e);!o||t&&!Se(0,n[o],o,t)||(delete n[o],r=!0)}}return G.isArray(e)?e.forEach(o):o(e),r}},{key:\"clear\",value:function(e){for(var t=Object.keys(this),n=t.length,r=!1;n--;){var o=t[n];e&&!Se(0,this[o],o,e,!0)||(delete this[o],r=!0)}return r}},{key:\"normalize\",value:function(e){var t=this,n={};return G.forEach(this,(function(r,o){var i=G.findKey(n,o);if(i)return t[i]=Ee(r),void delete t[o];var a=e?function(e){return e.trim().toLowerCase().replace(/([a-z\\d])(\\w*)/g,(function(e,t,n){return t.toUpperCase()+n}))}(o):String(o).trim();a!==o&&delete t[o],t[a]=Ee(r),n[a]=!0})),this}},{key:\"concat\",value:function(){for(var e,t=arguments.length,n=new Array(t),r=0;r<t;r++)n[r]=arguments[r];return(e=this.constructor).concat.apply(e,[this].concat(n))}},{key:\"toJSON\",value:function(e){var t=Object.create(null);return G.forEach(this,(function(n,r){null!=n&&!1!==n&&(t[r]=e&&G.isArray(n)?n.join(\", \"):n)})),t}},{key:Symbol.iterator,value:function(){return Object.entries(this.toJSON())[Symbol.iterator]()}},{key:\"toString\",value:function(){return Object.entries(this.toJSON()).map((function(e){var t=s(e,2);return t[0]+\": \"+t[1]})).join(\"\\n\")}},{key:Symbol.toStringTag,get:function(){return\"AxiosHeaders\"}}],[{key:\"from\",value:function(e){return e instanceof this?e:new this(e)}},{key:\"concat\",value:function(e){for(var t=new this(e),n=arguments.length,r=new Array(n>1?n-1:0),o=1;o<n;o++)r[o-1]=arguments[o];return r.forEach((function(e){return t.set(e)})),t}},{key:\"accessor\",value:function(e){var t=(this[we]=this[we]={accessors:{}}).accessors,n=this.prototype;function r(e){var r=Oe(e);t[r]||(!function(e,t){var n=G.toCamelCase(\" \"+t);[\"get\",\"set\",\"has\"].forEach((function(r){Object.defineProperty(e,r+n,{value:function(e,n,o){return this[r].call(this,t,e,n,o)},configurable:!0})}))}(n,e),t[r]=!0)}return G.isArray(e)?e.forEach(r):r(e),this}}]),n}();Re.accessor([\"Content-Type\",\"Content-Length\",\"Accept\",\"Accept-Encoding\",\"User-Agent\",\"Authorization\"]),G.reduceDescriptors(Re.prototype,(function(e,t){var n=e.value,r=t[0].toUpperCase()+t.slice(1);return{get:function(){return n},set:function(e){this[r]=e}}})),G.freezeMethods(Re);var Ae=Re;function je(e,t){var n=this||be,r=t||n,o=Ae.from(r.headers),i=r.data;return G.forEach(e,(function(e){i=e.call(n,i,o.normalize(),t?t.status:void 0)})),o.normalize(),i}function Te(e){return!(!e||!e.__CANCEL__)}function Pe(e,t,n){X.call(this,null==e?\"canceled\":e,X.ERR_CANCELED,t,n),this.name=\"CanceledError\"}G.inherits(Pe,X,{__CANCEL__:!0});var Ne=me.hasStandardBrowserEnv?{write:function(e,t,n,r,o,i){var a=[e+\"=\"+encodeURIComponent(t)];G.isNumber(n)&&a.push(\"expires=\"+new Date(n).toGMTString()),G.isString(r)&&a.push(\"path=\"+r),G.isString(o)&&a.push(\"domain=\"+o),!0===i&&a.push(\"secure\"),document.cookie=a.join(\"; \")},read:function(e){var t=document.cookie.match(new RegExp(\"(^|;\\\\s*)(\"+e+\")=([^;]*)\"));return t?decodeURIComponent(t[3]):null},remove:function(e){this.write(e,\"\",Date.now()-864e5)}}:{write:function(){},read:function(){return null},remove:function(){}};function xe(e,t){return e&&!/^([a-z][a-z\\d+\\-.]*:)?\\/\\//i.test(t)?function(e,t){return t?e.replace(/\\/+$/,\"\")+\"/\"+t.replace(/^\\/+/,\"\"):e}(e,t):t}var Ce=me.hasStandardBrowserEnv?function(){var e,t=/(msie|trident)/i.test(navigator.userAgent),n=document.createElement(\"a\");function r(e){var r=e;return t&&(n.setAttribute(\"href\",r),r=n.href),n.setAttribute(\"href\",r),{href:n.href,protocol:n.protocol?n.protocol.replace(/:$/,\"\"):\"\",host:n.host,search:n.search?n.search.replace(/^\\?/,\"\"):\"\",hash:n.hash?n.hash.replace(/^#/,\"\"):\"\",hostname:n.hostname,port:n.port,pathname:\"/\"===n.pathname.charAt(0)?n.pathname:\"/\"+n.pathname}}return e=r(window.location.href),function(t){var n=G.isString(t)?r(t):t;return n.protocol===e.protocol&&n.host===e.host}}():function(){return!0};function ke(e,t){var n=0,r=function(e,t){e=e||10;var n,r=new Array(e),o=new Array(e),i=0,a=0;return t=void 0!==t?t:1e3,function(s){var u=Date.now(),c=o[a];n||(n=u),r[i]=s,o[i]=u;for(var f=a,l=0;f!==i;)l+=r[f++],f%=e;if((i=(i+1)%e)===a&&(a=(a+1)%e),!(u-n<t)){var d=c&&u-c;return d?Math.round(1e3*l/d):void 0}}}(50,250);return function(o){var i=o.loaded,a=o.lengthComputable?o.total:void 0,s=i-n,u=r(s);n=i;var c={loaded:i,total:a,progress:a?i/a:void 0,bytes:s,rate:u||void 0,estimated:u&&a&&i<=a?(a-i)/u:void 0,event:o};c[t?\"download\":\"upload\"]=!0,e(c)}}var _e={http:null,xhr:\"undefined\"!=typeof XMLHttpRequest&&function(e){return new Promise((function(t,n){var r,o,i,a=e.data,s=Ae.from(e.headers).normalize(),d=e.responseType,h=e.withXSRFToken;function m(){e.cancelToken&&e.cancelToken.unsubscribe(r),e.signal&&e.signal.removeEventListener(\"abort\",r)}if(G.isFormData(a))if(me.hasStandardBrowserEnv||me.hasStandardBrowserWebWorkerEnv)s.setContentType(!1);else if(!1!==(o=s.getContentType())){var y=o?o.split(\";\").map((function(e){return e.trim()})).filter(Boolean):[],v=c(i=y)||f(i)||l(i)||p(),b=v[0],g=v.slice(1);s.setContentType([b||\"multipart/form-data\"].concat(u(g)).join(\"; \"))}var w=new XMLHttpRequest;if(e.auth){var O=e.auth.username||\"\",E=e.auth.password?unescape(encodeURIComponent(e.auth.password)):\"\";s.set(\"Authorization\",\"Basic \"+btoa(O+\":\"+E))}var S=xe(e.baseURL,e.url);function R(){if(w){var r=Ae.from(\"getAllResponseHeaders\"in w&&w.getAllResponseHeaders());!function(e,t,n){var r=n.config.validateStatus;n.status&&r&&!r(n.status)?t(new X(\"Request failed with status code \"+n.status,[X.ERR_BAD_REQUEST,X.ERR_BAD_RESPONSE][Math.floor(n.status/100)-4],n.config,n.request,n)):e(n)}((function(e){t(e),m()}),(function(e){n(e),m()}),{data:d&&\"text\"!==d&&\"json\"!==d?w.response:w.responseText,status:w.status,statusText:w.statusText,headers:r,config:e,request:w}),w=null}}if(w.open(e.method.toUpperCase(),se(S,e.params,e.paramsSerializer),!0),w.timeout=e.timeout,\"onloadend\"in w?w.onloadend=R:w.onreadystatechange=function(){w&&4===w.readyState&&(0!==w.status||w.responseURL&&0===w.responseURL.indexOf(\"file:\"))&&setTimeout(R)},w.onabort=function(){w&&(n(new X(\"Request aborted\",X.ECONNABORTED,e,w)),w=null)},w.onerror=function(){n(new X(\"Network Error\",X.ERR_NETWORK,e,w)),w=null},w.ontimeout=function(){var t=e.timeout?\"timeout of \"+e.timeout+\"ms exceeded\":\"timeout exceeded\",r=e.transitional||fe;e.timeoutErrorMessage&&(t=e.timeoutErrorMessage),n(new X(t,r.clarifyTimeoutError?X.ETIMEDOUT:X.ECONNABORTED,e,w)),w=null},me.hasStandardBrowserEnv&&(h&&G.isFunction(h)&&(h=h(e)),h||!1!==h&&Ce(S))){var A=e.xsrfHeaderName&&e.xsrfCookieName&&Ne.read(e.xsrfCookieName);A&&s.set(e.xsrfHeaderName,A)}void 0===a&&s.setContentType(null),\"setRequestHeader\"in w&&G.forEach(s.toJSON(),(function(e,t){w.setRequestHeader(t,e)})),G.isUndefined(e.withCredentials)||(w.withCredentials=!!e.withCredentials),d&&\"json\"!==d&&(w.responseType=e.responseType),\"function\"==typeof e.onDownloadProgress&&w.addEventListener(\"progress\",ke(e.onDownloadProgress,!0)),\"function\"==typeof e.onUploadProgress&&w.upload&&w.upload.addEventListener(\"progress\",ke(e.onUploadProgress)),(e.cancelToken||e.signal)&&(r=function(t){w&&(n(!t||t.type?new Pe(null,e,w):t),w.abort(),w=null)},e.cancelToken&&e.cancelToken.subscribe(r),e.signal&&(e.signal.aborted?r():e.signal.addEventListener(\"abort\",r)));var j,T=(j=/^([-+\\w]{1,25})(:?\\/\\/|:)/.exec(S))&&j[1]||\"\";T&&-1===me.protocols.indexOf(T)?n(new X(\"Unsupported protocol \"+T+\":\",X.ERR_BAD_REQUEST,e)):w.send(a||null)}))}};G.forEach(_e,(function(e,t){if(e){try{Object.defineProperty(e,\"name\",{value:t})}catch(e){}Object.defineProperty(e,\"adapterName\",{value:t})}}));var Fe=function(e){return\"- \".concat(e)},Ue=function(e){return G.isFunction(e)||null===e||!1===e},De=function(e){for(var t,n,r=(e=G.isArray(e)?e:[e]).length,o={},i=0;i<r;i++){var a=void 0;if(n=t=e[i],!Ue(t)&&void 0===(n=_e[(a=String(t)).toLowerCase()]))throw new X(\"Unknown adapter '\".concat(a,\"'\"));if(n)break;o[a||\"#\"+i]=n}if(!n){var u=Object.entries(o).map((function(e){var t=s(e,2),n=t[0],r=t[1];return\"adapter \".concat(n,\" \")+(!1===r?\"is not supported by the environment\":\"is not available in the build\")}));throw new X(\"There is no suitable adapter to dispatch the request \"+(r?u.length>1?\"since :\\n\"+u.map(Fe).join(\"\\n\"):\" \"+Fe(u[0]):\"as no adapter specified\"),\"ERR_NOT_SUPPORT\")}return n};function Be(e){if(e.cancelToken&&e.cancelToken.throwIfRequested(),e.signal&&e.signal.aborted)throw new Pe(null,e)}function Le(e){return Be(e),e.headers=Ae.from(e.headers),e.data=je.call(e,e.transformRequest),-1!==[\"post\",\"put\",\"patch\"].indexOf(e.method)&&e.headers.setContentType(\"application/x-www-form-urlencoded\",!1),De(e.adapter||be.adapter)(e).then((function(t){return Be(e),t.data=je.call(e,e.transformResponse,t),t.headers=Ae.from(t.headers),t}),(function(t){return Te(t)||(Be(e),t&&t.response&&(t.response.data=je.call(e,e.transformResponse,t.response),t.response.headers=Ae.from(t.response.headers))),Promise.reject(t)}))}var Ie=function(e){return e instanceof Ae?e.toJSON():e};function qe(e,t){t=t||{};var n={};function r(e,t,n){return G.isPlainObject(e)&&G.isPlainObject(t)?G.merge.call({caseless:n},e,t):G.isPlainObject(t)?G.merge({},t):G.isArray(t)?t.slice():t}function o(e,t,n){return G.isUndefined(t)?G.isUndefined(e)?void 0:r(void 0,e,n):r(e,t,n)}function i(e,t){if(!G.isUndefined(t))return r(void 0,t)}function a(e,t){return G.isUndefined(t)?G.isUndefined(e)?void 0:r(void 0,e):r(void 0,t)}function s(n,o,i){return i in t?r(n,o):i in e?r(void 0,n):void 0}var u={url:i,method:i,data:i,baseURL:a,transformRequest:a,transformResponse:a,paramsSerializer:a,timeout:a,timeoutMessage:a,withCredentials:a,withXSRFToken:a,adapter:a,responseType:a,xsrfCookieName:a,xsrfHeaderName:a,onUploadProgress:a,onDownloadProgress:a,decompress:a,maxContentLength:a,maxBodyLength:a,beforeRedirect:a,transport:a,httpAgent:a,httpsAgent:a,cancelToken:a,socketPath:a,responseEncoding:a,validateStatus:s,headers:function(e,t){return o(Ie(e),Ie(t),!0)}};return G.forEach(Object.keys(Object.assign({},e,t)),(function(r){var i=u[r]||o,a=i(e[r],t[r],r);G.isUndefined(a)&&i!==s||(n[r]=a)})),n}var ze=\"1.6.2\",Me={};[\"object\",\"boolean\",\"number\",\"function\",\"string\",\"symbol\"].forEach((function(e,t){Me[e]=function(r){return n(r)===e||\"a\"+(t<1?\"n \":\" \")+e}}));var He={};Me.transitional=function(e,t,n){function r(e,t){return\"[Axios v1.6.2] Transitional option '\"+e+\"'\"+t+(n?\". \"+n:\"\")}return function(n,o,i){if(!1===e)throw new X(r(o,\" has been removed\"+(t?\" in \"+t:\"\")),X.ERR_DEPRECATED);return t&&!He[o]&&(He[o]=!0,console.warn(r(o,\" has been deprecated since v\"+t+\" and will be removed in the near future\"))),!e||e(n,o,i)}};var Je={assertOptions:function(e,t,r){if(\"object\"!==n(e))throw new X(\"options must be an object\",X.ERR_BAD_OPTION_VALUE);for(var o=Object.keys(e),i=o.length;i-- >0;){var a=o[i],s=t[a];if(s){var u=e[a],c=void 0===u||s(u,a,e);if(!0!==c)throw new X(\"option \"+a+\" must be \"+c,X.ERR_BAD_OPTION_VALUE)}else if(!0!==r)throw new X(\"Unknown option \"+a,X.ERR_BAD_OPTION)}},validators:Me},We=Je.validators,Ke=function(){function e(t){r(this,e),this.defaults=t,this.interceptors={request:new ce,response:new ce}}return i(e,[{key:\"request\",value:function(e,t){\"string\"==typeof e?(t=t||{}).url=e:t=e||{};var n=t=qe(this.defaults,t),r=n.transitional,o=n.paramsSerializer,i=n.headers;void 0!==r&&Je.assertOptions(r,{silentJSONParsing:We.transitional(We.boolean),forcedJSONParsing:We.transitional(We.boolean),clarifyTimeoutError:We.transitional(We.boolean)},!1),null!=o&&(G.isFunction(o)?t.paramsSerializer={serialize:o}:Je.assertOptions(o,{encode:We.function,serialize:We.function},!0)),t.method=(t.method||this.defaults.method||\"get\").toLowerCase();var a=i&&G.merge(i.common,i[t.method]);i&&G.forEach([\"delete\",\"get\",\"head\",\"post\",\"put\",\"patch\",\"common\"],(function(e){delete i[e]})),t.headers=Ae.concat(a,i);var s=[],u=!0;this.interceptors.request.forEach((function(e){\"function\"==typeof e.runWhen&&!1===e.runWhen(t)||(u=u&&e.synchronous,s.unshift(e.fulfilled,e.rejected))}));var c,f=[];this.interceptors.response.forEach((function(e){f.push(e.fulfilled,e.rejected)}));var l,d=0;if(!u){var p=[Le.bind(this),void 0];for(p.unshift.apply(p,s),p.push.apply(p,f),l=p.length,c=Promise.resolve(t);d<l;)c=c.then(p[d++],p[d++]);return c}l=s.length;var h=t;for(d=0;d<l;){var m=s[d++],y=s[d++];try{h=m(h)}catch(e){y.call(this,e);break}}try{c=Le.call(this,h)}catch(e){return Promise.reject(e)}for(d=0,l=f.length;d<l;)c=c.then(f[d++],f[d++]);return c}},{key:\"getUri\",value:function(e){return se(xe((e=qe(this.defaults,e)).baseURL,e.url),e.params,e.paramsSerializer)}}]),e}();G.forEach([\"delete\",\"get\",\"head\",\"options\"],(function(e){Ke.prototype[e]=function(t,n){return this.request(qe(n||{},{method:e,url:t,data:(n||{}).data}))}})),G.forEach([\"post\",\"put\",\"patch\"],(function(e){function t(t){return function(n,r,o){return this.request(qe(o||{},{method:e,headers:t?{\"Content-Type\":\"multipart/form-data\"}:{},url:n,data:r}))}}Ke.prototype[e]=t(),Ke.prototype[e+\"Form\"]=t(!0)}));var Ve=Ke,Ge=function(){function e(t){if(r(this,e),\"function\"!=typeof t)throw new TypeError(\"executor must be a function.\");var n;this.promise=new Promise((function(e){n=e}));var o=this;this.promise.then((function(e){if(o._listeners){for(var t=o._listeners.length;t-- >0;)o._listeners[t](e);o._listeners=null}})),this.promise.then=function(e){var t,n=new Promise((function(e){o.subscribe(e),t=e})).then(e);return n.cancel=function(){o.unsubscribe(t)},n},t((function(e,t,r){o.reason||(o.reason=new Pe(e,t,r),n(o.reason))}))}return i(e,[{key:\"throwIfRequested\",value:function(){if(this.reason)throw this.reason}},{key:\"subscribe\",value:function(e){this.reason?e(this.reason):this._listeners?this._listeners.push(e):this._listeners=[e]}},{key:\"unsubscribe\",value:function(e){if(this._listeners){var t=this._listeners.indexOf(e);-1!==t&&this._listeners.splice(t,1)}}}],[{key:\"source\",value:function(){var t;return{token:new e((function(e){t=e})),cancel:t}}}]),e}();var Xe={Continue:100,SwitchingProtocols:101,Processing:102,EarlyHints:103,Ok:200,Created:201,Accepted:202,NonAuthoritativeInformation:203,NoContent:204,ResetContent:205,PartialContent:206,MultiStatus:207,AlreadyReported:208,ImUsed:226,MultipleChoices:300,MovedPermanently:301,Found:302,SeeOther:303,NotModified:304,UseProxy:305,Unused:306,TemporaryRedirect:307,PermanentRedirect:308,BadRequest:400,Unauthorized:401,PaymentRequired:402,Forbidden:403,NotFound:404,MethodNotAllowed:405,NotAcceptable:406,ProxyAuthenticationRequired:407,RequestTimeout:408,Conflict:409,Gone:410,LengthRequired:411,PreconditionFailed:412,PayloadTooLarge:413,UriTooLong:414,UnsupportedMediaType:415,RangeNotSatisfiable:416,ExpectationFailed:417,ImATeapot:418,MisdirectedRequest:421,UnprocessableEntity:422,Locked:423,FailedDependency:424,TooEarly:425,UpgradeRequired:426,PreconditionRequired:428,TooManyRequests:429,RequestHeaderFieldsTooLarge:431,UnavailableForLegalReasons:451,InternalServerError:500,NotImplemented:501,BadGateway:502,ServiceUnavailable:503,GatewayTimeout:504,HttpVersionNotSupported:505,VariantAlsoNegotiates:506,InsufficientStorage:507,LoopDetected:508,NotExtended:510,NetworkAuthenticationRequired:511};Object.entries(Xe).forEach((function(e){var t=s(e,2),n=t[0],r=t[1];Xe[r]=n}));var $e=Xe;var Qe=function e(t){var n=new Ve(t),r=h(Ve.prototype.request,n);return G.extend(r,Ve.prototype,n,{allOwnKeys:!0}),G.extend(r,n,null,{allOwnKeys:!0}),r.create=function(n){return e(qe(t,n))},r}(be);return Qe.Axios=Ve,Qe.CanceledError=Pe,Qe.CancelToken=Ge,Qe.isCancel=Te,Qe.VERSION=ze,Qe.toFormData=ne,Qe.AxiosError=X,Qe.Cancel=Qe.CanceledError,Qe.all=function(e){return Promise.all(e)},Qe.spread=function(e){return function(t){return e.apply(null,t)}},Qe.isAxiosError=function(e){return G.isObject(e)&&!0===e.isAxiosError},Qe.mergeConfig=qe,Qe.AxiosHeaders=Ae,Qe.formToJSON=function(e){return ye(G.isHTMLForm(e)?new FormData(e):e)},Qe.getAdapter=De,Qe.HttpStatusCode=$e,Qe.default=Qe,Qe}));"
   987  
   988  var geeTestHTTPClient = &http.Client{Timeout: 5 * time.Second}