* Update files in /app * Update files in /plugin * Update files in /store * Update files in /utils * Update files in /web * Update store.go * Update command_response.go * check-mocks and check-store-layer checks * Fix build errors * Revert "Fix build errors" This reverts commit 4ee38c3d0bf7bd7d8386f46f0985a0d03245a1d4. * Update .golangci.yml * make i18n-extract and make i18n-check * Commit suggestions * check-mocks and check-store-layers * Update en.json * Update product_notices.go * Update main.go * Fix translations * Regenerate mocks Co-authored-by: Mattermod <mattermod@users.noreply.github.com> Co-authored-by: Claudio Costa <cstcld91@gmail.com>
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
5f190b5624
Коммит
6356e906e0
@@ -115,7 +115,7 @@ func RenderWebError(config *model.Config, w http.ResponseWriter, r *http.Request
|
||||
fmt.Fprintln(w, `</body></html>`)
|
||||
}
|
||||
|
||||
func RenderMobileAuthComplete(w http.ResponseWriter, redirectUrl string) {
|
||||
func RenderMobileAuthComplete(w http.ResponseWriter, redirectURL string) {
|
||||
RenderMobileMessage(w, `
|
||||
<div class="icon text-success" style="font-size: 4em">
|
||||
<i class="fa fa-check-circle" title="Success Icon"></i>
|
||||
@@ -123,13 +123,13 @@ func RenderMobileAuthComplete(w http.ResponseWriter, redirectUrl string) {
|
||||
<h2> `+T("api.oauth.auth_complete")+` </h2>
|
||||
<p id="redirecting-message"> `+T("api.oauth.redirecting_back")+` </p>
|
||||
<p id="close-tab-message" style="display: none"> `+T("api.oauth.close_browser")+` </p>
|
||||
<noscript><meta http-equiv="refresh" content="2; url=`+template.HTMLEscapeString(redirectUrl)+`"></noscript>
|
||||
<noscript><meta http-equiv="refresh" content="2; url=`+template.HTMLEscapeString(redirectURL)+`"></noscript>
|
||||
<script>
|
||||
window.onload = function() {
|
||||
setTimeout(function() {
|
||||
document.getElementById('redirecting-message').style.display = 'none';
|
||||
document.getElementById('close-tab-message').style.display = 'block';
|
||||
window.location='`+template.HTMLEscapeString(template.JSEscapeString(redirectUrl))+`';
|
||||
window.location='`+template.HTMLEscapeString(template.JSEscapeString(redirectURL))+`';
|
||||
}, 2000);
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -89,7 +89,7 @@ type HTMLTemplate struct {
|
||||
Templates *template.Template
|
||||
TemplateName string
|
||||
Props map[string]interface{}
|
||||
Html map[string]template.HTML
|
||||
HTML map[string]template.HTML
|
||||
}
|
||||
|
||||
func NewHTMLTemplate(templates *template.Template, templateName string) *HTMLTemplate {
|
||||
@@ -97,7 +97,7 @@ func NewHTMLTemplate(templates *template.Template, templateName string) *HTMLTem
|
||||
Templates: templates,
|
||||
TemplateName: templateName,
|
||||
Props: make(map[string]interface{}),
|
||||
Html: make(map[string]template.HTML),
|
||||
HTML: make(map[string]template.HTML),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,14 +120,14 @@ func (t *HTMLTemplate) RenderToWriter(w io.Writer) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func TranslateAsHtml(t i18n.TranslateFunc, translationID string, args map[string]interface{}) template.HTML {
|
||||
message := t(translationID, escapeForHtml(args))
|
||||
func TranslateAsHTML(t i18n.TranslateFunc, translationID string, args map[string]interface{}) template.HTML {
|
||||
message := t(translationID, escapeForHTML(args))
|
||||
message = strings.Replace(message, "[[", "<strong>", -1)
|
||||
message = strings.Replace(message, "]]", "</strong>", -1)
|
||||
return template.HTML(message)
|
||||
}
|
||||
|
||||
func escapeForHtml(arg interface{}) interface{} {
|
||||
func escapeForHTML(arg interface{}) interface{} {
|
||||
switch typedArg := arg.(type) {
|
||||
case string:
|
||||
return template.HTMLEscapeString(typedArg)
|
||||
@@ -136,7 +136,7 @@ func escapeForHtml(arg interface{}) interface{} {
|
||||
case map[string]interface{}:
|
||||
safeArg := make(map[string]interface{}, len(typedArg))
|
||||
for key, value := range typedArg {
|
||||
safeArg[key] = escapeForHtml(value)
|
||||
safeArg[key] = escapeForHTML(value)
|
||||
}
|
||||
return safeArg
|
||||
default:
|
||||
|
||||
@@ -103,7 +103,7 @@ func TestHTMLTemplate_RenderError(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestTranslateAsHtml(t *testing.T) {
|
||||
assert.EqualValues(t, "<p><strong><i>foo</i></strong></p>", TranslateAsHtml(i18n.TranslateFunc(htmlTestTranslationBundle.MustTfunc("en")), "foo.bold", map[string]interface{}{
|
||||
assert.EqualValues(t, "<p><strong><i>foo</i></strong></p>", TranslateAsHTML(i18n.TranslateFunc(htmlTestTranslationBundle.MustTfunc("en")), "foo.bold", map[string]interface{}{
|
||||
"Foo": "<i>foo</i>",
|
||||
}))
|
||||
}
|
||||
@@ -141,7 +141,7 @@ func TestEscapeForHtml(t *testing.T) {
|
||||
},
|
||||
} {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert.Equal(t, tc.Expected, escapeForHtml(tc.In))
|
||||
assert.Equal(t, tc.Expected, escapeForHTML(tc.In))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,34 +10,34 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type HumanizedJsonError struct {
|
||||
type HumanizedJSONError struct {
|
||||
Err error
|
||||
Line int
|
||||
Character int
|
||||
}
|
||||
|
||||
func (e *HumanizedJsonError) Error() string {
|
||||
func (e *HumanizedJSONError) Error() string {
|
||||
return e.Err.Error()
|
||||
}
|
||||
|
||||
// HumanizeJsonError extracts error offsets and annotates the error with useful context
|
||||
func HumanizeJsonError(err error, data []byte) error {
|
||||
// HumanizeJSONError extracts error offsets and annotates the error with useful context
|
||||
func HumanizeJSONError(err error, data []byte) error {
|
||||
if syntaxError, ok := err.(*json.SyntaxError); ok {
|
||||
return NewHumanizedJsonError(syntaxError, data, syntaxError.Offset)
|
||||
return NewHumanizedJSONError(syntaxError, data, syntaxError.Offset)
|
||||
} else if unmarshalError, ok := err.(*json.UnmarshalTypeError); ok {
|
||||
return NewHumanizedJsonError(unmarshalError, data, unmarshalError.Offset)
|
||||
return NewHumanizedJSONError(unmarshalError, data, unmarshalError.Offset)
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func NewHumanizedJsonError(err error, data []byte, offset int64) *HumanizedJsonError {
|
||||
func NewHumanizedJSONError(err error, data []byte, offset int64) *HumanizedJSONError {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if offset < 0 || offset > int64(len(data)) {
|
||||
return &HumanizedJsonError{
|
||||
return &HumanizedJSONError{
|
||||
Err: errors.Wrapf(err, "invalid offset %d", offset),
|
||||
}
|
||||
}
|
||||
@@ -48,7 +48,7 @@ func NewHumanizedJsonError(err error, data []byte, offset int64) *HumanizedJsonE
|
||||
lastLineOffset := bytes.LastIndex(data[:offset], lineSep)
|
||||
character := int(offset) - (lastLineOffset + 1) + 1
|
||||
|
||||
return &HumanizedJsonError{
|
||||
return &HumanizedJSONError{
|
||||
Line: line,
|
||||
Character: character,
|
||||
Err: errors.Wrapf(err, "parsing error at line %d, character %d", line, character),
|
||||
|
||||
@@ -63,7 +63,7 @@ func TestHumanizeJsonError(t *testing.T) {
|
||||
for _, testCase := range testCases {
|
||||
testCase := testCase
|
||||
t.Run(testCase.Description, func(t *testing.T) {
|
||||
actual := jsonutils.HumanizeJsonError(testCase.Err, testCase.Data)
|
||||
actual := jsonutils.HumanizeJSONError(testCase.Err, testCase.Data)
|
||||
if testCase.ExpectedErr == "" {
|
||||
assert.NoError(t, actual)
|
||||
} else {
|
||||
@@ -73,7 +73,7 @@ func TestHumanizeJsonError(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewHumanizedJsonError(t *testing.T) {
|
||||
func TestNewHumanizedJSONError(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
testCases := []struct {
|
||||
@@ -81,7 +81,7 @@ func TestNewHumanizedJsonError(t *testing.T) {
|
||||
Data []byte
|
||||
Offset int64
|
||||
Err error
|
||||
Expected *jsonutils.HumanizedJsonError
|
||||
Expected *jsonutils.HumanizedJSONError
|
||||
}{
|
||||
{
|
||||
"nil error",
|
||||
@@ -95,7 +95,7 @@ func TestNewHumanizedJsonError(t *testing.T) {
|
||||
[]byte("line 1\nline 2\nline 3"),
|
||||
-1,
|
||||
errors.New("message"),
|
||||
&jsonutils.HumanizedJsonError{
|
||||
&jsonutils.HumanizedJSONError{
|
||||
Err: errors.Wrap(errors.New("message"), "invalid offset -1"),
|
||||
},
|
||||
},
|
||||
@@ -104,7 +104,7 @@ func TestNewHumanizedJsonError(t *testing.T) {
|
||||
[]byte("line 1\nline 2\nline 3"),
|
||||
0,
|
||||
errors.New("message"),
|
||||
&jsonutils.HumanizedJsonError{
|
||||
&jsonutils.HumanizedJSONError{
|
||||
Err: errors.Wrap(errors.New("message"), "parsing error at line 1, character 1"),
|
||||
Line: 1,
|
||||
Character: 1,
|
||||
@@ -115,7 +115,7 @@ func TestNewHumanizedJsonError(t *testing.T) {
|
||||
[]byte("line 1\nline 2\nline 3"),
|
||||
5,
|
||||
errors.New("message"),
|
||||
&jsonutils.HumanizedJsonError{
|
||||
&jsonutils.HumanizedJSONError{
|
||||
Err: errors.Wrap(errors.New("message"), "parsing error at line 1, character 6"),
|
||||
Line: 1,
|
||||
Character: 6,
|
||||
@@ -126,7 +126,7 @@ func TestNewHumanizedJsonError(t *testing.T) {
|
||||
[]byte("line 1\nline 2\nline 3"),
|
||||
6,
|
||||
errors.New("message"),
|
||||
&jsonutils.HumanizedJsonError{
|
||||
&jsonutils.HumanizedJSONError{
|
||||
Err: errors.Wrap(errors.New("message"), "parsing error at line 1, character 7"),
|
||||
Line: 1,
|
||||
Character: 7,
|
||||
@@ -137,7 +137,7 @@ func TestNewHumanizedJsonError(t *testing.T) {
|
||||
[]byte("line 1\nline 2\nline 3"),
|
||||
7,
|
||||
errors.New("message"),
|
||||
&jsonutils.HumanizedJsonError{
|
||||
&jsonutils.HumanizedJSONError{
|
||||
Err: errors.Wrap(errors.New("message"), "parsing error at line 2, character 1"),
|
||||
Line: 2,
|
||||
Character: 1,
|
||||
@@ -148,7 +148,7 @@ func TestNewHumanizedJsonError(t *testing.T) {
|
||||
[]byte("line 1\nline 2\nline 3"),
|
||||
12,
|
||||
errors.New("message"),
|
||||
&jsonutils.HumanizedJsonError{
|
||||
&jsonutils.HumanizedJSONError{
|
||||
Err: errors.Wrap(errors.New("message"), "parsing error at line 2, character 6"),
|
||||
Line: 2,
|
||||
Character: 6,
|
||||
@@ -159,7 +159,7 @@ func TestNewHumanizedJsonError(t *testing.T) {
|
||||
[]byte("line 1\nline 2\nline 3"),
|
||||
13,
|
||||
errors.New("message"),
|
||||
&jsonutils.HumanizedJsonError{
|
||||
&jsonutils.HumanizedJSONError{
|
||||
Err: errors.Wrap(errors.New("message"), "parsing error at line 2, character 7"),
|
||||
Line: 2,
|
||||
Character: 7,
|
||||
@@ -170,7 +170,7 @@ func TestNewHumanizedJsonError(t *testing.T) {
|
||||
[]byte("line 1\nline 2\nline 3"),
|
||||
17,
|
||||
errors.New("message"),
|
||||
&jsonutils.HumanizedJsonError{
|
||||
&jsonutils.HumanizedJSONError{
|
||||
Err: errors.Wrap(errors.New("message"), "parsing error at line 3, character 4"),
|
||||
Line: 3,
|
||||
Character: 4,
|
||||
@@ -181,7 +181,7 @@ func TestNewHumanizedJsonError(t *testing.T) {
|
||||
[]byte("line 1\nline 2\nline 3"),
|
||||
19,
|
||||
errors.New("message"),
|
||||
&jsonutils.HumanizedJsonError{
|
||||
&jsonutils.HumanizedJSONError{
|
||||
Err: errors.Wrap(errors.New("message"), "parsing error at line 3, character 6"),
|
||||
Line: 3,
|
||||
Character: 6,
|
||||
@@ -192,7 +192,7 @@ func TestNewHumanizedJsonError(t *testing.T) {
|
||||
[]byte("line 1\nline 2\nline 3"),
|
||||
20,
|
||||
errors.New("message"),
|
||||
&jsonutils.HumanizedJsonError{
|
||||
&jsonutils.HumanizedJSONError{
|
||||
Err: errors.Wrap(errors.New("message"), "parsing error at line 3, character 7"),
|
||||
Line: 3,
|
||||
Character: 7,
|
||||
@@ -203,7 +203,7 @@ func TestNewHumanizedJsonError(t *testing.T) {
|
||||
[]byte("line 1\nline 2\nline 3\n"),
|
||||
21,
|
||||
errors.New("message"),
|
||||
&jsonutils.HumanizedJsonError{
|
||||
&jsonutils.HumanizedJSONError{
|
||||
Err: errors.Wrap(errors.New("message"), "parsing error at line 4, character 1"),
|
||||
Line: 4,
|
||||
Character: 1,
|
||||
@@ -214,7 +214,7 @@ func TestNewHumanizedJsonError(t *testing.T) {
|
||||
[]byte("line 1\nline 2\nline 3"),
|
||||
21,
|
||||
errors.New("message"),
|
||||
&jsonutils.HumanizedJsonError{
|
||||
&jsonutils.HumanizedJSONError{
|
||||
Err: errors.Wrap(errors.New("message"), "invalid offset 21"),
|
||||
},
|
||||
},
|
||||
@@ -223,7 +223,7 @@ func TestNewHumanizedJsonError(t *testing.T) {
|
||||
for _, testCase := range testCases {
|
||||
testCase := testCase
|
||||
t.Run(testCase.Description, func(t *testing.T) {
|
||||
actual := jsonutils.NewHumanizedJsonError(testCase.Err, testCase.Data, testCase.Offset)
|
||||
actual := jsonutils.NewHumanizedJSONError(testCase.Err, testCase.Data, testCase.Offset)
|
||||
if testCase.Expected != nil && actual.Err != nil {
|
||||
if assert.EqualValues(t, testCase.Expected.Err.Error(), actual.Err.Error()) {
|
||||
actual.Err = testCase.Expected.Err
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
// Based off of extensions/autolink.c from https://github.com/github/cmark
|
||||
|
||||
var (
|
||||
DefaultUrlSchemes = []string{"http", "https", "ftp", "mailto", "tel"}
|
||||
DefaultURLSchemes = []string{"http", "https", "ftp", "mailto", "tel"}
|
||||
wwwAutoLinkRegex = regexp.MustCompile(`^www\d{0,3}\.`)
|
||||
)
|
||||
|
||||
@@ -111,7 +111,7 @@ func parseURLAutolink(data string, position int) (Range, bool) {
|
||||
|
||||
func isSchemeAllowed(scheme string) bool {
|
||||
// Note that this doesn't support the custom URL schemes implemented by the client
|
||||
for _, allowed := range DefaultUrlSchemes {
|
||||
for _, allowed := range DefaultURLSchemes {
|
||||
if strings.EqualFold(allowed, scheme) {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -62,8 +62,8 @@ func UpdateAssetsSubpathInDir(subpath, directory string) error {
|
||||
return errors.Wrapf(err, "failed to resolve symlinks to %s", staticDir)
|
||||
}
|
||||
|
||||
rootHtmlPath := filepath.Join(staticDir, "root.html")
|
||||
oldRootHtml, err := ioutil.ReadFile(rootHtmlPath)
|
||||
rootHTMLPath := filepath.Join(staticDir, "root.html")
|
||||
oldRootHTML, err := ioutil.ReadFile(rootHTMLPath)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to open root.html")
|
||||
}
|
||||
@@ -73,7 +73,7 @@ func UpdateAssetsSubpathInDir(subpath, directory string) error {
|
||||
// Determine if a previous subpath had already been rewritten into the assets.
|
||||
reWebpackPublicPathScript := regexp.MustCompile("window.publicPath='([^']+/)static/'")
|
||||
alreadyRewritten := false
|
||||
if matches := reWebpackPublicPathScript.FindStringSubmatch(string(oldRootHtml)); matches != nil {
|
||||
if matches := reWebpackPublicPathScript.FindStringSubmatch(string(oldRootHTML)); matches != nil {
|
||||
oldSubpath = matches[1]
|
||||
alreadyRewritten = true
|
||||
}
|
||||
@@ -83,14 +83,14 @@ func UpdateAssetsSubpathInDir(subpath, directory string) error {
|
||||
|
||||
mlog.Debug("Rewriting static assets", mlog.String("from_subpath", oldSubpath), mlog.String("to_subpath", subpath))
|
||||
|
||||
newRootHtml := string(oldRootHtml)
|
||||
newRootHTML := string(oldRootHTML)
|
||||
|
||||
reCSP := regexp.MustCompile(`<meta http-equiv="Content-Security-Policy" content="script-src 'self' cdn.rudderlabs.com/ js.stripe.com/v3([^"]*)">`)
|
||||
if results := reCSP.FindAllString(newRootHtml, -1); len(results) == 0 {
|
||||
if results := reCSP.FindAllString(newRootHTML, -1); len(results) == 0 {
|
||||
return fmt.Errorf("failed to find 'Content-Security-Policy' meta tag to rewrite")
|
||||
}
|
||||
|
||||
newRootHtml = reCSP.ReplaceAllLiteralString(newRootHtml, fmt.Sprintf(
|
||||
newRootHTML = reCSP.ReplaceAllLiteralString(newRootHTML, fmt.Sprintf(
|
||||
`<meta http-equiv="Content-Security-Policy" content="script-src 'self' cdn.rudderlabs.com/ js.stripe.com/v3%s">`,
|
||||
GetSubpathScriptHash(subpath),
|
||||
))
|
||||
@@ -98,22 +98,22 @@ func UpdateAssetsSubpathInDir(subpath, directory string) error {
|
||||
// Rewrite the root.html references to `/static/*` to include the given subpath.
|
||||
// This potentially includes a previously injected inline script that needs to
|
||||
// be updated (and isn't covered by the cases above).
|
||||
newRootHtml = strings.Replace(newRootHtml, pathToReplace, newPath, -1)
|
||||
newRootHTML = strings.Replace(newRootHTML, pathToReplace, newPath, -1)
|
||||
|
||||
if alreadyRewritten && subpath == "/" {
|
||||
// Remove the injected script since no longer required. Note that the rewrite above
|
||||
// will have affected the script, so look for the new subpath, not the old one.
|
||||
oldScript := getSubpathScript(subpath)
|
||||
newRootHtml = strings.Replace(newRootHtml, fmt.Sprintf("</style><script>%s</script>", oldScript), "</style>", 1)
|
||||
newRootHTML = strings.Replace(newRootHTML, fmt.Sprintf("</style><script>%s</script>", oldScript), "</style>", 1)
|
||||
|
||||
} else if !alreadyRewritten && subpath != "/" {
|
||||
// Otherwise, inject the script to define `window.publicPath`.
|
||||
script := getSubpathScript(subpath)
|
||||
newRootHtml = strings.Replace(newRootHtml, "</style>", fmt.Sprintf("</style><script>%s</script>", script), 1)
|
||||
newRootHTML = strings.Replace(newRootHTML, "</style>", fmt.Sprintf("</style><script>%s</script>", script), 1)
|
||||
}
|
||||
|
||||
// Write out the updated root.html.
|
||||
if err = ioutil.WriteFile(rootHtmlPath, []byte(newRootHtml), 0); err != nil {
|
||||
if err = ioutil.WriteFile(rootHTMLPath, []byte(newRootHTML), 0); err != nil {
|
||||
return errors.Wrapf(err, "failed to update root.html with subpath %s", subpath)
|
||||
}
|
||||
|
||||
|
||||
@@ -86,78 +86,78 @@ func TestUpdateAssetsSubpath(t *testing.T) {
|
||||
"no changes required, empty subpath provided",
|
||||
baseRootHtml,
|
||||
baseCss,
|
||||
baseManifestJson,
|
||||
baseManifestJSON,
|
||||
"",
|
||||
nil,
|
||||
baseRootHtml,
|
||||
baseCss,
|
||||
baseManifestJson,
|
||||
baseManifestJSON,
|
||||
},
|
||||
{
|
||||
"no changes required",
|
||||
baseRootHtml,
|
||||
baseCss,
|
||||
baseManifestJson,
|
||||
baseManifestJSON,
|
||||
"/",
|
||||
nil,
|
||||
baseRootHtml,
|
||||
baseCss,
|
||||
baseManifestJson,
|
||||
baseManifestJSON,
|
||||
},
|
||||
{
|
||||
"content security policy not found (missing quotes)",
|
||||
contentSecurityPolicyNotFoundHtml,
|
||||
baseCss,
|
||||
baseManifestJson,
|
||||
baseManifestJSON,
|
||||
"/subpath",
|
||||
fmt.Errorf("failed to find 'Content-Security-Policy' meta tag to rewrite"),
|
||||
contentSecurityPolicyNotFoundHtml,
|
||||
baseCss,
|
||||
baseManifestJson,
|
||||
baseManifestJSON,
|
||||
},
|
||||
{
|
||||
"content security policy not found (missing unsafe-eval)",
|
||||
contentSecurityPolicyNotFound2Html,
|
||||
baseCss,
|
||||
baseManifestJson,
|
||||
baseManifestJSON,
|
||||
"/subpath",
|
||||
fmt.Errorf("failed to find 'Content-Security-Policy' meta tag to rewrite"),
|
||||
contentSecurityPolicyNotFound2Html,
|
||||
baseCss,
|
||||
baseManifestJson,
|
||||
baseManifestJSON,
|
||||
},
|
||||
{
|
||||
"subpath",
|
||||
baseRootHtml,
|
||||
baseCss,
|
||||
baseManifestJson,
|
||||
baseManifestJSON,
|
||||
"/subpath",
|
||||
nil,
|
||||
subpathRootHtml,
|
||||
subpathCss,
|
||||
subpathCSS,
|
||||
subpathManifestJson,
|
||||
},
|
||||
{
|
||||
"new subpath from old",
|
||||
subpathRootHtml,
|
||||
subpathCss,
|
||||
subpathCSS,
|
||||
subpathManifestJson,
|
||||
"/nested/subpath",
|
||||
nil,
|
||||
newSubpathRootHtml,
|
||||
newSubpathCss,
|
||||
newSubpathRootHTML,
|
||||
newSubpathCSS,
|
||||
newSubpathManifestJson,
|
||||
},
|
||||
{
|
||||
"resetting to /",
|
||||
subpathRootHtml,
|
||||
subpathCss,
|
||||
baseManifestJson,
|
||||
subpathCSS,
|
||||
baseManifestJSON,
|
||||
"/",
|
||||
nil,
|
||||
baseRootHtml,
|
||||
baseCss,
|
||||
baseManifestJson,
|
||||
baseManifestJSON,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -278,13 +278,13 @@ const baseCss = `@font-face{font-family:FontAwesome;src:url(/static/files/674f50
|
||||
|
||||
const subpathRootHtml = `<!DOCTYPE html> <html lang=en> <head> <meta charset=utf-8> <meta http-equiv="Content-Security-Policy" content="script-src 'self' cdn.rudderlabs.com/ js.stripe.com/v3 'sha256-tPOjw+tkVs9axL78ZwGtYl975dtyPHB6LYKAO2R3gR4='"> <meta http-equiv=X-UA-Compatible content="IE=edge"> <meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=0"> <meta name=robots content="noindex, nofollow"> <meta name=referrer content=no-referrer> <title>Mattermost</title> <meta name=apple-mobile-web-app-capable content=yes> <meta name=apple-mobile-web-app-status-bar-style content=default> <meta name=mobile-web-app-capable content=yes> <meta name=apple-mobile-web-app-title content=Mattermost> <meta name=application-name content=Mattermost> <meta name=format-detection content="telephone=no"> <link rel=apple-touch-icon sizes=57x57 href=/subpath/static/files/78b7e73b41b8731ce2c41c870ecc8886.png> <link rel=apple-touch-icon sizes=60x60 href=/subpath/static/files/51d00ffd13afb6d74fd8f6dfdeef768a.png> <link rel=apple-touch-icon sizes=72x72 href=/subpath/static/files/23645596f8f78f017bd4d457abb855c4.png> <link rel=apple-touch-icon sizes=76x76 href=/subpath/static/files/26e9d72f472663a00b4b206149459fab.png> <link rel=apple-touch-icon sizes=144x144 href=/subpath/static/files/7bd91659bf3fc8c68fcd45fc1db9c630.png> <link rel=apple-touch-icon sizes=120x120 href=/subpath/static/files/fa69ffe11eb334aaef5aece8d848ca62.png> <link rel=apple-touch-icon sizes=152x152 href=/subpath/static/files/f046777feb6ab12fc43b8f9908b1db35.png> <link rel=icon type=image/png sizes=16x16 href=/subpath/static/files/02b96247d275680adaaabf01c71c571d.png> <link rel=icon type=image/png sizes=32x32 href=/subpath/static/files/1d9020f201a6762421cab8d30624fdd8.png> <link rel=icon type=image/png sizes=96x96 href=/subpath/static/files/fe23af39ae98d77dc26ae8586565970f.png> <link rel=icon type=image/png sizes=192x192 href=/subpath/static/files/d7ff68a7675f84337cc154c3d4abe713.png> <link rel=manifest href=/subpath/static/files/a985ad72552ad069537d6eea81e719c7.json> <link rel=stylesheet class=code_theme> <style>.error-screen{font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding-top:50px;max-width:750px;font-size:14px;color:#333;margin:auto;display:none;line-height:1.5}.error-screen h2{font-size:30px;font-weight:400;line-height:1.2}.error-screen ul{padding-left:15px;line-height:1.7;margin-top:0;margin-bottom:10px}.error-screen hr{color:#ddd;margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.error-screen-visible{display:block}</style><script>window.publicPath='/subpath/static/'</script> <link href="/subpath/static/main.364fd054d7a6d741efc6.css" rel="stylesheet"><script type="text/javascript" src="/subpath/static/main.e49599ac425584ffead5.js"></script></head> <body class=font--open_sans> <div id=root> <div class=error-screen> <h2>Cannot connect to Mattermost</h2> <hr/> <p>We're having trouble connecting to Mattermost. If refreshing this page (Ctrl+R or Command+R) does not work, please verify that your computer is connected to the internet.</p> <br/> </div> <div class=loading-screen style=position:relative> <div class=loading__content> <div class="round round-1"></div> <div class="round round-2"></div> <div class="round round-3"></div> </div> </div> </div> <noscript> To use Mattermost, please enable JavaScript. </noscript> </body> </html>`
|
||||
|
||||
const subpathCss = `@font-face{font-family:FontAwesome;src:url(/subpath/static/files/674f50d287a8c48dc19ba404d20fe713.eot);src:url(/subpath/static/files/674f50d287a8c48dc19ba404d20fe713.eot?#iefix&v=4.7.0) format("embedded-opentype"),url(/subpath/static/files/af7ae505a9eed503f8b8e6982036873e.woff2) format("woff2"),url(/subpath/static/files/fee66e712a8a08eef5805a46892932ad.woff) format("woff"),url(/subpath/static/files/b06871f281fee6b241d60582ae9369b9.ttf) format("truetype"),url(/subpath/static/files/677433a0892aaed7b7d2628c313c9775.svg#fontawesomeregular) format("svg");font-weight:400;font-style:normal}`
|
||||
const subpathCSS = `@font-face{font-family:FontAwesome;src:url(/subpath/static/files/674f50d287a8c48dc19ba404d20fe713.eot);src:url(/subpath/static/files/674f50d287a8c48dc19ba404d20fe713.eot?#iefix&v=4.7.0) format("embedded-opentype"),url(/subpath/static/files/af7ae505a9eed503f8b8e6982036873e.woff2) format("woff2"),url(/subpath/static/files/fee66e712a8a08eef5805a46892932ad.woff) format("woff"),url(/subpath/static/files/b06871f281fee6b241d60582ae9369b9.ttf) format("truetype"),url(/subpath/static/files/677433a0892aaed7b7d2628c313c9775.svg#fontawesomeregular) format("svg");font-weight:400;font-style:normal}`
|
||||
|
||||
const newSubpathRootHtml = `<!DOCTYPE html> <html lang=en> <head> <meta charset=utf-8> <meta http-equiv="Content-Security-Policy" content="script-src 'self' cdn.rudderlabs.com/ js.stripe.com/v3 'sha256-mbRaPRRpWz6MNkX9SyXWMJ8XnWV4w/DoqK2M0ryUAvc='"> <meta http-equiv=X-UA-Compatible content="IE=edge"> <meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=0"> <meta name=robots content="noindex, nofollow"> <meta name=referrer content=no-referrer> <title>Mattermost</title> <meta name=apple-mobile-web-app-capable content=yes> <meta name=apple-mobile-web-app-status-bar-style content=default> <meta name=mobile-web-app-capable content=yes> <meta name=apple-mobile-web-app-title content=Mattermost> <meta name=application-name content=Mattermost> <meta name=format-detection content="telephone=no"> <link rel=apple-touch-icon sizes=57x57 href=/nested/subpath/static/files/78b7e73b41b8731ce2c41c870ecc8886.png> <link rel=apple-touch-icon sizes=60x60 href=/nested/subpath/static/files/51d00ffd13afb6d74fd8f6dfdeef768a.png> <link rel=apple-touch-icon sizes=72x72 href=/nested/subpath/static/files/23645596f8f78f017bd4d457abb855c4.png> <link rel=apple-touch-icon sizes=76x76 href=/nested/subpath/static/files/26e9d72f472663a00b4b206149459fab.png> <link rel=apple-touch-icon sizes=144x144 href=/nested/subpath/static/files/7bd91659bf3fc8c68fcd45fc1db9c630.png> <link rel=apple-touch-icon sizes=120x120 href=/nested/subpath/static/files/fa69ffe11eb334aaef5aece8d848ca62.png> <link rel=apple-touch-icon sizes=152x152 href=/nested/subpath/static/files/f046777feb6ab12fc43b8f9908b1db35.png> <link rel=icon type=image/png sizes=16x16 href=/nested/subpath/static/files/02b96247d275680adaaabf01c71c571d.png> <link rel=icon type=image/png sizes=32x32 href=/nested/subpath/static/files/1d9020f201a6762421cab8d30624fdd8.png> <link rel=icon type=image/png sizes=96x96 href=/nested/subpath/static/files/fe23af39ae98d77dc26ae8586565970f.png> <link rel=icon type=image/png sizes=192x192 href=/nested/subpath/static/files/d7ff68a7675f84337cc154c3d4abe713.png> <link rel=manifest href=/nested/subpath/static/files/a985ad72552ad069537d6eea81e719c7.json> <link rel=stylesheet class=code_theme> <style>.error-screen{font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding-top:50px;max-width:750px;font-size:14px;color:#333;margin:auto;display:none;line-height:1.5}.error-screen h2{font-size:30px;font-weight:400;line-height:1.2}.error-screen ul{padding-left:15px;line-height:1.7;margin-top:0;margin-bottom:10px}.error-screen hr{color:#ddd;margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.error-screen-visible{display:block}</style><script>window.publicPath='/nested/subpath/static/'</script> <link href="/nested/subpath/static/main.364fd054d7a6d741efc6.css" rel="stylesheet"><script type="text/javascript" src="/nested/subpath/static/main.e49599ac425584ffead5.js"></script></head> <body class=font--open_sans> <div id=root> <div class=error-screen> <h2>Cannot connect to Mattermost</h2> <hr/> <p>We're having trouble connecting to Mattermost. If refreshing this page (Ctrl+R or Command+R) does not work, please verify that your computer is connected to the internet.</p> <br/> </div> <div class=loading-screen style=position:relative> <div class=loading__content> <div class="round round-1"></div> <div class="round round-2"></div> <div class="round round-3"></div> </div> </div> </div> <noscript> To use Mattermost, please enable JavaScript. </noscript> </body> </html>`
|
||||
const newSubpathRootHTML = `<!DOCTYPE html> <html lang=en> <head> <meta charset=utf-8> <meta http-equiv="Content-Security-Policy" content="script-src 'self' cdn.rudderlabs.com/ js.stripe.com/v3 'sha256-mbRaPRRpWz6MNkX9SyXWMJ8XnWV4w/DoqK2M0ryUAvc='"> <meta http-equiv=X-UA-Compatible content="IE=edge"> <meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=0"> <meta name=robots content="noindex, nofollow"> <meta name=referrer content=no-referrer> <title>Mattermost</title> <meta name=apple-mobile-web-app-capable content=yes> <meta name=apple-mobile-web-app-status-bar-style content=default> <meta name=mobile-web-app-capable content=yes> <meta name=apple-mobile-web-app-title content=Mattermost> <meta name=application-name content=Mattermost> <meta name=format-detection content="telephone=no"> <link rel=apple-touch-icon sizes=57x57 href=/nested/subpath/static/files/78b7e73b41b8731ce2c41c870ecc8886.png> <link rel=apple-touch-icon sizes=60x60 href=/nested/subpath/static/files/51d00ffd13afb6d74fd8f6dfdeef768a.png> <link rel=apple-touch-icon sizes=72x72 href=/nested/subpath/static/files/23645596f8f78f017bd4d457abb855c4.png> <link rel=apple-touch-icon sizes=76x76 href=/nested/subpath/static/files/26e9d72f472663a00b4b206149459fab.png> <link rel=apple-touch-icon sizes=144x144 href=/nested/subpath/static/files/7bd91659bf3fc8c68fcd45fc1db9c630.png> <link rel=apple-touch-icon sizes=120x120 href=/nested/subpath/static/files/fa69ffe11eb334aaef5aece8d848ca62.png> <link rel=apple-touch-icon sizes=152x152 href=/nested/subpath/static/files/f046777feb6ab12fc43b8f9908b1db35.png> <link rel=icon type=image/png sizes=16x16 href=/nested/subpath/static/files/02b96247d275680adaaabf01c71c571d.png> <link rel=icon type=image/png sizes=32x32 href=/nested/subpath/static/files/1d9020f201a6762421cab8d30624fdd8.png> <link rel=icon type=image/png sizes=96x96 href=/nested/subpath/static/files/fe23af39ae98d77dc26ae8586565970f.png> <link rel=icon type=image/png sizes=192x192 href=/nested/subpath/static/files/d7ff68a7675f84337cc154c3d4abe713.png> <link rel=manifest href=/nested/subpath/static/files/a985ad72552ad069537d6eea81e719c7.json> <link rel=stylesheet class=code_theme> <style>.error-screen{font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding-top:50px;max-width:750px;font-size:14px;color:#333;margin:auto;display:none;line-height:1.5}.error-screen h2{font-size:30px;font-weight:400;line-height:1.2}.error-screen ul{padding-left:15px;line-height:1.7;margin-top:0;margin-bottom:10px}.error-screen hr{color:#ddd;margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.error-screen-visible{display:block}</style><script>window.publicPath='/nested/subpath/static/'</script> <link href="/nested/subpath/static/main.364fd054d7a6d741efc6.css" rel="stylesheet"><script type="text/javascript" src="/nested/subpath/static/main.e49599ac425584ffead5.js"></script></head> <body class=font--open_sans> <div id=root> <div class=error-screen> <h2>Cannot connect to Mattermost</h2> <hr/> <p>We're having trouble connecting to Mattermost. If refreshing this page (Ctrl+R or Command+R) does not work, please verify that your computer is connected to the internet.</p> <br/> </div> <div class=loading-screen style=position:relative> <div class=loading__content> <div class="round round-1"></div> <div class="round round-2"></div> <div class="round round-3"></div> </div> </div> </div> <noscript> To use Mattermost, please enable JavaScript. </noscript> </body> </html>`
|
||||
|
||||
const newSubpathCss = `@font-face{font-family:FontAwesome;src:url(/nested/subpath/static/files/674f50d287a8c48dc19ba404d20fe713.eot);src:url(/nested/subpath/static/files/674f50d287a8c48dc19ba404d20fe713.eot?#iefix&v=4.7.0) format("embedded-opentype"),url(/nested/subpath/static/files/af7ae505a9eed503f8b8e6982036873e.woff2) format("woff2"),url(/nested/subpath/static/files/fee66e712a8a08eef5805a46892932ad.woff) format("woff"),url(/nested/subpath/static/files/b06871f281fee6b241d60582ae9369b9.ttf) format("truetype"),url(/nested/subpath/static/files/677433a0892aaed7b7d2628c313c9775.svg#fontawesomeregular) format("svg");font-weight:400;font-style:normal}`
|
||||
const newSubpathCSS = `@font-face{font-family:FontAwesome;src:url(/nested/subpath/static/files/674f50d287a8c48dc19ba404d20fe713.eot);src:url(/nested/subpath/static/files/674f50d287a8c48dc19ba404d20fe713.eot?#iefix&v=4.7.0) format("embedded-opentype"),url(/nested/subpath/static/files/af7ae505a9eed503f8b8e6982036873e.woff2) format("woff2"),url(/nested/subpath/static/files/fee66e712a8a08eef5805a46892932ad.woff) format("woff"),url(/nested/subpath/static/files/b06871f281fee6b241d60582ae9369b9.ttf) format("truetype"),url(/nested/subpath/static/files/677433a0892aaed7b7d2628c313c9775.svg#fontawesomeregular) format("svg");font-weight:400;font-style:normal}`
|
||||
|
||||
const baseManifestJson = `{
|
||||
const baseManifestJSON = `{
|
||||
"icons": [
|
||||
{
|
||||
"src": "/static/icon_96x96.png",
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
func UrlEncode(str string) string {
|
||||
func URLEncode(str string) string {
|
||||
strs := strings.Split(str, " ")
|
||||
|
||||
for i, s := range strs {
|
||||
|
||||
@@ -12,17 +12,17 @@ import (
|
||||
func TestUrlEncode(t *testing.T) {
|
||||
|
||||
toEncode := "testing 1 2 3"
|
||||
encoded := UrlEncode(toEncode)
|
||||
encoded := URLEncode(toEncode)
|
||||
|
||||
require.Equal(t, encoded, "testing%201%202%203")
|
||||
|
||||
toEncode = "testing123"
|
||||
encoded = UrlEncode(toEncode)
|
||||
encoded = URLEncode(toEncode)
|
||||
|
||||
require.Equal(t, encoded, "testing123")
|
||||
|
||||
toEncode = "testing$#~123"
|
||||
encoded = UrlEncode(toEncode)
|
||||
encoded = URLEncode(toEncode)
|
||||
|
||||
require.Equal(t, encoded, "testing%24%23~123")
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ func StringSliceDiff(a, b []string) []string {
|
||||
return result
|
||||
}
|
||||
|
||||
func GetIpAddress(r *http.Request, trustedProxyIPHeader []string) string {
|
||||
func GetIPAddress(r *http.Request, trustedProxyIPHeader []string) string {
|
||||
address := ""
|
||||
|
||||
for _, proxyHeader := range trustedProxyIPHeader {
|
||||
@@ -135,7 +135,7 @@ type RequestCache struct {
|
||||
|
||||
// Fetch JSON data from the notices server
|
||||
// if skip is passed, does a fetch without touching the cache
|
||||
func GetUrlWithCache(url string, cache *RequestCache, skip bool) ([]byte, error) {
|
||||
func GetURLWithCache(url string, cache *RequestCache, skip bool) ([]byte, error) {
|
||||
// Build a GET Request, including optional If-None-Match header.
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
@@ -198,8 +198,8 @@ func IsValidWebAuthRedirectURL(config *model.Config, redirectURL string) bool {
|
||||
u, err := url.Parse(redirectURL)
|
||||
if err == nil && (u.Scheme == "http" || u.Scheme == "https") {
|
||||
if config.ServiceSettings.SiteURL != nil {
|
||||
siteUrl := *config.ServiceSettings.SiteURL
|
||||
return strings.Index(strings.ToLower(redirectURL), strings.ToLower(siteUrl)) == 0
|
||||
siteURL := *config.ServiceSettings.SiteURL
|
||||
return strings.Index(strings.ToLower(redirectURL), strings.ToLower(siteURL)) == 0
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ func TestStringSliceDiff(t *testing.T) {
|
||||
assert.Equal(t, expected, StringSliceDiff(a, b))
|
||||
}
|
||||
|
||||
func TestGetIpAddress(t *testing.T) {
|
||||
func TestGetIPAddress(t *testing.T) {
|
||||
// Test with a single IP in the X-Forwarded-For
|
||||
httpRequest1 := http.Request{
|
||||
Header: http.Header{
|
||||
@@ -59,7 +59,7 @@ func TestGetIpAddress(t *testing.T) {
|
||||
RemoteAddr: "10.2.0.1:12345",
|
||||
}
|
||||
|
||||
assert.Equal(t, "10.0.0.1", GetIpAddress(&httpRequest1, []string{"X-Forwarded-For"}))
|
||||
assert.Equal(t, "10.0.0.1", GetIPAddress(&httpRequest1, []string{"X-Forwarded-For"}))
|
||||
|
||||
// Test with multiple IPs in the X-Forwarded-For
|
||||
httpRequest2 := http.Request{
|
||||
@@ -70,7 +70,7 @@ func TestGetIpAddress(t *testing.T) {
|
||||
RemoteAddr: "10.2.0.1:12345",
|
||||
}
|
||||
|
||||
assert.Equal(t, "10.0.0.1", GetIpAddress(&httpRequest2, []string{"X-Forwarded-For"}))
|
||||
assert.Equal(t, "10.0.0.1", GetIPAddress(&httpRequest2, []string{"X-Forwarded-For"}))
|
||||
|
||||
// Test with an empty X-Forwarded-For
|
||||
httpRequest3 := http.Request{
|
||||
@@ -81,7 +81,7 @@ func TestGetIpAddress(t *testing.T) {
|
||||
RemoteAddr: "10.2.0.1:12345",
|
||||
}
|
||||
|
||||
assert.Equal(t, "10.1.0.1", GetIpAddress(&httpRequest3, []string{"X-Forwarded-For", "X-Real-Ip"}))
|
||||
assert.Equal(t, "10.1.0.1", GetIPAddress(&httpRequest3, []string{"X-Forwarded-For", "X-Real-Ip"}))
|
||||
|
||||
// Test without an X-Fowarded-For
|
||||
httpRequest4 := http.Request{
|
||||
@@ -91,14 +91,14 @@ func TestGetIpAddress(t *testing.T) {
|
||||
RemoteAddr: "10.2.0.1:12345",
|
||||
}
|
||||
|
||||
assert.Equal(t, "10.1.0.1", GetIpAddress(&httpRequest4, []string{"X-Forwarded-For", "X-Real-Ip"}))
|
||||
assert.Equal(t, "10.1.0.1", GetIPAddress(&httpRequest4, []string{"X-Forwarded-For", "X-Real-Ip"}))
|
||||
|
||||
// Test without any headers
|
||||
httpRequest5 := http.Request{
|
||||
RemoteAddr: "10.2.0.1:12345",
|
||||
}
|
||||
|
||||
assert.Equal(t, "10.2.0.1", GetIpAddress(&httpRequest5, []string{"X-Forwarded-For", "X-Real-Ip"}))
|
||||
assert.Equal(t, "10.2.0.1", GetIPAddress(&httpRequest5, []string{"X-Forwarded-For", "X-Real-Ip"}))
|
||||
|
||||
// Test with both headers, but both untrusted
|
||||
httpRequest6 := http.Request{
|
||||
@@ -109,7 +109,7 @@ func TestGetIpAddress(t *testing.T) {
|
||||
RemoteAddr: "10.2.0.1:12345",
|
||||
}
|
||||
|
||||
assert.Equal(t, "10.2.0.1", GetIpAddress(&httpRequest6, nil))
|
||||
assert.Equal(t, "10.2.0.1", GetIPAddress(&httpRequest6, nil))
|
||||
|
||||
// Test with both headers, but only X-Real-Ip trusted
|
||||
httpRequest7 := http.Request{
|
||||
@@ -120,7 +120,7 @@ func TestGetIpAddress(t *testing.T) {
|
||||
RemoteAddr: "10.2.0.1:12345",
|
||||
}
|
||||
|
||||
assert.Equal(t, "10.1.0.1", GetIpAddress(&httpRequest7, []string{"X-Real-Ip"}))
|
||||
assert.Equal(t, "10.1.0.1", GetIPAddress(&httpRequest7, []string{"X-Real-Ip"}))
|
||||
|
||||
// Test with X-Forwarded-For, comma separated, untrusted
|
||||
httpRequest8 := http.Request{
|
||||
@@ -130,7 +130,7 @@ func TestGetIpAddress(t *testing.T) {
|
||||
RemoteAddr: "10.2.0.1:12345",
|
||||
}
|
||||
|
||||
assert.Equal(t, "10.2.0.1", GetIpAddress(&httpRequest8, nil))
|
||||
assert.Equal(t, "10.2.0.1", GetIPAddress(&httpRequest8, nil))
|
||||
|
||||
// Test with X-Forwarded-For, comma separated, untrusted
|
||||
httpRequest9 := http.Request{
|
||||
@@ -140,7 +140,7 @@ func TestGetIpAddress(t *testing.T) {
|
||||
RemoteAddr: "10.2.0.1:12345",
|
||||
}
|
||||
|
||||
assert.Equal(t, "10.3.0.1", GetIpAddress(&httpRequest9, []string{"X-Forwarded-For"}))
|
||||
assert.Equal(t, "10.3.0.1", GetIPAddress(&httpRequest9, []string{"X-Forwarded-For"}))
|
||||
|
||||
// Test with both headers, both allowed, first one in trusted used
|
||||
httpRequest10 := http.Request{
|
||||
@@ -151,7 +151,7 @@ func TestGetIpAddress(t *testing.T) {
|
||||
RemoteAddr: "10.2.0.1:12345",
|
||||
}
|
||||
|
||||
assert.Equal(t, "10.1.0.1", GetIpAddress(&httpRequest10, []string{"X-Real-Ip", "X-Forwarded-For"}))
|
||||
assert.Equal(t, "10.1.0.1", GetIPAddress(&httpRequest10, []string{"X-Real-Ip", "X-Forwarded-For"}))
|
||||
}
|
||||
|
||||
func TestRemoveStringFromSlice(t *testing.T) {
|
||||
|
||||
Ссылка в новой задаче
Block a user