mirror of
https://github.com/axllent/mailpit.git
synced 2025-03-19 21:28:07 +02:00
Merge branch 'release/0.1.5'
This commit is contained in:
commit
8b6b6640d5
10
CHANGELOG.md
10
CHANGELOG.md
@ -3,6 +3,16 @@
|
|||||||
Notable changes to Mailpit will be documented in this file.
|
Notable changes to Mailpit will be documented in this file.
|
||||||
|
|
||||||
|
|
||||||
|
## 0.1.5
|
||||||
|
|
||||||
|
### Feature
|
||||||
|
- Improved message search - any order & phrase quoting
|
||||||
|
|
||||||
|
### UI
|
||||||
|
- Change breakpoints for mobile view of messages
|
||||||
|
- Resize iframes with viewport resize
|
||||||
|
|
||||||
|
|
||||||
## 0.1.4
|
## 0.1.4
|
||||||
|
|
||||||
### Feature
|
### Feature
|
||||||
|
1
go.mod
1
go.mod
@ -9,6 +9,7 @@ require (
|
|||||||
github.com/jhillyerd/enmime v0.10.0
|
github.com/jhillyerd/enmime v0.10.0
|
||||||
github.com/k3a/html2text v1.0.8
|
github.com/k3a/html2text v1.0.8
|
||||||
github.com/klauspost/compress v1.15.9
|
github.com/klauspost/compress v1.15.9
|
||||||
|
github.com/mattn/go-shellwords v1.0.12
|
||||||
github.com/mhale/smtpd v0.8.0
|
github.com/mhale/smtpd v0.8.0
|
||||||
github.com/ostafen/clover/v2 v2.0.0-alpha.2
|
github.com/ostafen/clover/v2 v2.0.0-alpha.2
|
||||||
github.com/sirupsen/logrus v1.9.0
|
github.com/sirupsen/logrus v1.9.0
|
||||||
|
2
go.sum
2
go.sum
@ -124,6 +124,8 @@ github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m
|
|||||||
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||||
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
|
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
|
||||||
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||||
|
github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk=
|
||||||
|
github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
|
||||||
github.com/mhale/smtpd v0.8.0 h1:5JvdsehCg33PQrZBvFyDMMUDQmvbzVpZgKob7eYBJc0=
|
github.com/mhale/smtpd v0.8.0 h1:5JvdsehCg33PQrZBvFyDMMUDQmvbzVpZgKob7eYBJc0=
|
||||||
github.com/mhale/smtpd v0.8.0/go.mod h1:MQl+y2hwIEQCXtNhe5+55n0GZOjSmeqORDIXbqUL3x4=
|
github.com/mhale/smtpd v0.8.0/go.mod h1:MQl+y2hwIEQCXtNhe5+55n0GZOjSmeqORDIXbqUL3x4=
|
||||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
|
@ -443,33 +443,33 @@ export default {
|
|||||||
<div class="list-group" v-if="items.length">
|
<div class="list-group" v-if="items.length">
|
||||||
<a v-for="message in items" :href="'#'+message.ID" class="row message d-flex small list-group-item list-group-item-action"
|
<a v-for="message in items" :href="'#'+message.ID" class="row message d-flex small list-group-item list-group-item-action"
|
||||||
:class="message.Read ? 'read':''" XXXv-on:click="openMessage(message)">
|
:class="message.Read ? 'read':''" XXXv-on:click="openMessage(message)">
|
||||||
<div class="col-md-3">
|
<div class="col-lg-3">
|
||||||
<div class="d-md-none float-end text-muted text-nowrap small">
|
<div class="d-lg-none float-end text-muted text-nowrap small">
|
||||||
<i class="bi bi-paperclip h6 me-1" v-if="message.Attachments"></i>
|
<i class="bi bi-paperclip h6 me-1" v-if="message.Attachments"></i>
|
||||||
{{ getRelativeCreated(message) }}
|
{{ getRelativeCreated(message) }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="text-truncate d-md-none privacy">
|
<div class="text-truncate d-lg-none privacy">
|
||||||
<span v-if="message.From" :title="message.From.Address">{{ message.From.Name ? message.From.Name : message.From.Address }}</span>
|
<span v-if="message.From" :title="message.From.Address">{{ message.From.Name ? message.From.Name : message.From.Address }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-truncate d-none d-md-block privacy">
|
<div class="text-truncate d-none d-lg-block privacy">
|
||||||
<b v-if="message.From" :title="message.From.Address">{{ message.From.Name ? message.From.Name : message.From.Address }}</b>
|
<b v-if="message.From" :title="message.From.Address">{{ message.From.Name ? message.From.Name : message.From.Address }}</b>
|
||||||
</div>
|
</div>
|
||||||
<div class="d-none d-md-block text-truncate text-muted small privacy">
|
<div class="d-none d-lg-block text-truncate text-muted small privacy">
|
||||||
{{ getPrimaryEmailTo(message) }}
|
{{ getPrimaryEmailTo(message) }}
|
||||||
<span v-if="message.To && message.To.length > 1">
|
<span v-if="message.To && message.To.length > 1">
|
||||||
[+{{message.To.length - 1}}]
|
[+{{message.To.length - 1}}]
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6 mt-2 mt-md-0">
|
<div class="col-lg-6 mt-2 mt-lg-0">
|
||||||
<b>{{ message.Subject != "" ? message.Subject : "[ no subject ]" }}</b>
|
<b>{{ message.Subject != "" ? message.Subject : "[ no subject ]" }}</b>
|
||||||
</div>
|
</div>
|
||||||
<div class="d-none d-md-block col-1 small text-end text-muted">
|
<div class="d-none d-lg-block col-1 small text-end text-muted">
|
||||||
<i class="bi bi-paperclip float-start h6" v-if="message.Attachments"></i>
|
<i class="bi bi-paperclip float-start h6" v-if="message.Attachments"></i>
|
||||||
{{ getFileSize(message.Size) }}
|
{{ getFileSize(message.Size) }}
|
||||||
</div>
|
</div>
|
||||||
<div class="d-none d-md-block col-2 small text-end text-muted">
|
<div class="d-none d-lg-block col-2 small text-end text-muted">
|
||||||
{{ getRelativeCreated(message) }}
|
{{ getRelativeCreated(message) }}
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
|
@ -44,6 +44,7 @@
|
|||||||
font-family: Courier New, Courier, System, fixed-width;
|
font-family: Courier New, Courier, System, fixed-width;
|
||||||
font-size: 0.85em;
|
font-size: 0.85em;
|
||||||
}
|
}
|
||||||
|
|
||||||
#nav-plain-text {
|
#nav-plain-text {
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
}
|
}
|
||||||
@ -52,7 +53,7 @@
|
|||||||
margin: 15px 0 0;
|
margin: 15px 0 0;
|
||||||
|
|
||||||
th {
|
th {
|
||||||
padding-right: 10px;
|
padding-right: 1.5rem;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
}
|
}
|
||||||
@ -62,6 +63,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#nav-html {
|
||||||
|
padding-right: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#preview-html {
|
||||||
|
min-height: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
.list-group-item:first-child {
|
.list-group-item:first-child {
|
||||||
border-top: 0;
|
border-top: 0;
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ export default {
|
|||||||
iframes: [], // for resizing
|
iframes: [], // for resizing
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
@ -46,11 +47,29 @@ export default {
|
|||||||
self.srcURI = 'api/' + self.mailbox + '/' + self.message.ID + '/source';
|
self.srcURI = 'api/' + self.mailbox + '/' + self.message.ID + '/source';
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
unmounted: function() {
|
||||||
|
window.removeEventListener("resize", this.resizeIframes);
|
||||||
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
resizeIframe: function(el) {
|
resizeIframe: function(el) {
|
||||||
let i = el.target;
|
let i = el.target;
|
||||||
i.style.height = i.contentWindow.document.body.scrollHeight + 50 + 'px';
|
i.style.height = i.contentWindow.document.body.scrollHeight + 50 + 'px';
|
||||||
},
|
},
|
||||||
|
|
||||||
|
resizeIframes: function() {
|
||||||
|
let h = document.getElementById('preview-html');
|
||||||
|
if (h) {
|
||||||
|
h.style.height = h.contentWindow.document.body.scrollHeight + 50 + 'px';
|
||||||
|
}
|
||||||
|
|
||||||
|
let s = document.getElementById('message-src');
|
||||||
|
if (s) {
|
||||||
|
s.style.height = s.contentWindow.document.body.scrollHeight + 50 + 'px';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
allAttachments: function(message){
|
allAttachments: function(message){
|
||||||
let a = [];
|
let a = [];
|
||||||
for (let i in message.Attachments) {
|
for (let i in message.Attachments) {
|
||||||
|
@ -18,6 +18,7 @@ import (
|
|||||||
"github.com/axllent/mailpit/server/websockets"
|
"github.com/axllent/mailpit/server/websockets"
|
||||||
"github.com/jhillyerd/enmime"
|
"github.com/jhillyerd/enmime"
|
||||||
"github.com/klauspost/compress/zstd"
|
"github.com/klauspost/compress/zstd"
|
||||||
|
"github.com/mattn/go-shellwords"
|
||||||
"github.com/ostafen/clover/v2"
|
"github.com/ostafen/clover/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -327,21 +328,58 @@ func List(mailbox string, start, limit int) ([]data.Summary, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Search returns a summary of items mathing a search. It searched the SearchText field.
|
// Search returns a summary of items mathing a search. It searched the SearchText field.
|
||||||
func Search(mailbox, search string, start, limit int) ([]data.Summary, error) {
|
func Search(mailbox, s string, start, limit int) ([]data.Summary, error) {
|
||||||
mailbox = sanitizeMailboxName(mailbox)
|
mailbox = sanitizeMailboxName(mailbox)
|
||||||
|
|
||||||
sq := fmt.Sprintf("(?i)%s", cleanString(regexp.QuoteMeta(search)))
|
s = strings.ToLower(s)
|
||||||
|
s = strings.Replace(s, "'", `\'`, -1)
|
||||||
|
s = strings.Replace(s, "(", ``, -1)
|
||||||
|
s = strings.Replace(s, ")", ``, -1)
|
||||||
|
// add another quote if quotes are odd
|
||||||
|
quotes := strings.Count(s, `"`)
|
||||||
|
if quotes%2 != 0 {
|
||||||
|
s += `"`
|
||||||
|
}
|
||||||
|
|
||||||
|
p := shellwords.NewParser()
|
||||||
|
args, err := p.Parse(s)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("Your search contains invalid characters")
|
||||||
|
}
|
||||||
|
|
||||||
|
results := []data.Summary{}
|
||||||
|
include := []string{}
|
||||||
|
|
||||||
|
for _, w := range args {
|
||||||
|
word := cleanString(w)
|
||||||
|
if word != "" {
|
||||||
|
include = append(include, fmt.Sprintf("%s", regexp.QuoteMeta(word)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(include) == 0 {
|
||||||
|
return results, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var where clover.Criteria
|
||||||
|
|
||||||
|
for i, w := range include {
|
||||||
|
if i == 0 {
|
||||||
|
where = clover.Field("SearchText").Like(w)
|
||||||
|
} else {
|
||||||
|
where = where.And(clover.Field("SearchText").Like(w))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
q, err := db.FindAll(clover.NewQuery(mailbox).
|
q, err := db.FindAll(clover.NewQuery(mailbox).
|
||||||
Skip(start).
|
Skip(start).
|
||||||
Limit(limit).
|
Limit(limit).
|
||||||
Sort(clover.SortOption{Field: "Created", Direction: -1}).
|
Sort(clover.SortOption{Field: "Created", Direction: -1}).
|
||||||
Where(clover.Field("SearchText").Like(sq)))
|
Where(where))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
results := []data.Summary{}
|
|
||||||
|
|
||||||
for _, d := range q {
|
for _, d := range q {
|
||||||
cs := &data.Summary{}
|
cs := &data.Summary{}
|
||||||
if err := d.Unmarshal(cs); err != nil {
|
if err := d.Unmarshal(cs); err != nil {
|
||||||
|
@ -259,9 +259,9 @@ RepeatTest:
|
|||||||
case 2:
|
case 2:
|
||||||
search = fmt.Sprintf("to-%d@example.com", i)
|
search = fmt.Sprintf("to-%d@example.com", i)
|
||||||
case 3:
|
case 3:
|
||||||
search = fmt.Sprintf("Subject line %d end", i)
|
search = fmt.Sprintf("\"Subject line %d end\"", i)
|
||||||
default:
|
default:
|
||||||
search = fmt.Sprintf("the email body %d jdsauk dwqmdqw", i)
|
search = fmt.Sprintf("\"the email body %d jdsauk dwqmdqw\"", i)
|
||||||
}
|
}
|
||||||
|
|
||||||
summaries, err := Search(DefaultMailbox, search, 0, 10)
|
summaries, err := Search(DefaultMailbox, search, 0, 10)
|
||||||
|
@ -178,7 +178,7 @@ func GithubUpdate(repo, appName, currentVersion string) (string, error) {
|
|||||||
// get the running binary
|
// get the running binary
|
||||||
oldExec, err := os.Executable()
|
oldExec, err := os.Executable()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = replaceFile(oldExec, newExec); err != nil {
|
if err = replaceFile(oldExec, newExec); err != nil {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user