diff --git a/server/ui-src/mixins/CommonMixins.js b/server/ui-src/mixins/CommonMixins.js
index da71142..530383f 100644
--- a/server/ui-src/mixins/CommonMixins.js
+++ b/server/ui-src/mixins/CommonMixins.js
@@ -20,9 +20,16 @@ export default {
return {
loading: 0,
tagColorCache: {},
+ copiedText: {}, // used for clipboard copy feedback
};
},
+ computed: {
+ copyToClipboardSupported() {
+ return !!navigator.clipboard;
+ },
+ },
+
methods: {
resolve(u) {
return this.$router.resolve(u).href;
@@ -222,12 +229,15 @@ export default {
allAttachments(message) {
const a = [];
for (const i in message.Attachments) {
+ message.Attachments[i].ContentDisposition = "Attachment";
a.push(message.Attachments[i]);
}
for (const i in message.OtherParts) {
+ message.OtherParts[i].ContentDisposition = "Other";
a.push(message.OtherParts[i]);
}
for (const i in message.Inline) {
+ message.Inline[i].ContentDisposition = "Inline";
a.push(message.Inline[i]);
}
@@ -288,5 +298,21 @@ export default {
return this.tagColorCache[s];
},
+
+ // Copy to clipboard functionality
+ copyToClipboard(text) {
+ navigator.clipboard.writeText(text).then(
+ () => {
+ this.copiedText[text] = true;
+ setTimeout(() => {
+ delete this.copiedText[text];
+ }, 2000);
+ },
+ () => {
+ // failure
+ alert("Failed to copy to clipboard");
+ },
+ );
+ },
},
};
diff --git a/server/ui-src/stores/mailbox.js b/server/ui-src/stores/mailbox.js
index 35b3d22..1fea4d8 100644
--- a/server/ui-src/stores/mailbox.js
+++ b/server/ui-src/stores/mailbox.js
@@ -32,6 +32,7 @@ export const mailbox = reactive({
timeZone: localStorage.getItem("timeZone")
? localStorage.getItem("timeZone")
: Intl.DateTimeFormat().resolvedOptions().timeZone,
+ showAttachmentDetails: localStorage.getItem("showAttachmentDetails"), // show attachment details
});
watch(
@@ -106,3 +107,14 @@ watch(
}
},
);
+
+watch(
+ () => mailbox.showAttachmentDetails,
+ (v) => {
+ if (v) {
+ localStorage.setItem("showAttachmentDetails", "1");
+ } else {
+ localStorage.removeItem("showAttachmentDetails");
+ }
+ },
+);
diff --git a/server/ui/api/v1/swagger.json b/server/ui/api/v1/swagger.json
index e5fc5fd..581c311 100644
--- a/server/ui/api/v1/swagger.json
+++ b/server/ui/api/v1/swagger.json
@@ -1345,9 +1345,27 @@
"x-go-package": "github.com/axllent/mailpit/internal/stats"
},
"Attachment": {
- "description": "Attachment struct for inline and attachments",
+ "description": "Attachment struct for inline images and attachments",
"type": "object",
"properties": {
+ "Checksums": {
+ "description": "File checksums",
+ "type": "object",
+ "properties": {
+ "MD5": {
+ "description": "MD5 checksum hash of file",
+ "type": "string"
+ },
+ "SHA1": {
+ "description": "SHA1 checksum hash of file",
+ "type": "string"
+ },
+ "SHA256": {
+ "description": "SHA256 checksum hash of file",
+ "type": "string"
+ }
+ }
+ },
"ContentID": {
"description": "Content ID",
"type": "string"
diff --git a/server/webhook/webhook.go b/server/webhook/webhook.go
index 0b60d76..7f58376 100644
--- a/server/webhook/webhook.go
+++ b/server/webhook/webhook.go
@@ -16,6 +16,10 @@ var (
// RateLimit is the minimum number of seconds between requests
RateLimit = 1
+ // Delay is the number of seconds to wait before sending each webhook request
+ // This can allow for other processing to complete before the webhook is triggered.
+ Delay = 0
+
rl rate.Sometimes
rateLimiterSet bool
@@ -38,6 +42,11 @@ func Send(msg any) {
}
go func() {
+ // Apply delay if configured
+ if Delay > 0 {
+ time.Sleep(time.Duration(Delay) * time.Second)
+ }
+
rl.Do(func() {
b, err := json.Marshal(msg)
if err != nil {
diff --git a/server/websockets/client.go b/server/websockets/client.go
index 7902dae..4d44110 100644
--- a/server/websockets/client.go
+++ b/server/websockets/client.go
@@ -35,6 +35,10 @@ var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
EnableCompression: true,
+ CheckOrigin: func(_ *http.Request) bool {
+ // origin is checked via server.go's CORS settings
+ return true
+ },
}
// Client is a middleman between the websocket connection and the hub.