diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index e26ce52..ce99ba5 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -16,7 +16,8 @@ jobs: with: go-version: ${{ matrix.go-version }} - uses: actions/checkout@v4 - - uses: actions/cache@v3 + - name: Run Go tests + uses: actions/cache@v3 with: path: | ~/.cache/go-build @@ -24,13 +25,20 @@ jobs: key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} restore-keys: | ${{ runner.os }}-go- - - run: go test ./internal/storage ./server ./internal/tools -v - - run: go test ./internal/storage -bench=. + - run: go test ./internal/storage ./server ./internal/tools ./internal/tools/html2text -v + - run: go test ./internal/storage ./internal/tools/html2text -bench=. # build the assets - - uses: actions/setup-node@v3 + - name: Build web UI + uses: actions/setup-node@v3 with: node-version: 18 cache: 'npm' - run: npm install - run: npm run package + + # validate the swagger file + - name: Validate OpenAPI definition + uses: char0n/swagger-editor-validate@v1 + with: + definition-file: server/ui/api/v1/swagger.json diff --git a/CHANGELOG.md b/CHANGELOG.md index f572a24..d713514 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,24 @@ Notable changes to Mailpit will be documented in this file. +## [v1.9.8] + +### Chore +- Replace satori/go.uuid with github.com/google/uuid ([#190](https://github.com/axllent/mailpit/issues/190)) +- Replace html2text modules with simplified internal function + +### Libs +- Update node modules +- Update Go modules + +### Swagger +- Update swagger documentation + +### Tests +- Add test to validate swagger.json +- Add html2text tests + + ## [v1.9.7] ### Fix diff --git a/go.mod b/go.mod index 7276b1a..a678cb7 100644 --- a/go.mod +++ b/go.mod @@ -8,16 +8,14 @@ require ( github.com/axllent/semver v0.0.1 github.com/disintegration/imaging v1.6.2 github.com/gomarkdown/markdown v0.0.0-20230922112808-5421fefb8386 + github.com/google/uuid v1.3.1 github.com/gorilla/mux v1.8.0 github.com/gorilla/websocket v1.5.0 github.com/jhillyerd/enmime v1.0.1 - github.com/k3a/html2text v1.2.1 - github.com/klauspost/compress v1.17.0 + github.com/klauspost/compress v1.17.1 github.com/leporo/sqlf v1.4.0 github.com/mhale/smtpd v0.8.0 - github.com/microcosm-cc/bluemonday v1.0.25 github.com/reiver/go-telnet v0.0.0-20180421082511-9ff0b2ab096e - github.com/satori/go.uuid v1.2.0 github.com/sirupsen/logrus v1.9.3 github.com/spf13/cobra v1.7.0 github.com/spf13/pflag v1.0.5 @@ -33,12 +31,10 @@ require ( github.com/DATA-DOG/go-sqlmock v1.5.0 // indirect github.com/GehirnInc/crypt v0.0.0-20230320061759-8cc1b52080c5 // indirect github.com/andybalholm/cascadia v1.3.2 // indirect - github.com/aymerick/douceur v0.2.0 // indirect github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a // indirect github.com/cznic/ql v1.2.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f // indirect - github.com/google/uuid v1.3.1 // indirect github.com/gorilla/css v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056 // indirect diff --git a/go.sum b/go.sum index 87fc6de..29659da 100644 --- a/go.sum +++ b/go.sum @@ -13,8 +13,6 @@ github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsVi github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU= github.com/axllent/semver v0.0.1 h1:QqF+KSGxgj8QZzSXAvKFqjGWE5792ksOnQhludToK8E= github.com/axllent/semver v0.0.1/go.mod h1:2xSPzvG8n9mRfdtxSvWvfTfQGWfHsMsHO1iZnKATMSc= -github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= -github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a h1:MISbI8sU/PSK/ztvmWKFcI7UGb5/HQT7B+i3a2myKgI= github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a/go.mod h1:2GxOXOlEPAMFPfp014mK1SWq8G8BN8o7/dfYqJrVGn8= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= @@ -59,8 +57,6 @@ github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ= github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= @@ -73,14 +69,10 @@ github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056 h1:iCHtR9CQykt github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk= github.com/jhillyerd/enmime v1.0.1 h1:y6RyqIgBOI2hIinOXIzmeB+ITRVls0zTJIm5GwgXnjE= github.com/jhillyerd/enmime v1.0.1/go.mod h1:LMMbm6oTlzWHghPavqHtOrP/NosVv3l42CUrZjn03/Q= -github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= -github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/k3a/html2text v1.2.1 h1:nvnKgBvBR/myqrwfLuiqecUtaK1lB9hGziIJKatNFVY= -github.com/k3a/html2text v1.2.1/go.mod h1:ieEXykM67iT8lTvEWBh6fhpH4B23kB9OMKPdIBmgUqA= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= -github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM= -github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.17.1 h1:NE3C767s2ak2bweCZo3+rdP4U/HoyVXLv/X9f2gPS5g= +github.com/klauspost/compress v1.17.1/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= @@ -99,8 +91,6 @@ github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwp github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/mhale/smtpd v0.8.0 h1:5JvdsehCg33PQrZBvFyDMMUDQmvbzVpZgKob7eYBJc0= github.com/mhale/smtpd v0.8.0/go.mod h1:MQl+y2hwIEQCXtNhe5+55n0GZOjSmeqORDIXbqUL3x4= -github.com/microcosm-cc/bluemonday v1.0.25 h1:4NEwSfiJ+Wva0VxN5B8OwMicaJvD8r9tlJWm9rtloEg= -github.com/microcosm-cc/bluemonday v1.0.25/go.mod h1:ZIOjCQp1OrzBBPIJmfX4qDYFuhU02nx4bn030ixfHLE= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -119,14 +109,8 @@ github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUc github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= -github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= -github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= @@ -166,7 +150,6 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -210,7 +193,6 @@ golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= diff --git a/internal/storage/database.go b/internal/storage/database.go index c6d55ed..9e8e0f7 100644 --- a/internal/storage/database.go +++ b/internal/storage/database.go @@ -22,10 +22,10 @@ import ( "github.com/axllent/mailpit/internal/logger" "github.com/axllent/mailpit/internal/tools" "github.com/axllent/mailpit/server/websockets" + "github.com/google/uuid" "github.com/jhillyerd/enmime" "github.com/klauspost/compress/zstd" "github.com/leporo/sqlf" - uuid "github.com/satori/go.uuid" // sqlite (native) - https://gitlab.com/cznic/sqlite _ "modernc.org/sqlite" @@ -161,7 +161,7 @@ func Store(body []byte) (string, error) { searchText := createSearchText(env) // generate unique ID - id := uuid.NewV4().String() + id := uuid.New().String() summaryJSON, err := json.Marshal(obj) if err != nil { diff --git a/internal/storage/utils.go b/internal/storage/utils.go index 58f91b0..877b993 100644 --- a/internal/storage/utils.go +++ b/internal/storage/utils.go @@ -11,9 +11,9 @@ import ( "github.com/axllent/mailpit/config" "github.com/axllent/mailpit/internal/logger" + "github.com/axllent/mailpit/internal/tools/html2text" "github.com/axllent/mailpit/server/websockets" "github.com/jhillyerd/enmime" - "github.com/k3a/html2text" "github.com/leporo/sqlf" ) @@ -39,12 +39,8 @@ func createSearchText(env *enmime.Envelope) string { b.WriteString(env.GetHeader("Bcc") + " ") b.WriteString(env.GetHeader("Reply-To") + " ") b.WriteString(env.GetHeader("Return-Path") + " ") - h := strings.TrimSpace( - html2text.HTML2TextWithOptions( - env.HTML, - html2text.WithLinksInnerText(), - ), - ) + + h := html2text.Strip(env.HTML, true) if h != "" { b.WriteString(h + " ") } else { diff --git a/internal/tools/html.go b/internal/tools/html.go index 06de636..6e5e883 100644 --- a/internal/tools/html.go +++ b/internal/tools/html.go @@ -2,9 +2,7 @@ package tools import ( "fmt" - "strings" - "github.com/microcosm-cc/bluemonday" "golang.org/x/net/html" ) @@ -19,12 +17,3 @@ func GetHTMLAttributeVal(e *html.Node, key string) (string, error) { return "", fmt.Errorf("%s not found", key) } - -// StripHTML returns text from an HTML string -func stripHTML(h string) string { - p := bluemonday.StrictPolicy() - // // ensure joining html elements are spaced apart, eg table cells etc - h = strings.ReplaceAll(h, "><", "> <") - // return p.Sanitize(h) - return html.UnescapeString(p.Sanitize(h)) -} diff --git a/internal/tools/html2text/html2text.go b/internal/tools/html2text/html2text.go new file mode 100644 index 0000000..9f3f6f6 --- /dev/null +++ b/internal/tools/html2text/html2text.go @@ -0,0 +1,82 @@ +// Package html2text is a simple library to convert HTML to plain text +package html2text + +import ( + "bytes" + "log" + "regexp" + "strings" + "unicode" + + "golang.org/x/net/html" +) + +var ( + re = regexp.MustCompile(`\s+`) + spaceRe = regexp.MustCompile(`(?mi)<\/(div|p|td|th|h[1-6]|ul|ol|li|address|article|aside|blockquote|dl|dt|footer|header|hr|main|nav|pre|table|thead|tfoot|video)><`) + brRe = regexp.MustCompile(`(?mi)<(br /|br)>`) + imgRe = regexp.MustCompile(`(?mi)<(img)`) + skip = make(map[string]bool) +) + +func init() { + skip["script"] = true + skip["title"] = true + skip["head"] = true + skip["link"] = true + skip["meta"] = true + skip["style"] = true + skip["noscript"] = true +} + +// Strip will convert a HTML string to plain text +func Strip(h string, includeLinks bool) string { + h = spaceRe.ReplaceAllString(h, " <") + h = brRe.ReplaceAllString(h, " ") + h = imgRe.ReplaceAllString(h, " <$1") + var buffer bytes.Buffer + doc, err := html.Parse(strings.NewReader(h)) + if err != nil { + log.Fatal(err) + } + + extract(doc, &buffer, includeLinks) + return clean(buffer.String()) +} + +func extract(node *html.Node, buff *bytes.Buffer, includeLinks bool) { + if node.Type == html.TextNode { + data := node.Data + if data != "" { + buff.WriteString(data) + } + } + for c := node.FirstChild; c != nil; c = c.NextSibling { + if _, skip := skip[c.Data]; !skip { + if includeLinks && c.Data == "a" { + for _, a := range c.Attr { + if a.Key == "href" && strings.HasPrefix(strings.ToLower(a.Val), "http") { + buff.WriteString(" " + a.Val + " ") + } + } + } + extract(c, buff, includeLinks) + } + } +} + +func clean(text string) string { + // replace \uFEFF with space, see https://github.com/golang/go/issues/42274#issuecomment-1017258184 + text = strings.ReplaceAll(text, string('\uFEFF'), " ") + + // remove non-printable characters + text = strings.Map(func(r rune) rune { + if unicode.IsPrint(r) { + return r + } + return []rune(" ")[0] + }, text) + + text = re.ReplaceAllString(text, " ") + return strings.TrimSpace(text) +} diff --git a/internal/tools/html2text/html2text_test.go b/internal/tools/html2text/html2text_test.go new file mode 100644 index 0000000..96ca9a1 --- /dev/null +++ b/internal/tools/html2text/html2text_test.go @@ -0,0 +1,250 @@ +package html2text + +import "testing" + +func TestPlain(t *testing.T) { + tests := map[string]string{} + tests["this is a test"] = "this is a test" + tests["thiS IS a Test"] = "thiS IS a Test" + tests["thiS IS a Test :-)"] = "thiS IS a Test :-)" + tests["

