Skip to content

Commit

Permalink
Merge branch 'release/v1.21.1'
Browse files Browse the repository at this point in the history
  • Loading branch information
axllent committed Nov 1, 2024
2 parents 10c20dd + 2743e2e commit 0f24496
Show file tree
Hide file tree
Showing 7 changed files with 138 additions and 11 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,17 @@

Notable changes to Mailpit will be documented in this file.

## [v1.21.1]

### Feature
- Add ability to search by size smaller or larger than a value (eg: `larger:1M` / `smaller:2.5M`)
- Add ability to search for messages containing inline images (`has:inline`)

### Chore
- Update Go dependencies
- Separate attachments and inline images in download nav and badges ([#379](https://github.com/axllent/mailpit/issues/379))


## [v1.21.0]

### Feature
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ require (
github.com/sirupsen/logrus v1.9.3
github.com/spf13/cobra v1.8.1
github.com/spf13/pflag v1.0.5
github.com/tg123/go-htpasswd v1.2.2
github.com/tg123/go-htpasswd v1.2.3
github.com/vanng822/go-premailer v1.22.0
golang.org/x/net v0.30.0
golang.org/x/text v0.19.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,8 @@ github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tg123/go-htpasswd v1.2.2 h1:tmNccDsQ+wYsoRfiONzIhDm5OkVHQzN3w4FOBAlN6BY=
github.com/tg123/go-htpasswd v1.2.2/go.mod h1:FcIrK0J+6zptgVwK1JDlqyajW/1B4PtuJ/FLWl7nx8A=
github.com/tg123/go-htpasswd v1.2.3 h1:ALR6ZBIc2m9u70m+eAWUFt5p43ISbIvAvRFYzZPTOY8=
github.com/tg123/go-htpasswd v1.2.3/go.mod h1:FcIrK0J+6zptgVwK1JDlqyajW/1B4PtuJ/FLWl7nx8A=
github.com/unrolled/render v1.7.0/go.mod h1:LwQSeDhjml8NLjIO9GJO1/1qpFJxtfVIpzxXKjfVkoI=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
Expand Down
60 changes: 60 additions & 0 deletions internal/storage/search.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import (
"context"
"database/sql"
"encoding/json"
"fmt"
"regexp"
"strconv"
"strings"
"time"

Expand Down Expand Up @@ -355,6 +357,12 @@ func searchQueryBuilder(searchString, timezone string) *sqlf.Stmt {
} else {
q.Where(`m.ID IN (SELECT DISTINCT mt.ID FROM ` + tenant("message_tags") + ` mt JOIN tags t ON mt.TagID = t.ID)`)
}
} else if lw == "has:inline" || lw == "has:inlines" {
if exclude {
q.Where("Inline = 0")
} else {
q.Where("Inline > 0")
}
} else if lw == "has:attachment" || lw == "has:attachments" {
if exclude {
q.Where("Attachments = 0")
Expand Down Expand Up @@ -391,6 +399,22 @@ func searchQueryBuilder(searchString, timezone string) *sqlf.Stmt {
}
}
}
} else if strings.HasPrefix(lw, "larger:") && sizeToBytes(cleanString(w[7:])) > 0 {
w = cleanString(w[7:])
size := sizeToBytes(w)
if exclude {
q.Where("Size < ?", size)
} else {
q.Where("Size > ?", size)
}
} else if strings.HasPrefix(lw, "smaller:") && sizeToBytes(cleanString(w[8:])) > 0 {
w = cleanString(w[8:])
size := sizeToBytes(w)
if exclude {
q.Where("Size > ?", size)
} else {
q.Where("Size < ?", size)
}
} else {
// search text
if exclude {
Expand All @@ -403,3 +427,39 @@ func searchQueryBuilder(searchString, timezone string) *sqlf.Stmt {

return q
}

// Simple function to return a size in bytes, eg 2kb, 4MB or 1.5m.
//
// K, k, Kb, KB, kB and kb are treated as Kilobytes.
// M, m, Mb, MB and mb are treated as Megabytes.
func sizeToBytes(v string) int64 {
v = strings.ToLower(v)
re := regexp.MustCompile(`^(\d+)(\.\d+)?\s?([a-z]{1,2})?$`)

m := re.FindAllStringSubmatch(v, -1)
if len(m) == 0 {
return 0
}

val := fmt.Sprintf("%s%s", m[0][1], m[0][2])
unit := m[0][3]

i, err := strconv.ParseFloat(strings.TrimSpace(val), 64)
if err != nil {
return 0
}

if unit == "" {
return int64(i)
}

if unit == "k" || unit == "kb" {
return int64(i * 1024)
}

if unit == "m" || unit == "mb" {
return int64(i * 1024 * 1024)
}

return 0
}
22 changes: 22 additions & 0 deletions internal/storage/search_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,3 +201,25 @@ func TestEscPercentChar(t *testing.T) {
assertEqual(t, res, expected, "no match")
}
}

func TestSizeToBytes(t *testing.T) {
tests := map[string]int64{}
tests["1m"] = 1048576
tests["1mb"] = 1048576
tests["1 M"] = 1048576
tests["1 MB"] = 1048576
tests["1k"] = 1024
tests["1kb"] = 1024
tests["1 K"] = 1024
tests["1 kB"] = 1024
tests["1.5M"] = 1572864
tests["1234567890"] = 1234567890
tests["invalid"] = 0
tests["1.2.3"] = 0
tests["1.2.3M"] = 0

for search, expected := range tests {
res := sizeToBytes(search)
assertEqual(t, res, expected, "size does not match")
}
}
19 changes: 14 additions & 5 deletions server/ui-src/components/message/Message.vue
Original file line number Diff line number Diff line change
Expand Up @@ -457,11 +457,20 @@ export default {
</tbody>
</table>
</div>
<div class="col-md-auto d-none d-md-block text-end mt-md-3">
<div class="mt-2 mt-md-0" v-if="allAttachments(message)">
<span class="badge rounded-pill text-bg-secondary p-2">
Attachment<span v-if="allAttachments(message).length > 1">s</span>
({{ allAttachments(message).length }})
<div class="col-md-auto d-none d-md-block text-end mt-md-3"
v-if="message.Attachments && message.Attachments.length || message.Inline && message.Inline.length">
<div class="mt-2 mt-md-0">
<template v-if="message.Attachments.length">
<span class="badge rounded-pill text-bg-secondary p-2 mb-2" title="Attachments in this message">
Attachment<span v-if="message.Attachments.length > 1">s</span>
({{ message.Attachments.length }})
</span>
<br>
</template>
<span class="badge rounded-pill text-bg-secondary p-2" v-if="message.Inline.length"
title="Inline images in this message">
Inline image<span v-if="message.Inline.length > 1">s</span>
({{ message.Inline.length }})
</span>
</div>
</div>
Expand Down
31 changes: 28 additions & 3 deletions server/ui-src/views/MessageView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -504,16 +504,41 @@ export default {
Text body
</button>
</li>
<template v-if="allAttachments(message).length">
<template v-if="message.Attachments && message.Attachments.length">
<li>
<hr class="dropdown-divider">
</li>
<li>
<h6 class="dropdown-header">
Attachment<template v-if="allAttachments(message).length > 1">s</template>
Attachments
</h6>
</li>
<li v-for="part in allAttachments(message)">
<li v-for="part in message.Attachments">
<RouterLink :to="'/api/v1/message/' + message.ID + '/part/' + part.PartID"
class="row m-0 dropdown-item d-flex" target="_blank"
:title="part.FileName != '' ? part.FileName : '[ unknown ]'" style="min-width: 350px">
<div class="col-auto p-0 pe-1">
<i class="bi" :class="attachmentIcon(part)"></i>
</div>
<div class="col text-truncate p-0 pe-1">
{{ part.FileName != '' ? part.FileName : '[ unknown ]' }}
</div>
<div class="col-auto text-muted small p-0">
{{ getFileSize(part.Size) }}
</div>
</RouterLink>
</li>
</template>
<template v-if="message.Inline && message.Inline.length">
<li>
<hr class="dropdown-divider">
</li>
<li>
<h6 class="dropdown-header">
Inline image<span v-if="message.Inline.length > 1">s</span>
</h6>
</li>
<li v-for="part in message.Inline">
<RouterLink :to="'/api/v1/message/' + message.ID + '/part/' + part.PartID"
class="row m-0 dropdown-item d-flex" target="_blank"
:title="part.FileName != '' ? part.FileName : '[ unknown ]'" style="min-width: 350px">
Expand Down

0 comments on commit 0f24496

Please sign in to comment.