This is a test.

"] = "This is a test." + tests["

Paragraph 1

Paragraph 2

"] = "Paragraph 1 Paragraph 2" + tests["

Heading

Paragraph

"] = "Heading Paragraph" + tests["Alphabet chars"] = "Alphabet chars" + tests["Alphabet chars."] = "Alphabet chars." + tests["
FirstSecond
"] = "First Second" + tests[`

Heading

+

Paragraph

`] = "Heading Paragraph" + tests[`

Heading

linked text

`] = "Heading linked text" + // broken html + tests[`

Heading

linked text.`] = "Heading linked text." + + for str, expected := range tests { + res := Strip(str, false) + if res != expected { + t.Log("error:", res, "!=", expected) + t.Fail() + } + } +} + +func TestWithLinks(t *testing.T) { + tests := map[string]string{} + tests["this is a test"] = "this is a test" + tests["thiS IS a Test"] = "thiS IS a Test" + tests["thiS IS a Test :-)"] = "thiS IS a Test :-)" + tests["

This is a test.

"] = "This is a test." + tests["

Paragraph 1

Paragraph 2

"] = "Paragraph 1 Paragraph 2" + tests["

Heading

Paragraph

"] = "Heading Paragraph" + tests["Alphabet chars"] = "Alphabet chars" + tests["Alphabet chars."] = "Alphabet chars." + tests["
FirstSecond
"] = "First Second" + tests["

Heading

Paragraph

"] = "Heading Paragraph" + tests[`

Heading

+

Paragraph

`] = "Heading Paragraph" + tests[`

Heading

linked text

`] = "Heading https://github.com linked text" + // broken html + tests[`

Heading

linked text.`] = "Heading https://github.com linked text." + + for str, expected := range tests { + res := Strip(str, true) + if res != expected { + t.Log("error:", res, "!=", expected) + t.Fail() + } + } +} + +func BenchmarkPlain(b *testing.B) { + for i := 0; i < b.N; i++ { + Strip(htmlTestData, false) + } +} + +func BenchmarkLinks(b *testing.B) { + for i := 0; i < b.N; i++ { + Strip(htmlTestData, true) + } +} + +var htmlTestData = ` + + + + + [axllent/mailpit] Run failed: .github/workflows/tests.yml - feature/swagger (284335a) + + + + + + + +
+
+ + + + +
+ + + + + + +
 
+ + + + + +
+ GitHub +

+ [axllent/mailpit] .github/workflows/tests.yml workflow run + +

+
+ + + + + + +
 
+ +
+ + + + +
+ + + + +
+ + + + +
+ + + + + +
+ + + + +
+ + + + + + + + +
 
+ +

.github/workflows/tests.yml: No jobs were run

+ + + + + + +
 
+ + + + + + + +
+ + + + +
+ + + + +
+ + View workflow run + +
+
+ +
+ + + + + + +
 
+ + +
+
+ + + + +
+
+
+ + + + +
+ + + + + + +
 
+ + + + + + + +
 
+ +


You are receiving this because you are subscribed to this thread.
Manage your GitHub Actions notifications

+ +
+ + + + +
+ + + + + + +
 
+ +

GitHub, Inc. ・88 Colin P Kelly Jr Street ・San Francisco, CA 94107

+
+ +
+
+ +

                                                           
+ +` diff --git a/internal/tools/snippets.go b/internal/tools/snippets.go index 5b8510f..1322caf 100644 --- a/internal/tools/snippets.go +++ b/internal/tools/snippets.go @@ -3,6 +3,8 @@ package tools import ( "regexp" "strings" + + "github.com/axllent/mailpit/internal/tools/html2text" ) // CreateSnippet returns a message snippet. It will use the HTML version (if it exists) @@ -12,17 +14,13 @@ func CreateSnippet(text, html string) string { html = strings.TrimSpace(html) limit := 200 spaceRe := regexp.MustCompile(`\s+`) - nlRe := regexp.MustCompile(`\r?\n`) if text == "" && html == "" { return "" } if html != "" { - data := nlRe.ReplaceAllString(stripHTML(html), " ") - // replace \uFEFF with space, see https://github.com/golang/go/issues/42274#issuecomment-1017258184 - data = strings.ReplaceAll(data, string('\uFEFF'), " ") - data = strings.TrimSpace(spaceRe.ReplaceAllString(data, " ")) + data := html2text.Strip(html, false) if len(data) <= limit { return data diff --git a/package-lock.json b/package-lock.json index ebd7580..7a06940 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1847,9 +1847,9 @@ } }, "node_modules/object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.0.tgz", + "integrity": "sha512-HQ4J+ic8hKrgIt3mqk6cVOVrW2ozL4KdvHlqpBv9vDYWx9ysAgENAdvy4FoGF+KFdhR7nQTNm5J0ctAeOwn+3g==", "funding": { "url": "https://github.com/sponsors/ljharb" } diff --git a/server/apiv1/api.go b/server/apiv1/api.go index c1f64a7..5ad4951 100644 --- a/server/apiv1/api.go +++ b/server/apiv1/api.go @@ -17,8 +17,8 @@ import ( "github.com/axllent/mailpit/internal/storage" "github.com/axllent/mailpit/internal/tools" "github.com/axllent/mailpit/server/smtpd" + "github.com/google/uuid" "github.com/gorilla/mux" - uuid "github.com/satori/go.uuid" ) // GetMessages returns a paginated list of messages as JSON @@ -144,11 +144,11 @@ func Search(w http.ResponseWriter, r *http.Request) { // DeleteSearch will delete all messages matching a search func DeleteSearch(w http.ResponseWriter, r *http.Request) { - // swagger:route DELETE /api/v1/search messages MessagesSummary + // swagger:route DELETE /api/v1/search messages DeleteSearch // // # Delete messages by search // - // Deletes messages matching a search. + // Delete all messages matching a search. // // Produces: // - application/json @@ -196,7 +196,7 @@ func GetMessage(w http.ResponseWriter, r *http.Request) { // Parameters: // + name: ID // in: path - // description: Database ID + // description: Message database ID // required: true // type: string // @@ -237,7 +237,7 @@ func DownloadAttachment(w http.ResponseWriter, r *http.Request) { // Parameters: // + name: ID // in: path - // description: Database ID + // description: Message database ID // required: true // type: string // + name: PartID @@ -362,11 +362,11 @@ func DownloadRaw(w http.ResponseWriter, r *http.Request) { // DeleteMessages (method: DELETE) deletes all messages matching IDS. func DeleteMessages(w http.ResponseWriter, r *http.Request) { - // swagger:route DELETE /api/v1/messages messages Delete + // swagger:route DELETE /api/v1/messages messages DeleteMessages // // # Delete messages // - // If no IDs are provided then all messages are deleted. + // Delete individual or all messages. If no IDs are provided then all messages are deleted. // // Consumes: // - application/json @@ -376,13 +376,6 @@ func DeleteMessages(w http.ResponseWriter, r *http.Request) { // // Schemes: http, https // - // Parameters: - // + name: ids - // in: body - // description: Database IDs to delete - // required: false - // type: DeleteRequest - // // Responses: // 200: OKResponse // default: ErrorResponse @@ -406,7 +399,7 @@ func DeleteMessages(w http.ResponseWriter, r *http.Request) { } } - w.Header().Add("Content-Type", "application/json") + w.Header().Add("Content-Type", "application/plain") _, _ = w.Write([]byte("ok")) } @@ -427,13 +420,6 @@ func SetReadStatus(w http.ResponseWriter, r *http.Request) { // // Schemes: http, https // - // Parameters: - // + name: ids - // in: body - // description: Database IDs to update - // required: false - // type: SetReadStatusRequest - // // Responses: // 200: OKResponse // default: ErrorResponse @@ -491,7 +477,7 @@ func SetReadStatus(w http.ResponseWriter, r *http.Request) { // GetTags (method: GET) will get all tags currently in use func GetTags(w http.ResponseWriter, _ *http.Request) { - // swagger:route GET /api/v1/tags tags SetTags + // swagger:route GET /api/v1/tags tags GetTags // // # Get all current tags // @@ -524,7 +510,7 @@ func SetTags(w http.ResponseWriter, r *http.Request) { // // # Set message tags // - // To remove all tags from a message, pass an empty tags array. + // This will overwrite any existing tags for selected message database IDs. To remove all tags from a message, pass an empty tags array. // // Consumes: // - application/json @@ -534,13 +520,6 @@ func SetTags(w http.ResponseWriter, r *http.Request) { // // Schemes: http, https // - // Parameters: - // + name: ids - // in: body - // description: Database IDs to update - // required: true - // type: SetTagsRequest - // // Responses: // 200: OKResponse // default: ErrorResponse @@ -576,11 +555,11 @@ func SetTags(w http.ResponseWriter, r *http.Request) { // ReleaseMessage (method: POST) will release a message via a pre-configured external SMTP server. // If no IDs are provided then all messages are updated. func ReleaseMessage(w http.ResponseWriter, r *http.Request) { - // swagger:route POST /api/v1/message/{ID}/release message Release + // swagger:route POST /api/v1/message/{ID}/release message ReleaseMessage // // # Release message // - // Release a message via a pre-configured external SMTP server.. + // Release a message via a pre-configured external SMTP server. This is only enabled if message relaying has been configured. // // Consumes: // - application/json @@ -590,18 +569,6 @@ func ReleaseMessage(w http.ResponseWriter, r *http.Request) { // // Schemes: http, https // - // Parameters: - // + name: ID - // in: path - // description: Database ID - // required: true - // type: string - // + name: to - // in: body - // description: Array of email addresses to release message to - // required: true - // type: ReleaseMessageRequest - // // Responses: // 200: OKResponse // default: ErrorResponse @@ -618,7 +585,7 @@ func ReleaseMessage(w http.ResponseWriter, r *http.Request) { decoder := json.NewDecoder(r.Body) - data := releaseMessageRequest{} + data := releaseMessageRequestBody{} if err := decoder.Decode(&data); err != nil { httpError(w, err.Error()) @@ -686,7 +653,7 @@ func ReleaseMessage(w http.ResponseWriter, r *http.Request) { } // generate unique ID - uid := uuid.NewV4().String() + "@mailpit" + uid := uuid.New().String() + "@mailpit" // add unique ID msg = append([]byte("Message-Id: <"+uid+">\r\n"), msg...) @@ -702,7 +669,7 @@ func ReleaseMessage(w http.ResponseWriter, r *http.Request) { // HTMLCheck returns a summary of the HTML client support func HTMLCheck(w http.ResponseWriter, r *http.Request) { - // swagger:route GET /api/v1/message/{ID}/html-check Other HTMLCheckResponse + // swagger:route GET /api/v1/message/{ID}/html-check Other HTMLCheck // // # HTML check (beta) // @@ -716,13 +683,6 @@ func HTMLCheck(w http.ResponseWriter, r *http.Request) { // // Schemes: http, https // - // Parameters: - // + name: ID - // in: path - // description: Database ID - // required: true - // type: string - // // Responses: // 200: HTMLCheckResponse // default: ErrorResponse @@ -754,7 +714,7 @@ func HTMLCheck(w http.ResponseWriter, r *http.Request) { // LinkCheck returns a summary of links in the email func LinkCheck(w http.ResponseWriter, r *http.Request) { - // swagger:route GET /api/v1/message/{ID}/link-check Other LinkCheckResponse + // swagger:route GET /api/v1/message/{ID}/link-check Other LinkCheck // // # Link check (beta) // @@ -768,19 +728,6 @@ func LinkCheck(w http.ResponseWriter, r *http.Request) { // // Schemes: http, https // - // Parameters: - // + name: ID - // in: path - // description: Database ID - // required: true - // type: string - // + name: follow - // in: query - // description: Follow redirects - // required: false - // type: boolean - // default: false - // // Responses: // 200: LinkCheckResponse // default: ErrorResponse diff --git a/server/apiv1/structs.go b/server/apiv1/structs.go index 318ef69..8bf1290 100644 --- a/server/apiv1/structs.go +++ b/server/apiv1/structs.go @@ -22,9 +22,6 @@ type MessagesSummary struct { // Total number of messages matching current query MessagesCount int `json:"messages_count"` - // // Number of results returned on current page - // Count int `json:"count"` - // Pagination offset Start int `json:"start"` @@ -32,7 +29,7 @@ type MessagesSummary struct { Tags []string `json:"tags"` // Messages summary - // in:body + // in: body Messages []storage.MessageSummary `json:"messages"` } diff --git a/server/apiv1/swagger.go b/server/apiv1/swagger.go index 43e0646..b5ed980 100644 --- a/server/apiv1/swagger.go +++ b/server/apiv1/swagger.go @@ -1,11 +1,15 @@ package apiv1 +import "os" + // These structs are for the purpose of defining swagger HTTP responses // Application information // swagger:response InfoResponse type infoResponse struct { // Application information + // + // in: body Body appInformation } @@ -13,6 +17,8 @@ type infoResponse struct { // swagger:response WebUIConfigurationResponse type webUIConfigurationResponse struct { // Web UI configuration settings + // + // in: body Body webUIConfiguration } @@ -28,81 +34,140 @@ type messagesSummaryResponse struct { // swagger:model MessageHeaders type messageHeaders map[string][]string +// swagger:parameters DeleteMessages +type deleteMessagesParams struct { + // in: body + Body *deleteMessagesRequestBody +} + // Delete request // swagger:model DeleteRequest -type deleteRequest struct { - // ids - // in:body +type deleteMessagesRequestBody struct { + // Array of message database IDs + // + // required: false + // example: ["5dec4247-812e-4b77-9101-e25ad406e9ea", "8ac66bbc-2d9a-4c41-ad99-00aa75fa674e"] IDs []string `json:"ids"` } +// swagger:parameters SetReadStatus +type setReadStatusParams struct { + // in: body + Body *setReadStatusRequestBody +} + // Set read status request -// swagger:model SetReadStatusRequest -type setReadStatusRequest struct { +// swagger:model setReadStatusRequestBody +type setReadStatusRequestBody struct { // Read status + // + // required: false + // default: false + // example: true Read bool `json:"read"` - // ids - // in:body + // Array of message database IDs + // + // required: false + // example: ["5dec4247-812e-4b77-9101-e25ad406e9ea", "8ac66bbc-2d9a-4c41-ad99-00aa75fa674e"] IDs []string `json:"ids"` } +// swagger:parameters SetTags +type setTagsParams struct { + // in: body + Body *setTagsRequestBody +} + // Set tags request -// swagger:model SetTagsRequest -type setTagsRequest struct { - // Tags - // in:body +// swagger:model setTagsRequestBody +type setTagsRequestBody struct { + // Array of tag names to set + // + // required: true + // example: ["Tag 1", "Tag 2"] Tags []string `json:"tags"` - // IDs - // in:body + // Array of message database IDs + // + // required: true + // example: ["5dec4247-812e-4b77-9101-e25ad406e9ea", "8ac66bbc-2d9a-4c41-ad99-00aa75fa674e"] IDs []string `json:"ids"` } +// swagger:parameters ReleaseMessage +type releaseMessageParams struct { + // Message database ID + // + // in: path + // description: Message database ID + // required: true + ID string + + // in: body + Body *releaseMessageRequestBody +} + // Release request -// swagger:model ReleaseMessageRequest -type releaseMessageRequest struct { - // To - // in:body +// swagger:model releaseMessageRequestBody +type releaseMessageRequestBody struct { + // Array of email addresses to relay the message to + // + // required: true + // example: ["user1@example.com", "user2@example.com"] To []string `json:"to"` } +// swagger:parameters HTMLCheck +type htmlCheckParams struct { + // Message database ID + // + // in: path + // description: Message database ID + // required: true + ID string +} + +// swagger:parameters LinkCheck +type linkCheckParams struct { + // Message database ID + // + // in: path + // description: Message database ID + // required: true + ID string + + // Follow redirects + // + // in: query + // description: Follow redirects + // required: false + // default: false + Follow string `json:"follow"` +} + // Binary data response inherits the attachment's content type // swagger:response BinaryResponse type binaryResponse struct { // in: body - Body string + File os.File } // Plain text response // swagger:response TextResponse -type textResponse struct { - // in: body - Body string -} +type textResponse string // HTML response // swagger:response HTMLResponse -type htmlResponse struct { - // in: body - Body string -} +type htmlResponse string // Error response // swagger:response ErrorResponse -type errorResponse struct { - // The error message - // in: body - Body string -} +type errorResponse string // Plain text "ok" response // swagger:response OKResponse -type okResponse struct { - // Default response - // in: body - Body string -} +type okResponse string // Plain JSON array response // swagger:response ArrayResponse diff --git a/server/smtpd/smtpd.go b/server/smtpd/smtpd.go index e953106..d0e310e 100644 --- a/server/smtpd/smtpd.go +++ b/server/smtpd/smtpd.go @@ -13,8 +13,8 @@ import ( "github.com/axllent/mailpit/internal/auth" "github.com/axllent/mailpit/internal/logger" "github.com/axllent/mailpit/internal/storage" + "github.com/google/uuid" "github.com/mhale/smtpd" - uuid "github.com/satori/go.uuid" ) func mailHandler(origin net.Addr, from string, to []string, data []byte) error { @@ -57,7 +57,7 @@ func mailHandler(origin net.Addr, from string, to []string, data []byte) error { // add a message ID if not set if messageID == "" { // generate unique ID - messageID = uuid.NewV4().String() + "@mailpit" + messageID = uuid.New().String() + "@mailpit" // add unique ID data = append([]byte("Message-Id: <"+messageID+">\r\n"), data...) } else if config.IgnoreDuplicateIDs { diff --git a/server/ui/api/v1/swagger.json b/server/ui/api/v1/swagger.json index d0a85a5..29c18da 100644 --- a/server/ui/api/v1/swagger.json +++ b/server/ui/api/v1/swagger.json @@ -66,7 +66,7 @@ "parameters": [ { "type": "string", - "description": "Database ID", + "description": "Message database ID", "name": "ID", "in": "path", "required": true @@ -136,11 +136,11 @@ "Other" ], "summary": "HTML check (beta)", - "operationId": "HTMLCheckResponse", + "operationId": "HTMLCheck", "parameters": [ { "type": "string", - "description": "Database ID", + "description": "Message database ID", "name": "ID", "in": "path", "required": true @@ -173,18 +173,19 @@ "Other" ], "summary": "Link check (beta)", - "operationId": "LinkCheckResponse", + "operationId": "LinkCheck", "parameters": [ { "type": "string", - "description": "Database ID", + "description": "Message database ID", "name": "ID", "in": "path", "required": true }, { - "type": "boolean", - "default": false, + "type": "string", + "default": "false", + "x-go-name": "Follow", "description": "Follow redirects", "name": "follow", "in": "query" @@ -223,7 +224,7 @@ "parameters": [ { "type": "string", - "description": "Database ID", + "description": "Message database ID", "name": "ID", "in": "path", "required": true @@ -323,7 +324,7 @@ }, "/api/v1/message/{ID}/release": { "post": { - "description": "Release a message via a pre-configured external SMTP server..", + "description": "Release a message via a pre-configured external SMTP server. This is only enabled if message relaying has been configured.", "consumes": [ "application/json" ], @@ -338,24 +339,20 @@ "message" ], "summary": "Release message", - "operationId": "Release", + "operationId": "ReleaseMessage", "parameters": [ { "type": "string", - "description": "Database ID", + "description": "Message database ID", "name": "ID", "in": "path", "required": true }, { - "description": "Array of email addresses to release message to", - "name": "to", + "name": "Body", "in": "body", - "required": true, "schema": { - "description": "Array of email addresses to release message to", - "type": "object", - "$ref": "#/definitions/ReleaseMessageRequest" + "$ref": "#/definitions/releaseMessageRequestBody" } } ], @@ -428,13 +425,10 @@ "operationId": "SetReadStatus", "parameters": [ { - "description": "Database IDs to update", - "name": "ids", + "name": "Body", "in": "body", "schema": { - "description": "Database IDs to update", - "type": "object", - "$ref": "#/definitions/SetReadStatusRequest" + "$ref": "#/definitions/setReadStatusRequestBody" } } ], @@ -448,7 +442,7 @@ } }, "delete": { - "description": "If no IDs are provided then all messages are deleted.", + "description": "Delete individual or all messages. If no IDs are provided then all messages are deleted.", "consumes": [ "application/json" ], @@ -463,15 +457,12 @@ "messages" ], "summary": "Delete messages", - "operationId": "Delete", + "operationId": "DeleteMessages", "parameters": [ { - "description": "Database IDs to delete", - "name": "ids", + "name": "Body", "in": "body", "schema": { - "description": "Database IDs to delete", - "type": "object", "$ref": "#/definitions/DeleteRequest" } } @@ -534,7 +525,7 @@ } }, "delete": { - "description": "Deletes messages matching a search.", + "description": "Delete all messages matching a search.", "produces": [ "application/json" ], @@ -546,7 +537,7 @@ "messages" ], "summary": "Delete messages by search", - "operationId": "MessagesSummary", + "operationId": "DeleteSearch", "parameters": [ { "type": "string", @@ -580,7 +571,7 @@ "tags" ], "summary": "Get all current tags", - "operationId": "SetTags", + "operationId": "GetTags", "responses": { "200": { "$ref": "#/responses/ArrayResponse" @@ -591,7 +582,7 @@ } }, "put": { - "description": "To remove all tags from a message, pass an empty tags array.", + "description": "This will overwrite any existing tags for selected message database IDs. To remove all tags from a message, pass an empty tags array.", "consumes": [ "application/json" ], @@ -609,14 +600,10 @@ "operationId": "SetTags", "parameters": [ { - "description": "Database IDs to update", - "name": "ids", + "name": "Body", "in": "body", - "required": true, "schema": { - "description": "Database IDs to update", - "type": "object", - "$ref": "#/definitions/SetTagsRequest" + "$ref": "#/definitions/setTagsRequestBody" } } ], @@ -807,17 +794,26 @@ "type": "object", "properties": { "ids": { - "description": "ids\nin:body", + "description": "Array of message database IDs", "type": "array", "items": { "type": "string" }, - "x-go-name": "IDs" + "x-go-name": "IDs", + "example": [ + "5dec4247-812e-4b77-9101-e25ad406e9ea", + "8ac66bbc-2d9a-4c41-ad99-00aa75fa674e" + ] } }, - "x-go-name": "deleteRequest", + "x-go-name": "deleteMessagesRequestBody", "x-go-package": "github.com/axllent/mailpit/server/apiv1" }, + "File": { + "type": "object", + "title": "File represents an open file descriptor.", + "x-go-package": "os" + }, "HTMLCheckResponse": { "description": "Response represents the HTML check response struct", "type": "object", @@ -1188,6 +1184,10 @@ "type": "integer", "format": "int64" }, + "Snippet": { + "description": "Message snippet includes up to 250 characters", + "type": "string" + }, "Subject": { "description": "Email subject", "type": "string" @@ -1214,7 +1214,7 @@ "type": "object", "properties": { "messages": { - "description": "Messages summary\nin:body", + "description": "Messages summary\nin: body", "type": "array", "items": { "$ref": "#/definitions/MessageSummary" @@ -1256,67 +1256,6 @@ }, "x-go-package": "github.com/axllent/mailpit/server/apiv1" }, - "ReleaseMessageRequest": { - "description": "Release request", - "type": "object", - "properties": { - "to": { - "description": "To\nin:body", - "type": "array", - "items": { - "type": "string" - }, - "x-go-name": "To" - } - }, - "x-go-name": "releaseMessageRequest", - "x-go-package": "github.com/axllent/mailpit/server/apiv1" - }, - "SetReadStatusRequest": { - "description": "Set read status request", - "type": "object", - "properties": { - "ids": { - "description": "ids\nin:body", - "type": "array", - "items": { - "type": "string" - }, - "x-go-name": "IDs" - }, - "read": { - "description": "Read status", - "type": "boolean", - "x-go-name": "Read" - } - }, - "x-go-name": "setReadStatusRequest", - "x-go-package": "github.com/axllent/mailpit/server/apiv1" - }, - "SetTagsRequest": { - "description": "Set tags request", - "type": "object", - "properties": { - "ids": { - "description": "IDs\nin:body", - "type": "array", - "items": { - "type": "string" - }, - "x-go-name": "IDs" - }, - "tags": { - "description": "Tags\nin:body", - "type": "array", - "items": { - "type": "string" - }, - "x-go-name": "Tags" - } - }, - "x-go-name": "setTagsRequest", - "x-go-package": "github.com/axllent/mailpit/server/apiv1" - }, "WebUIConfiguration": { "description": "Response includes global web UI settings", "type": "object", @@ -1350,6 +1289,89 @@ }, "x-go-name": "webUIConfiguration", "x-go-package": "github.com/axllent/mailpit/server/apiv1" + }, + "releaseMessageRequestBody": { + "description": "Release request", + "type": "object", + "required": [ + "to" + ], + "properties": { + "to": { + "description": "Array of email addresses to relay the message to", + "type": "array", + "items": { + "type": "string" + }, + "x-go-name": "To", + "example": [ + "user1@example.com", + "user2@example.com" + ] + } + }, + "x-go-package": "github.com/axllent/mailpit/server/apiv1" + }, + "setReadStatusRequestBody": { + "description": "Set read status request", + "type": "object", + "properties": { + "ids": { + "description": "Array of message database IDs", + "type": "array", + "items": { + "type": "string" + }, + "x-go-name": "IDs", + "example": [ + "5dec4247-812e-4b77-9101-e25ad406e9ea", + "8ac66bbc-2d9a-4c41-ad99-00aa75fa674e" + ] + }, + "read": { + "description": "Read status", + "type": "boolean", + "default": false, + "x-go-name": "Read", + "example": true + } + }, + "x-go-package": "github.com/axllent/mailpit/server/apiv1" + }, + "setTagsRequestBody": { + "description": "Set tags request", + "type": "object", + "required": [ + "tags", + "ids" + ], + "properties": { + "ids": { + "description": "Array of message database IDs", + "type": "array", + "items": { + "type": "string" + }, + "x-go-name": "IDs", + "example": [ + "5dec4247-812e-4b77-9101-e25ad406e9ea", + "8ac66bbc-2d9a-4c41-ad99-00aa75fa674e" + ] + }, + "tags": { + "description": "Array of tag names to set", + "type": "array", + "items": { + "type": "string" + }, + "x-go-name": "Tags", + "example": [ + "Tag 1", + "Tag 2" + ] + } + }, + "x-go-package": "github.com/axllent/mailpit/server/apiv1" } }, "responses": { @@ -1363,23 +1385,27 @@ } }, "BinaryResponse": { - "description": "Binary data response inherits the attachment's content type" + "description": "Binary data response inherits the attachment's content type", + "schema": { + "$ref": "#/definitions/File" + } }, "ErrorResponse": { - "description": "Error response" + "description": "Error response", + "schema": { + "type": "string" + } }, "HTMLResponse": { - "description": "HTML response" + "description": "HTML response", + "schema": { + "type": "string" + } }, "InfoResponse": { "description": "Application information", "schema": { "$ref": "#/definitions/AppInformation" - }, - "headers": { - "Body": { - "description": "Application information" - } } }, "MessagesSummaryResponse": { @@ -1389,20 +1415,21 @@ } }, "OKResponse": { - "description": "Plain text \"ok\" response" + "description": "Plain text \"ok\" response", + "schema": { + "type": "string" + } }, "TextResponse": { - "description": "Plain text response" + "description": "Plain text response", + "schema": { + "type": "string" + } }, "WebUIConfigurationResponse": { "description": "Web UI configuration", "schema": { "$ref": "#/definitions/WebUIConfiguration" - }, - "headers": { - "Body": { - "description": "Web UI configuration settings" - } } } }