mirror of
https://github.com/axllent/mailpit.git
synced 2026-03-03 23:27:01 +00:00
Compare commits
177 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
53bbf4c7dc | ||
|
|
0015300920 | ||
|
|
fa6a5d729f | ||
|
|
cc9fba7adf | ||
|
|
93665656cf | ||
|
|
d918fdb137 | ||
|
|
fd1346c5f4 | ||
|
|
388bea740b | ||
|
|
583df9ee1f | ||
|
|
8f05b97947 | ||
|
|
8bdd0cc635 | ||
|
|
a372e8150e | ||
|
|
2bc2660ad5 | ||
|
|
5d6aa7c48a | ||
|
|
997e041042 | ||
|
|
5c362c1430 | ||
|
|
9219b2d411 | ||
|
|
86abc7ea68 | ||
|
|
867dbf41d5 | ||
|
|
51e458ad57 | ||
|
|
d29a7d6218 | ||
|
|
f6a8de3215 | ||
|
|
4e2e59ec87 | ||
|
|
6aeebb9824 | ||
|
|
a426f64795 | ||
|
|
b228c9477e | ||
|
|
d70f2fd196 | ||
|
|
0da89d91dd | ||
|
|
edab9e1b6b | ||
|
|
66aead387e | ||
|
|
efe1ac732e | ||
|
|
33dcd489eb | ||
|
|
6b2e5b2e41 | ||
|
|
812c9b99d1 | ||
|
|
8202c94a43 | ||
|
|
c1d4a73440 | ||
|
|
8e100ff21b | ||
|
|
088b772de5 | ||
|
|
faf8bd4a08 | ||
|
|
0e83a5a985 | ||
|
|
3ee91eb6c8 | ||
|
|
5cd0a6e2f3 | ||
|
|
fea733a43e | ||
|
|
d4e520772e | ||
|
|
e4a7212f89 | ||
|
|
e6a5fceedd | ||
|
|
bf4d5fbc6b | ||
|
|
93c3dec66e | ||
|
|
98026e0685 | ||
|
|
ecd3a97853 | ||
|
|
695270e515 | ||
|
|
43403bc6f7 | ||
|
|
6dbdbf1637 | ||
|
|
3c81e152e6 | ||
|
|
9501b460c5 | ||
|
|
6233cb1e07 | ||
|
|
f64f377199 | ||
|
|
f872424526 | ||
|
|
5d530edfab | ||
|
|
12c54f4bb3 | ||
|
|
23e47c567a | ||
|
|
b6940eccff | ||
|
|
eb796924b1 | ||
|
|
54ba59872e | ||
|
|
eff483c1c4 | ||
|
|
9f5d329105 | ||
|
|
77e6b88c5d | ||
|
|
5a9fd0686e | ||
|
|
3054dfe79e | ||
|
|
40cb76810e | ||
|
|
8b6b6640d5 | ||
|
|
a8945bd303 | ||
|
|
53e199b20f | ||
|
|
a6693481fa | ||
|
|
1aa58eeaaf | ||
|
|
133b36c34c | ||
|
|
ed28a4cc0d | ||
|
|
bc30b012cf | ||
|
|
2ae51c3f64 | ||
|
|
b6a87b9410 | ||
|
|
1f7dd0287a | ||
|
|
f33cbce63f | ||
|
|
79b6892320 | ||
|
|
799987ecb1 | ||
|
|
2d57839b3e | ||
|
|
86cc237c78 | ||
|
|
cc15ada304 | ||
|
|
49bc62f0aa | ||
|
|
444b65d371 | ||
|
|
15859f7be9 | ||
|
|
486388a798 | ||
|
|
9ab28d606a | ||
|
|
18b5ce8c18 | ||
|
|
93d5289d25 | ||
|
|
97bf9c257c | ||
|
|
18b0f5b790 | ||
|
|
94feb2ccaa | ||
|
|
aba3c46eb1 | ||
|
|
c9c910ab7c | ||
|
|
29c7295d16 | ||
|
|
61e15e4155 | ||
|
|
e03618570d | ||
|
|
d4cf95363f | ||
|
|
f260495495 | ||
|
|
d9f1f88107 | ||
|
|
09b704bcd7 | ||
|
|
a14cdce07f | ||
|
|
9fc5318e86 | ||
|
|
8affa0f375 | ||
|
|
cf8994ceaf | ||
|
|
39132723db | ||
|
|
642487742c | ||
|
|
544f0175d9 | ||
|
|
788e390e01 | ||
|
|
f6ae6bbdbb | ||
|
|
1155443785 | ||
|
|
056bef7d5e | ||
|
|
37eec298d7 | ||
|
|
a77b532328 | ||
|
|
00d6463de1 | ||
|
|
a3b92711a9 | ||
|
|
ba8c4cd2aa | ||
|
|
ec5267f5a5 | ||
|
|
73d2b1ba93 | ||
|
|
56fdaa1224 | ||
|
|
25090aeb2a | ||
|
|
9bc8d005fb | ||
|
|
b57e340389 | ||
|
|
b9043b6c39 | ||
|
|
5860171002 | ||
|
|
ad49bf2898 | ||
|
|
2d221a6b67 | ||
|
|
4f266cd3f3 | ||
|
|
9fc7202552 | ||
|
|
22a476ded5 | ||
|
|
54d3f6e3ad | ||
|
|
cbe61e3f2e | ||
|
|
3b65a8852e | ||
|
|
970a534d77 | ||
|
|
f7502b1c14 | ||
|
|
e0f7d88d61 | ||
|
|
fc8148bfb3 | ||
|
|
74fe6d55b4 | ||
|
|
47376d4db9 | ||
|
|
4b9b60f247 | ||
|
|
123b0f19db | ||
|
|
9fed08245a | ||
|
|
f807c166f7 | ||
|
|
9d257dd3c0 | ||
|
|
f74bb70499 | ||
|
|
802f6f5672 | ||
|
|
19966fad81 | ||
|
|
48db1437b3 | ||
|
|
1df270bab3 | ||
|
|
6fe1bdb579 | ||
|
|
9a27f33079 | ||
|
|
e363ece5a0 | ||
|
|
86d73f9118 | ||
|
|
bd87dcabf6 | ||
|
|
8019d3e0e2 | ||
|
|
8866720631 | ||
|
|
8f474bc313 | ||
|
|
3103b50f08 | ||
|
|
8d308a6776 | ||
|
|
00d254d7c4 | ||
|
|
2944c2a32f | ||
|
|
41c7c2a93a | ||
|
|
154b234205 | ||
|
|
ad1037c02b | ||
|
|
4b707537b9 | ||
|
|
bca7bec867 | ||
|
|
d15b3eb05e | ||
|
|
72709acb90 | ||
|
|
83f289eb40 | ||
|
|
7fd73a6fdb | ||
|
|
3bbc122869 | ||
|
|
55fd56a4a3 |
47
.chglog/CHANGELOG.tpl.md
Executable file
47
.chglog/CHANGELOG.tpl.md
Executable file
@@ -0,0 +1,47 @@
|
||||
# Changelog
|
||||
|
||||
Notable changes to Mailpit will be documented in this file.
|
||||
|
||||
{{ if .Versions -}}
|
||||
{{ if .Unreleased.CommitGroups -}}
|
||||
## [Unreleased]
|
||||
|
||||
{{ if .Unreleased.CommitGroups -}}
|
||||
{{ range .Unreleased.CommitGroups -}}
|
||||
### {{ .Title }}
|
||||
{{ range .Commits -}}
|
||||
- {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end -}}
|
||||
{{ end -}}
|
||||
{{ end -}}
|
||||
|
||||
{{ range .Versions }}
|
||||
{{- if .CommitGroups -}}
|
||||
## {{ .Tag.Name }}
|
||||
|
||||
{{ if .NoteGroups -}}
|
||||
{{ range .NoteGroups -}}
|
||||
### {{ .Title }}
|
||||
{{ range .Notes }}
|
||||
{{ .Body }}
|
||||
{{ end -}}
|
||||
{{ end }}
|
||||
{{ end -}}
|
||||
{{ end -}}
|
||||
|
||||
{{ range .CommitGroups -}}
|
||||
### {{ .Title }}
|
||||
{{ range .Commits -}}
|
||||
- {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{- if .MergeCommits -}}
|
||||
### Pull Requests
|
||||
{{ range .MergeCommits -}}
|
||||
- {{ .Header }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end -}}
|
||||
12
.chglog/RELEASE.tpl.md
Executable file
12
.chglog/RELEASE.tpl.md
Executable file
@@ -0,0 +1,12 @@
|
||||
{{ if .Versions -}}
|
||||
{{ range .Versions }}
|
||||
{{- if .CommitGroups -}}
|
||||
{{ range .CommitGroups -}}
|
||||
### {{ .Title }}
|
||||
{{ range .Commits -}}
|
||||
- {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }}
|
||||
{{ end }}
|
||||
{{ end -}}
|
||||
{{ end -}}
|
||||
{{ end -}}
|
||||
{{ end -}}
|
||||
28
.chglog/config.yml
Executable file
28
.chglog/config.yml
Executable file
@@ -0,0 +1,28 @@
|
||||
style: github
|
||||
template: CHANGELOG.tpl.md
|
||||
info:
|
||||
title: CHANGELOG
|
||||
repository_url: https://github.com/axllent/mailpit
|
||||
options:
|
||||
commits:
|
||||
# filters:
|
||||
# Type:
|
||||
# - feat
|
||||
# - fix
|
||||
# - perf
|
||||
# - refactor
|
||||
commit_groups:
|
||||
title_maps:
|
||||
feature: Feature
|
||||
fix: Fix
|
||||
# perf: Performance Improvements
|
||||
# refactor: Code Refactoring
|
||||
header:
|
||||
pattern: "^(\\w*)(?:\\(([\\w\\$\\.\\-\\*\\s]*)\\))?\\:\\s(.*)$"
|
||||
pattern_maps:
|
||||
- Type
|
||||
- Scope
|
||||
- Subject
|
||||
notes:
|
||||
keywords:
|
||||
- BREAKING CHANGE
|
||||
2
.dockerignore
Normal file
2
.dockerignore
Normal file
@@ -0,0 +1,2 @@
|
||||
/node_modules
|
||||
/mailpit
|
||||
31
.git-rewrite/backup-refs
Normal file
31
.git-rewrite/backup-refs
Normal file
@@ -0,0 +1,31 @@
|
||||
7c73d1883ba2ee2a9fe39d7c1d90e19d9606b0a1 commit refs/heads/develop
|
||||
d6fb872c795a8d57f6813da55d6cf3b453a96cc7 commit refs/heads/feature/version
|
||||
89e6fb4c42d70fa5a44075a2023cc6b520c17d8c commit refs/heads/master
|
||||
7c73d1883ba2ee2a9fe39d7c1d90e19d9606b0a1 commit refs/remotes/origin/develop
|
||||
89e6fb4c42d70fa5a44075a2023cc6b520c17d8c commit refs/remotes/origin/master
|
||||
438504fa46b2aa6847096d45be682c92bcfa00a2 commit refs/stash
|
||||
f0c66b466e49942d7ff47a4a788ba41d6028ae8b tag refs/tags/0.0.1-beta
|
||||
6c2be2ac88682884897fc440805c03679c2ced84 tag refs/tags/0.0.2
|
||||
1f39040d1d6e5c7804a090ae5cd09f2e0fdaca09 tag refs/tags/0.0.3
|
||||
3b781c77672a03600b170426298c0b30c0433542 tag refs/tags/0.0.4
|
||||
82f78fbf5a75d5dd3dae03e7c7ecedce99fc4642 tag refs/tags/0.0.5
|
||||
df9b109f382ff148c4254a4c98b0511c09f6687b tag refs/tags/0.0.6
|
||||
52859e22f355fafb6e1babf3d83e6e84e2aa380d tag refs/tags/0.0.7
|
||||
5e3e91a22c3557b3af1f787bd5b8a1ee462f5604 tag refs/tags/0.0.8
|
||||
0eb4066a4ce7839b81b6333be0e9660c8811b913 tag refs/tags/0.0.9
|
||||
13d94ea2541ad13d2f12825c604467b11f3e4a8a tag refs/tags/0.1.0
|
||||
e591d5c093ee3ba06a0c1ee891f961e287faeeb3 tag refs/tags/0.1.1
|
||||
4f14cdea00bf0fd4299e557cf3e895b548a0f982 tag refs/tags/0.1.2
|
||||
2be1a43f3990adb865c9e1a713c168cba9d01af7 tag refs/tags/0.1.3
|
||||
625b72b1eead52df071716d95d49aed28ae59d64 tag refs/tags/0.1.4
|
||||
39e6757bea81d8c8b52d3765cb198c4d70a200bb tag refs/tags/0.1.5
|
||||
ddf198cb39f4ed254558819ece53af6ec2f0d568 tag refs/tags/1.0.0
|
||||
60079cf37f9e53c93853bfe99094fde764feec5c tag refs/tags/1.0.0-beta1
|
||||
15371a9cd3671238811c7467f86decc3c00b7119 tag refs/tags/1.1.0
|
||||
88361be8a0fe56b147b44ea5fa5e754d2461e530 tag refs/tags/1.1.1
|
||||
9c9bc84baa24de70658bf402ebdae32c861dd3b1 tag refs/tags/1.1.2
|
||||
ee82b104198a75f3d837b7ab678c2a427e399a47 tag refs/tags/1.1.3
|
||||
28cde017eb7f904b77c00265cde6f3fe319af983 tag refs/tags/1.1.4
|
||||
6a008ae9c9fd101721b399e75f562dc9240f4372 tag refs/tags/1.1.5
|
||||
f3811c3be7c7aad71b03ece17fe4412890bc0ea1 tag refs/tags/1.1.6
|
||||
4da0e28ac47593b415c3e35aa13ff9cfb2be904d tag refs/tags/1.1.7
|
||||
6
.git-rewrite/commit
Normal file
6
.git-rewrite/commit
Normal file
@@ -0,0 +1,6 @@
|
||||
tree 1f6b4426212043bf6b473fcbac898e9b871c84f6
|
||||
parent 802f6f5672200fff036a72abd84f39e78483d61f
|
||||
author Ralph Slooten <axllent@gmail.com> 1659602885 +1200
|
||||
committer Ralph Slooten <axllent@gmail.com> 1659602885 +1200
|
||||
|
||||
Release 0.0.6
|
||||
1
.git-rewrite/heads
Normal file
1
.git-rewrite/heads
Normal file
@@ -0,0 +1 @@
|
||||
refs/heads/develop
|
||||
@@ -0,0 +1 @@
|
||||
00d254d7c41be358cc0a99d388e796703c3ee5b9
|
||||
@@ -0,0 +1 @@
|
||||
07141d3a213a443b830fb05dae87f181a6b213cc
|
||||
@@ -0,0 +1 @@
|
||||
0799a6d67cfd65d0228014478632f85780ed5517
|
||||
@@ -0,0 +1 @@
|
||||
0fa58dffbd9d30f4c0aa9c93c96292fa89857bc5
|
||||
@@ -0,0 +1 @@
|
||||
154b2342056d3adb1be716e16f8919319d3c6dba
|
||||
@@ -0,0 +1 @@
|
||||
19966fad81e043ef9e2a2d062411e3990dad449d
|
||||
@@ -0,0 +1 @@
|
||||
1df270bab3caf8c2e36c5d6aaf84efdfb5da0db0
|
||||
@@ -0,0 +1 @@
|
||||
2944c2a32f6e1e3cbb7eb9603188b339f6d74700
|
||||
@@ -0,0 +1 @@
|
||||
2e5752f69385f8d3f75ab1446e46d3f2208a543f
|
||||
@@ -0,0 +1 @@
|
||||
3103b50f0857d18c48ec60ee622fabf4938c938f
|
||||
@@ -0,0 +1 @@
|
||||
324de3d99f22bbce918e0246daa6caf90a0fc734
|
||||
@@ -0,0 +1 @@
|
||||
335b0f3876e66da7bb06e6c39afb016068da1ac9
|
||||
@@ -0,0 +1 @@
|
||||
38da162cd90445a64e6586f72fed0a3c5e7dfce2
|
||||
@@ -0,0 +1 @@
|
||||
3bbc12286966b69455025aa4de3e88ecda439811
|
||||
@@ -0,0 +1 @@
|
||||
41c7c2a93af86acb48e5dd8b3d9406820c547b13
|
||||
@@ -0,0 +1 @@
|
||||
48db1437b3a9ea21bc987513c01bc4800b06f0f9
|
||||
@@ -0,0 +1 @@
|
||||
493e8a09aa10d8fe48896c429ea0c1260b2c0709
|
||||
@@ -0,0 +1 @@
|
||||
4b707537b9334641c37fb1d1aab96fe708e507ec
|
||||
@@ -0,0 +1 @@
|
||||
4e4fc22cb53abe2979d751e63671c771718147e5
|
||||
@@ -0,0 +1 @@
|
||||
50c7955e6d3b39b187c7aaa7da1cf901be6ac6b2
|
||||
@@ -0,0 +1 @@
|
||||
55fd56a4a3e40acbfa4720749018d13873d8af62
|
||||
@@ -0,0 +1 @@
|
||||
56449dd30e9f26312ddb2117faf319a514f73f1f
|
||||
@@ -0,0 +1 @@
|
||||
6d426c2a0bcd5c191c0e95163cf15b69a207fdd5
|
||||
@@ -0,0 +1 @@
|
||||
6fe1bdb579f0924b10b28f1fc9587ac49fc44f24
|
||||
@@ -0,0 +1 @@
|
||||
72709acb903556967dd5d1cead1f5b052151bb25
|
||||
@@ -0,0 +1 @@
|
||||
76c6c881d54b82d5ca4bc427c2968fbc15c7a090
|
||||
@@ -0,0 +1 @@
|
||||
7a9b11a9e5e63b987fde0e7d8536f9fa76585097
|
||||
@@ -0,0 +1 @@
|
||||
7cbe3a04ef7b95789287cf8fa7d654e18df11084
|
||||
@@ -0,0 +1 @@
|
||||
7fd73a6fdb49843d29aefa2bb8bb5de900e53811
|
||||
@@ -0,0 +1 @@
|
||||
8019d3e0e2a8a01f729911730128de72a2a1b6ba
|
||||
@@ -0,0 +1 @@
|
||||
802f6f5672200fff036a72abd84f39e78483d61f
|
||||
@@ -0,0 +1 @@
|
||||
8159694b935775e4072e1902124a8cb4bb6efacd
|
||||
@@ -0,0 +1 @@
|
||||
83f289eb40b34c07ad6b5df1099a7b6427e9be40
|
||||
@@ -0,0 +1 @@
|
||||
86d73f9118eee40fd842fc25898490a2923eaecd
|
||||
@@ -0,0 +1 @@
|
||||
8866720631a717b8df2df6fc206eb6db44f8a005
|
||||
@@ -0,0 +1 @@
|
||||
8d308a6776436ce1189e45268e4eceb8b5a1011a
|
||||
@@ -0,0 +1 @@
|
||||
8d6d48c59e9ca78d00a12c715494de99eb40a5ad
|
||||
@@ -0,0 +1 @@
|
||||
8e819aca56db7ab982243eddda0ab89525db1b23
|
||||
@@ -0,0 +1 @@
|
||||
8f474bc31303882270112cdfafeaedc908938707
|
||||
@@ -0,0 +1 @@
|
||||
927638f2d500dbfd29371b2b4a3f01b987749899
|
||||
@@ -0,0 +1 @@
|
||||
9a27f330791c08ee87dbb886c6d511bb6d3bffa0
|
||||
@@ -0,0 +1 @@
|
||||
a3b34ec32effa03acfa4aa99cbe38aee81f01e56
|
||||
@@ -0,0 +1 @@
|
||||
a56c9616adbf6f5c387a2ffc472cd42b6b9ba377
|
||||
@@ -0,0 +1 @@
|
||||
a810bdae244404a91d17c6293f3096b055dd74cf
|
||||
@@ -0,0 +1 @@
|
||||
a85a74bb9a03dafeaaaf3e2211cb796f195773e3
|
||||
@@ -0,0 +1 @@
|
||||
ad1037c02b654b7e582c2c644841ea7477fcf9b4
|
||||
@@ -0,0 +1 @@
|
||||
b7dbda0db65e24c8ad2549639cd22388db837e1f
|
||||
@@ -0,0 +1 @@
|
||||
bca7bec867cd6aff1b1692ed5508c18e62aa430b
|
||||
@@ -0,0 +1 @@
|
||||
bd87dcabf6b370316eac39b863ee5d84ad5851e2
|
||||
@@ -0,0 +1 @@
|
||||
c6f1c8213b0bb6489072bc379ae8b158e6a552d9
|
||||
@@ -0,0 +1 @@
|
||||
ce23e0616e4a29a266bbfc9c398c57c2976dc71a
|
||||
@@ -0,0 +1 @@
|
||||
cf12b3968b2d2a59fb1fa12ef83065b1d7090681
|
||||
@@ -0,0 +1 @@
|
||||
cf5c4297c9b975dff541a7384da6838dd7fab930
|
||||
@@ -0,0 +1 @@
|
||||
d15b3eb05e53326a81eab3f6839186e0840cb686
|
||||
@@ -0,0 +1 @@
|
||||
d4f17fb8ad988be6cbd467ff2d79a5ac894c5e89
|
||||
@@ -0,0 +1 @@
|
||||
e1dc184d7b25a64cc9b599b38cafbc3e7bfccb76
|
||||
@@ -0,0 +1 @@
|
||||
e363ece5a05eeb55264176e689ffdbf4e79b718f
|
||||
@@ -0,0 +1 @@
|
||||
eb7049163bbfa1bc73af5859dff1803901cf43b3
|
||||
@@ -0,0 +1 @@
|
||||
f6da1c6da4beacaa410beea8dc1fe45d21b3b8d2
|
||||
@@ -0,0 +1 @@
|
||||
f966b6a756f736f3f106a186faac183ce0eb0686
|
||||
1
.git-rewrite/message
Normal file
1
.git-rewrite/message
Normal file
@@ -0,0 +1 @@
|
||||
Release 0.0.6
|
||||
0
.git-rewrite/parse
Normal file
0
.git-rewrite/parse
Normal file
1
.git-rewrite/raw-refs
Normal file
1
.git-rewrite/raw-refs
Normal file
@@ -0,0 +1 @@
|
||||
refs/heads/develop
|
||||
212
.git-rewrite/revs
Normal file
212
.git-rewrite/revs
Normal file
@@ -0,0 +1,212 @@
|
||||
7a9b11a9e5e63b987fde0e7d8536f9fa76585097
|
||||
6d426c2a0bcd5c191c0e95163cf15b69a207fdd5 7a9b11a9e5e63b987fde0e7d8536f9fa76585097
|
||||
f6da1c6da4beacaa410beea8dc1fe45d21b3b8d2 6d426c2a0bcd5c191c0e95163cf15b69a207fdd5
|
||||
76c6c881d54b82d5ca4bc427c2968fbc15c7a090 7a9b11a9e5e63b987fde0e7d8536f9fa76585097 f6da1c6da4beacaa410beea8dc1fe45d21b3b8d2
|
||||
8159694b935775e4072e1902124a8cb4bb6efacd f6da1c6da4beacaa410beea8dc1fe45d21b3b8d2 76c6c881d54b82d5ca4bc427c2968fbc15c7a090
|
||||
07141d3a213a443b830fb05dae87f181a6b213cc 8159694b935775e4072e1902124a8cb4bb6efacd
|
||||
cf5c4297c9b975dff541a7384da6838dd7fab930 76c6c881d54b82d5ca4bc427c2968fbc15c7a090 07141d3a213a443b830fb05dae87f181a6b213cc
|
||||
eb7049163bbfa1bc73af5859dff1803901cf43b3 07141d3a213a443b830fb05dae87f181a6b213cc cf5c4297c9b975dff541a7384da6838dd7fab930
|
||||
927638f2d500dbfd29371b2b4a3f01b987749899 eb7049163bbfa1bc73af5859dff1803901cf43b3
|
||||
50c7955e6d3b39b187c7aaa7da1cf901be6ac6b2 cf5c4297c9b975dff541a7384da6838dd7fab930 927638f2d500dbfd29371b2b4a3f01b987749899
|
||||
e1dc184d7b25a64cc9b599b38cafbc3e7bfccb76 927638f2d500dbfd29371b2b4a3f01b987749899 50c7955e6d3b39b187c7aaa7da1cf901be6ac6b2
|
||||
a56c9616adbf6f5c387a2ffc472cd42b6b9ba377 e1dc184d7b25a64cc9b599b38cafbc3e7bfccb76
|
||||
a3b34ec32effa03acfa4aa99cbe38aee81f01e56 50c7955e6d3b39b187c7aaa7da1cf901be6ac6b2 a56c9616adbf6f5c387a2ffc472cd42b6b9ba377
|
||||
d4f17fb8ad988be6cbd467ff2d79a5ac894c5e89 a56c9616adbf6f5c387a2ffc472cd42b6b9ba377 a3b34ec32effa03acfa4aa99cbe38aee81f01e56
|
||||
b7dbda0db65e24c8ad2549639cd22388db837e1f d4f17fb8ad988be6cbd467ff2d79a5ac894c5e89
|
||||
0799a6d67cfd65d0228014478632f85780ed5517 b7dbda0db65e24c8ad2549639cd22388db837e1f
|
||||
493e8a09aa10d8fe48896c429ea0c1260b2c0709 a3b34ec32effa03acfa4aa99cbe38aee81f01e56 0799a6d67cfd65d0228014478632f85780ed5517
|
||||
0fa58dffbd9d30f4c0aa9c93c96292fa89857bc5 0799a6d67cfd65d0228014478632f85780ed5517 493e8a09aa10d8fe48896c429ea0c1260b2c0709
|
||||
cf12b3968b2d2a59fb1fa12ef83065b1d7090681 0fa58dffbd9d30f4c0aa9c93c96292fa89857bc5
|
||||
2e5752f69385f8d3f75ab1446e46d3f2208a543f cf12b3968b2d2a59fb1fa12ef83065b1d7090681
|
||||
4e4fc22cb53abe2979d751e63671c771718147e5 2e5752f69385f8d3f75ab1446e46d3f2208a543f
|
||||
8e819aca56db7ab982243eddda0ab89525db1b23 4e4fc22cb53abe2979d751e63671c771718147e5
|
||||
8d6d48c59e9ca78d00a12c715494de99eb40a5ad 8e819aca56db7ab982243eddda0ab89525db1b23
|
||||
324de3d99f22bbce918e0246daa6caf90a0fc734 8d6d48c59e9ca78d00a12c715494de99eb40a5ad
|
||||
335b0f3876e66da7bb06e6c39afb016068da1ac9 324de3d99f22bbce918e0246daa6caf90a0fc734
|
||||
a85a74bb9a03dafeaaaf3e2211cb796f195773e3 335b0f3876e66da7bb06e6c39afb016068da1ac9
|
||||
ce23e0616e4a29a266bbfc9c398c57c2976dc71a 8d6d48c59e9ca78d00a12c715494de99eb40a5ad a85a74bb9a03dafeaaaf3e2211cb796f195773e3
|
||||
7cbe3a04ef7b95789287cf8fa7d654e18df11084 ce23e0616e4a29a266bbfc9c398c57c2976dc71a
|
||||
f966b6a756f736f3f106a186faac183ce0eb0686 493e8a09aa10d8fe48896c429ea0c1260b2c0709 7cbe3a04ef7b95789287cf8fa7d654e18df11084
|
||||
a810bdae244404a91d17c6293f3096b055dd74cf 7cbe3a04ef7b95789287cf8fa7d654e18df11084 f966b6a756f736f3f106a186faac183ce0eb0686
|
||||
56449dd30e9f26312ddb2117faf319a514f73f1f a810bdae244404a91d17c6293f3096b055dd74cf
|
||||
38da162cd90445a64e6586f72fed0a3c5e7dfce2 56449dd30e9f26312ddb2117faf319a514f73f1f
|
||||
c6f1c8213b0bb6489072bc379ae8b158e6a552d9 f966b6a756f736f3f106a186faac183ce0eb0686 38da162cd90445a64e6586f72fed0a3c5e7dfce2
|
||||
55fd56a4a3e40acbfa4720749018d13873d8af62 38da162cd90445a64e6586f72fed0a3c5e7dfce2 c6f1c8213b0bb6489072bc379ae8b158e6a552d9
|
||||
3bbc12286966b69455025aa4de3e88ecda439811 55fd56a4a3e40acbfa4720749018d13873d8af62
|
||||
7fd73a6fdb49843d29aefa2bb8bb5de900e53811 3bbc12286966b69455025aa4de3e88ecda439811
|
||||
83f289eb40b34c07ad6b5df1099a7b6427e9be40 7fd73a6fdb49843d29aefa2bb8bb5de900e53811
|
||||
72709acb903556967dd5d1cead1f5b052151bb25 83f289eb40b34c07ad6b5df1099a7b6427e9be40
|
||||
d15b3eb05e53326a81eab3f6839186e0840cb686 72709acb903556967dd5d1cead1f5b052151bb25
|
||||
bca7bec867cd6aff1b1692ed5508c18e62aa430b d15b3eb05e53326a81eab3f6839186e0840cb686
|
||||
4b707537b9334641c37fb1d1aab96fe708e507ec 7cbe3a04ef7b95789287cf8fa7d654e18df11084
|
||||
ad1037c02b654b7e582c2c644841ea7477fcf9b4 4b707537b9334641c37fb1d1aab96fe708e507ec
|
||||
154b2342056d3adb1be716e16f8919319d3c6dba ad1037c02b654b7e582c2c644841ea7477fcf9b4
|
||||
41c7c2a93af86acb48e5dd8b3d9406820c547b13 154b2342056d3adb1be716e16f8919319d3c6dba
|
||||
2944c2a32f6e1e3cbb7eb9603188b339f6d74700 41c7c2a93af86acb48e5dd8b3d9406820c547b13
|
||||
00d254d7c41be358cc0a99d388e796703c3ee5b9 2944c2a32f6e1e3cbb7eb9603188b339f6d74700
|
||||
8d308a6776436ce1189e45268e4eceb8b5a1011a 00d254d7c41be358cc0a99d388e796703c3ee5b9
|
||||
3103b50f0857d18c48ec60ee622fabf4938c938f 8d308a6776436ce1189e45268e4eceb8b5a1011a
|
||||
8f474bc31303882270112cdfafeaedc908938707 3103b50f0857d18c48ec60ee622fabf4938c938f
|
||||
8866720631a717b8df2df6fc206eb6db44f8a005 bca7bec867cd6aff1b1692ed5508c18e62aa430b 8f474bc31303882270112cdfafeaedc908938707
|
||||
8019d3e0e2a8a01f729911730128de72a2a1b6ba 8866720631a717b8df2df6fc206eb6db44f8a005
|
||||
bd87dcabf6b370316eac39b863ee5d84ad5851e2 8019d3e0e2a8a01f729911730128de72a2a1b6ba
|
||||
86d73f9118eee40fd842fc25898490a2923eaecd bd87dcabf6b370316eac39b863ee5d84ad5851e2
|
||||
e363ece5a05eeb55264176e689ffdbf4e79b718f c6f1c8213b0bb6489072bc379ae8b158e6a552d9 86d73f9118eee40fd842fc25898490a2923eaecd
|
||||
9a27f330791c08ee87dbb886c6d511bb6d3bffa0 bd87dcabf6b370316eac39b863ee5d84ad5851e2 e363ece5a05eeb55264176e689ffdbf4e79b718f
|
||||
6fe1bdb579f0924b10b28f1fc9587ac49fc44f24 9a27f330791c08ee87dbb886c6d511bb6d3bffa0
|
||||
1df270bab3caf8c2e36c5d6aaf84efdfb5da0db0 6fe1bdb579f0924b10b28f1fc9587ac49fc44f24
|
||||
48db1437b3a9ea21bc987513c01bc4800b06f0f9 e363ece5a05eeb55264176e689ffdbf4e79b718f 1df270bab3caf8c2e36c5d6aaf84efdfb5da0db0
|
||||
19966fad81e043ef9e2a2d062411e3990dad449d 6fe1bdb579f0924b10b28f1fc9587ac49fc44f24 48db1437b3a9ea21bc987513c01bc4800b06f0f9
|
||||
802f6f5672200fff036a72abd84f39e78483d61f 19966fad81e043ef9e2a2d062411e3990dad449d
|
||||
f74bb70499424355b8ca1a0e1338131b0ecabd34 802f6f5672200fff036a72abd84f39e78483d61f
|
||||
9d257dd3c03a3d07323d5f74f6d94bc7f02f4de7 48db1437b3a9ea21bc987513c01bc4800b06f0f9 f74bb70499424355b8ca1a0e1338131b0ecabd34
|
||||
f807c166f7265440c9a190d05f67904d99812f65 802f6f5672200fff036a72abd84f39e78483d61f 9d257dd3c03a3d07323d5f74f6d94bc7f02f4de7
|
||||
9fed08245a87d631d1ee4f9d75a60dc1d8940a3e f807c166f7265440c9a190d05f67904d99812f65
|
||||
123b0f19dbcd0542a73df6f487372ace03bfc5ff 19966fad81e043ef9e2a2d062411e3990dad449d
|
||||
4b9b60f2476275e3cf4dab64d61d8512318c4605 9fed08245a87d631d1ee4f9d75a60dc1d8940a3e 123b0f19dbcd0542a73df6f487372ace03bfc5ff
|
||||
47376d4db98f6b303e05240bbd154085442cb913 4b9b60f2476275e3cf4dab64d61d8512318c4605
|
||||
74fe6d55b4ed096bb3b3a3a497389312e6f3adcc 47376d4db98f6b303e05240bbd154085442cb913
|
||||
fc8148bfb33835495499227d9b808d0572c0b7ee 9d257dd3c03a3d07323d5f74f6d94bc7f02f4de7 74fe6d55b4ed096bb3b3a3a497389312e6f3adcc
|
||||
e0f7d88d6138e849aee9aa454b32206c0f147ae3 47376d4db98f6b303e05240bbd154085442cb913 fc8148bfb33835495499227d9b808d0572c0b7ee
|
||||
f7502b1c143672d92b32f24974fc95f2ae4111b9 e0f7d88d6138e849aee9aa454b32206c0f147ae3
|
||||
970a534d77b0ef4c9ea3ff201f5e6281b1bacef2 f7502b1c143672d92b32f24974fc95f2ae4111b9
|
||||
3b65a8852e2dfd64130e1bf4b4e625d55813c62c 970a534d77b0ef4c9ea3ff201f5e6281b1bacef2
|
||||
cbe61e3f2e68d4c3b607ff8ad2cfb8dabbb11a3a 3b65a8852e2dfd64130e1bf4b4e625d55813c62c
|
||||
54d3f6e3adee9d4c4cb8f668d2cd1dfa5e28029e cbe61e3f2e68d4c3b607ff8ad2cfb8dabbb11a3a
|
||||
22a476ded520ce7052e3960e546ac1136fada0e8 54d3f6e3adee9d4c4cb8f668d2cd1dfa5e28029e
|
||||
9fc7202552ced5424f01fb2815e47d8ae98131d2 fc8148bfb33835495499227d9b808d0572c0b7ee 22a476ded520ce7052e3960e546ac1136fada0e8
|
||||
4f266cd3f3d9a0592928496f37d7c4793ab0ecd1 54d3f6e3adee9d4c4cb8f668d2cd1dfa5e28029e 9fc7202552ced5424f01fb2815e47d8ae98131d2
|
||||
2d221a6b67c39c0f96f2f864e9edc5f91fed3616 4f266cd3f3d9a0592928496f37d7c4793ab0ecd1
|
||||
ad49bf28982b20db3d541313180ef22337574d57 2d221a6b67c39c0f96f2f864e9edc5f91fed3616
|
||||
58601710028dd8d17eb837df915f1b38826b1fca ad49bf28982b20db3d541313180ef22337574d57
|
||||
b9043b6c39124ffc60859515f594bd3c23d5f726 58601710028dd8d17eb837df915f1b38826b1fca
|
||||
b57e340389c1ab3ec99cc1bd04e3f0fba0b51351 9fc7202552ced5424f01fb2815e47d8ae98131d2 b9043b6c39124ffc60859515f594bd3c23d5f726
|
||||
9bc8d005fb6693dc7349ac2f9fee6594c6c662d5 58601710028dd8d17eb837df915f1b38826b1fca b57e340389c1ab3ec99cc1bd04e3f0fba0b51351
|
||||
25090aeb2a86baecc692a916120e6e169b97bcc5 9bc8d005fb6693dc7349ac2f9fee6594c6c662d5
|
||||
56fdaa1224fbdb768758889fd8e18fb3bfada309 25090aeb2a86baecc692a916120e6e169b97bcc5
|
||||
73d2b1ba930a48b379132e081ee2baed319b26f5 56fdaa1224fbdb768758889fd8e18fb3bfada309
|
||||
ec5267f5a5e2e3a06da379aa49fa8232a1afe732 b57e340389c1ab3ec99cc1bd04e3f0fba0b51351 73d2b1ba930a48b379132e081ee2baed319b26f5
|
||||
ba8c4cd2aa58c2add42ed1e007506a48c765768a 56fdaa1224fbdb768758889fd8e18fb3bfada309 ec5267f5a5e2e3a06da379aa49fa8232a1afe732
|
||||
a3b92711a971d123fd2f2053ca558dd62d20a083 ec5267f5a5e2e3a06da379aa49fa8232a1afe732
|
||||
00d6463de1cb5217e11dbb52c9a78b871f3020cd ec5267f5a5e2e3a06da379aa49fa8232a1afe732 a3b92711a971d123fd2f2053ca558dd62d20a083
|
||||
a77b5323289ef0e72fdbc1660c32d302c963d665 ba8c4cd2aa58c2add42ed1e007506a48c765768a 00d6463de1cb5217e11dbb52c9a78b871f3020cd
|
||||
37eec298d7c990b34dfe630f905c0f2d6144b275 a77b5323289ef0e72fdbc1660c32d302c963d665
|
||||
056bef7d5eb75d51b812f50b02df22fab39a8df8 37eec298d7c990b34dfe630f905c0f2d6144b275
|
||||
11554437854646c5ee26d3056c2b9e2d3f89de33 056bef7d5eb75d51b812f50b02df22fab39a8df8
|
||||
f6ae6bbdbbd3ff69e431b690c4da1bc31a46396d 37eec298d7c990b34dfe630f905c0f2d6144b275 11554437854646c5ee26d3056c2b9e2d3f89de33
|
||||
788e390e01efdda0513b617eceae64f424c97f11 f6ae6bbdbbd3ff69e431b690c4da1bc31a46396d
|
||||
544f0175d9968151df8fcd537f6399ea3fcb05fa 788e390e01efdda0513b617eceae64f424c97f11
|
||||
642487742c8ba27f1a24c68098bfaa81cff0677a 544f0175d9968151df8fcd537f6399ea3fcb05fa
|
||||
39132723dbc0bf3da3e5b022ad06866deafc44c7 642487742c8ba27f1a24c68098bfaa81cff0677a
|
||||
cf8994ceaf370677336e1d8c6d34f113f9e780e6 39132723dbc0bf3da3e5b022ad06866deafc44c7
|
||||
8affa0f3757cf92c1b5604499d6912101cf47cff 00d6463de1cb5217e11dbb52c9a78b871f3020cd cf8994ceaf370677336e1d8c6d34f113f9e780e6
|
||||
9fc5318e8644b6ada6fe127c97cfd2590ff97789 39132723dbc0bf3da3e5b022ad06866deafc44c7 8affa0f3757cf92c1b5604499d6912101cf47cff
|
||||
a14cdce07f9e39bd428a22f6c40b3849e0aadfc2 9fc5318e8644b6ada6fe127c97cfd2590ff97789
|
||||
09b704bcd73f4158b63218053c82f0af7dc3f14c 9fc5318e8644b6ada6fe127c97cfd2590ff97789
|
||||
d9f1f88107ee7b447a3088a3a6d76181f5fbb000 a14cdce07f9e39bd428a22f6c40b3849e0aadfc2 09b704bcd73f4158b63218053c82f0af7dc3f14c
|
||||
f260495495bf90c93dd0d958b19d925b57b62e3e d9f1f88107ee7b447a3088a3a6d76181f5fbb000
|
||||
d4cf95363f00b56453bd9ae58a20e4c9a39dd715 f260495495bf90c93dd0d958b19d925b57b62e3e
|
||||
e03618570d2da623a0a4e8823e4a8fe9a4bbca38 d4cf95363f00b56453bd9ae58a20e4c9a39dd715
|
||||
61e15e415519f6cae4cc94b3c3558d310b087f7b e03618570d2da623a0a4e8823e4a8fe9a4bbca38
|
||||
29c7295d16e99969ef39f8d338b88e143820745b d9f1f88107ee7b447a3088a3a6d76181f5fbb000 61e15e415519f6cae4cc94b3c3558d310b087f7b
|
||||
c9c910ab7cb5ad637ee7d01abf9e1a832ca088ff 29c7295d16e99969ef39f8d338b88e143820745b
|
||||
aba3c46eb1a985014ed56ca2d93853e97b36b8b4 c9c910ab7cb5ad637ee7d01abf9e1a832ca088ff
|
||||
94feb2ccaa55aacc21f2538b269ef514f92b3f81 aba3c46eb1a985014ed56ca2d93853e97b36b8b4
|
||||
18b0f5b790ef622350b7020bae71ff73b3e23166 94feb2ccaa55aacc21f2538b269ef514f92b3f81
|
||||
97bf9c257cbd52fbb0c519c93a141d894daf94df 8affa0f3757cf92c1b5604499d6912101cf47cff 18b0f5b790ef622350b7020bae71ff73b3e23166
|
||||
93d5289d25dd4785ecaf54ca19f921c0de03c60b 94feb2ccaa55aacc21f2538b269ef514f92b3f81 97bf9c257cbd52fbb0c519c93a141d894daf94df
|
||||
18b5ce8c1813c232467bc74e10b259799e068ccb 93d5289d25dd4785ecaf54ca19f921c0de03c60b
|
||||
9ab28d606af35cdace55be4921105516360db310 18b5ce8c1813c232467bc74e10b259799e068ccb
|
||||
486388a7988427a0140a6f89d1d14c4253202f06 9ab28d606af35cdace55be4921105516360db310
|
||||
15859f7be9f6fef815e6ad38f76634618e1dc6e0 486388a7988427a0140a6f89d1d14c4253202f06
|
||||
444b65d3713bc274c6ee27551d3ce0ef1f3837c8 15859f7be9f6fef815e6ad38f76634618e1dc6e0
|
||||
49bc62f0aa2872347d77e6503c5d412393bde03f 444b65d3713bc274c6ee27551d3ce0ef1f3837c8
|
||||
cc15ada3043b71986a2d48df8027b0e41ba7df5f 49bc62f0aa2872347d77e6503c5d412393bde03f
|
||||
86cc237c78778c27950906111dc797a87319ec01 cc15ada3043b71986a2d48df8027b0e41ba7df5f
|
||||
799987ecb19aa16cc31cbff5d7d96a99ef0fd3ee 86cc237c78778c27950906111dc797a87319ec01
|
||||
79b68923200dbe52bdf2b87ceffe41aec0323552 97bf9c257cbd52fbb0c519c93a141d894daf94df 799987ecb19aa16cc31cbff5d7d96a99ef0fd3ee
|
||||
f33cbce63fc3ca197477d8e42162feee0f1745a5 86cc237c78778c27950906111dc797a87319ec01 79b68923200dbe52bdf2b87ceffe41aec0323552
|
||||
2d57839b3ebf21e5bb92b9089c0241f8a774cc49 86cc237c78778c27950906111dc797a87319ec01
|
||||
1f7dd0287a4873810daace1c814cc464da6c60e2 f33cbce63fc3ca197477d8e42162feee0f1745a5 2d57839b3ebf21e5bb92b9089c0241f8a774cc49
|
||||
b6a87b9410f6a185f672147c283ea298f2b22c20 1f7dd0287a4873810daace1c814cc464da6c60e2
|
||||
2ae51c3f64e000c2d04b58e0b5c41f93be61c5b9 79b68923200dbe52bdf2b87ceffe41aec0323552 b6a87b9410f6a185f672147c283ea298f2b22c20
|
||||
bc30b012cf25ed3aa725191be5916f3f1e3026eb 1f7dd0287a4873810daace1c814cc464da6c60e2 2ae51c3f64e000c2d04b58e0b5c41f93be61c5b9
|
||||
ed28a4cc0d36d2e25096f1ed6863e2624b81dfff bc30b012cf25ed3aa725191be5916f3f1e3026eb
|
||||
133b36c34c4fe9ec3eb0b6bdb8d0f1f1a547d065 ed28a4cc0d36d2e25096f1ed6863e2624b81dfff
|
||||
1aa58eeaafd961fe987c8bd12be1b9207c4f6309 133b36c34c4fe9ec3eb0b6bdb8d0f1f1a547d065
|
||||
a6693481fa0fa92f2c1d7c768a2bb4c78e1024f9 1aa58eeaafd961fe987c8bd12be1b9207c4f6309
|
||||
53e199b20ff53845562a520da4bfaecf0189fad2 a6693481fa0fa92f2c1d7c768a2bb4c78e1024f9
|
||||
a8945bd3032c60ef3522b7aeb89f69b13d292931 53e199b20ff53845562a520da4bfaecf0189fad2
|
||||
8b6b6640d5c9066e34cc5bdaf9a89695afe37be0 2ae51c3f64e000c2d04b58e0b5c41f93be61c5b9 a8945bd3032c60ef3522b7aeb89f69b13d292931
|
||||
40cb76810e4daabd4d265e29226f20b93fd0e791 53e199b20ff53845562a520da4bfaecf0189fad2 8b6b6640d5c9066e34cc5bdaf9a89695afe37be0
|
||||
3054dfe79e2912a86c17ee61273728bad3a28a4b 40cb76810e4daabd4d265e29226f20b93fd0e791
|
||||
5a9fd0686eb6a1a89524449ab8711257c37b77dc 3054dfe79e2912a86c17ee61273728bad3a28a4b
|
||||
77e6b88c5dddfa18327a299cf39a61d1f7c2b0bb 5a9fd0686eb6a1a89524449ab8711257c37b77dc
|
||||
9f5d329105d30d0a48c47413fcf23dc8328ad526 77e6b88c5dddfa18327a299cf39a61d1f7c2b0bb
|
||||
eff483c1c47cda01da415f58b101f0348c61c200 9f5d329105d30d0a48c47413fcf23dc8328ad526
|
||||
54ba59872e27d4abd8e2ef6dd07e0805a4fce505 eff483c1c47cda01da415f58b101f0348c61c200
|
||||
eb796924b16fd84a073eab388c5ee23842d029f2 5a9fd0686eb6a1a89524449ab8711257c37b77dc 54ba59872e27d4abd8e2ef6dd07e0805a4fce505
|
||||
b6940eccffdafcfc213d6a7c4ba33ac29f7352ba 8b6b6640d5c9066e34cc5bdaf9a89695afe37be0 eb796924b16fd84a073eab388c5ee23842d029f2
|
||||
23e47c567a018f4f613f617aa2dc0711670b74a8 eb796924b16fd84a073eab388c5ee23842d029f2 b6940eccffdafcfc213d6a7c4ba33ac29f7352ba
|
||||
12c54f4bb3020f26177332c110e438abb0d46dae 23e47c567a018f4f613f617aa2dc0711670b74a8
|
||||
5d530edfabd1bce8d692ca5573464e2807bcd9f9 12c54f4bb3020f26177332c110e438abb0d46dae
|
||||
f87242452610692e4346ef632d4286ee6ee75311 5d530edfabd1bce8d692ca5573464e2807bcd9f9
|
||||
f64f3771997e87bf5aed8f1b348e4af39d7e2b7e 12c54f4bb3020f26177332c110e438abb0d46dae
|
||||
6233cb1e07633d75a9164bd247b7585c408a0a79 f64f3771997e87bf5aed8f1b348e4af39d7e2b7e
|
||||
9501b460c5c55c64a8af5714660ace9f2af88a88 6233cb1e07633d75a9164bd247b7585c408a0a79
|
||||
3c81e152e66b5ca7f63c71d5c9fab85a0e3a1247 9501b460c5c55c64a8af5714660ace9f2af88a88
|
||||
6dbdbf16379c0311c197bbac5c468f146e908fed 3c81e152e66b5ca7f63c71d5c9fab85a0e3a1247
|
||||
43403bc6f724c52cda644d91cbf1a8b581d691e1 6dbdbf16379c0311c197bbac5c468f146e908fed
|
||||
695270e515ea1ce8b2b4dd9758dfefe7f0a02770 f87242452610692e4346ef632d4286ee6ee75311 43403bc6f724c52cda644d91cbf1a8b581d691e1
|
||||
ecd3a97853b0f7a32648a35313c5ee2571452c71 695270e515ea1ce8b2b4dd9758dfefe7f0a02770
|
||||
98026e0685984a709dd9d9c659cb718b562df5ea b6940eccffdafcfc213d6a7c4ba33ac29f7352ba ecd3a97853b0f7a32648a35313c5ee2571452c71
|
||||
93c3dec66edb51a049c55f94ee432c5228cd24f3 695270e515ea1ce8b2b4dd9758dfefe7f0a02770 98026e0685984a709dd9d9c659cb718b562df5ea
|
||||
bf4d5fbc6b49f68afd9d49b3077f98da5c3336ff 93c3dec66edb51a049c55f94ee432c5228cd24f3
|
||||
e6a5fceeddefb8a750b38003a90c8ee1d58bdaec bf4d5fbc6b49f68afd9d49b3077f98da5c3336ff
|
||||
e4a7212f89cf6ffba37aa61162758fde6c8bdf15 e6a5fceeddefb8a750b38003a90c8ee1d58bdaec
|
||||
d4e520772e265544dbf8975d5307e43bf6e2f849 e4a7212f89cf6ffba37aa61162758fde6c8bdf15
|
||||
fea733a43ec0cdc9796cd86cdf19faac8f4be846 d4e520772e265544dbf8975d5307e43bf6e2f849
|
||||
5cd0a6e2f39828149dea886ee10f0c937b0e83de fea733a43ec0cdc9796cd86cdf19faac8f4be846
|
||||
3ee91eb6c84599bedf195bef5c01c2ff785d7e26 5cd0a6e2f39828149dea886ee10f0c937b0e83de
|
||||
0e83a5a98535b43d9ead28cfb0be472bf0ef3767 98026e0685984a709dd9d9c659cb718b562df5ea 3ee91eb6c84599bedf195bef5c01c2ff785d7e26
|
||||
faf8bd4a08db065bb85b0b642cc8114e8bf94725 5cd0a6e2f39828149dea886ee10f0c937b0e83de 0e83a5a98535b43d9ead28cfb0be472bf0ef3767
|
||||
088b772de59cc9638dd44ffe468d9e7650db62ff faf8bd4a08db065bb85b0b642cc8114e8bf94725
|
||||
8e100ff21bc0a90ac8033b3d2aecea79734ab605 088b772de59cc9638dd44ffe468d9e7650db62ff
|
||||
c1d4a73440a77f2993f4607ae4e849c3a31767d0 0e83a5a98535b43d9ead28cfb0be472bf0ef3767 8e100ff21bc0a90ac8033b3d2aecea79734ab605
|
||||
8202c94a438d9909d97219136f4d92b4e729938a 088b772de59cc9638dd44ffe468d9e7650db62ff c1d4a73440a77f2993f4607ae4e849c3a31767d0
|
||||
812c9b99d10013dd349146577519c5019185fdd3 8202c94a438d9909d97219136f4d92b4e729938a
|
||||
6b2e5b2e416abe260eeeea208454770d67ac5f9f 812c9b99d10013dd349146577519c5019185fdd3
|
||||
33dcd489ebe126c89f6fa92b6f55185111e17551 6b2e5b2e416abe260eeeea208454770d67ac5f9f
|
||||
efe1ac732eee4bb280a51bfcc3f809e27adcbf6c 33dcd489ebe126c89f6fa92b6f55185111e17551
|
||||
66aead387ebcbbbfa59d590dae96a5d255d43c25 c1d4a73440a77f2993f4607ae4e849c3a31767d0 efe1ac732eee4bb280a51bfcc3f809e27adcbf6c
|
||||
edab9e1b6b7089adf5447b76e2b082076d87c6b8 33dcd489ebe126c89f6fa92b6f55185111e17551 66aead387ebcbbbfa59d590dae96a5d255d43c25
|
||||
0da89d91dde7a7f77a4cf69e1dbd2ec3af619835 edab9e1b6b7089adf5447b76e2b082076d87c6b8
|
||||
d70f2fd196875282278d5455819dcfa238948f59 0da89d91dde7a7f77a4cf69e1dbd2ec3af619835
|
||||
b228c9477ee56f89a09cce3adf2340b28817150c 66aead387ebcbbbfa59d590dae96a5d255d43c25 d70f2fd196875282278d5455819dcfa238948f59
|
||||
a426f64795667bd144151c0ebec4ec57cfcb85f1 d70f2fd196875282278d5455819dcfa238948f59
|
||||
6aeebb9824b93261ba8270db34ebf8eac76b28b0 a426f64795667bd144151c0ebec4ec57cfcb85f1
|
||||
4e2e59ec87d6cad9ee9084b683323e094fbfa1ad 6aeebb9824b93261ba8270db34ebf8eac76b28b0
|
||||
f6a8de32150407469750a153ba07b3aaf0699a3c 4e2e59ec87d6cad9ee9084b683323e094fbfa1ad
|
||||
d29a7d6218772ee19d2fe1396fc5cf6e611f0ff4 f6a8de32150407469750a153ba07b3aaf0699a3c
|
||||
51e458ad571807c6edb5d4a1399eed6a031b537b d29a7d6218772ee19d2fe1396fc5cf6e611f0ff4
|
||||
867dbf41d5cc8ca3fde4dd539afb488a401f357d 51e458ad571807c6edb5d4a1399eed6a031b537b
|
||||
86abc7ea68faad8bca54fafcaeaf1f84a6478caf 867dbf41d5cc8ca3fde4dd539afb488a401f357d
|
||||
9219b2d411a40573e04bf69db37891720786e494 b228c9477ee56f89a09cce3adf2340b28817150c 86abc7ea68faad8bca54fafcaeaf1f84a6478caf
|
||||
5c362c14301b9273ed1dae7edcf373d50b7f9f24 867dbf41d5cc8ca3fde4dd539afb488a401f357d 9219b2d411a40573e04bf69db37891720786e494
|
||||
997e041042c1f9bdef18c0cfd1ffbf7a4a28695b 5c362c14301b9273ed1dae7edcf373d50b7f9f24
|
||||
5d6aa7c48a8116840ce35691cf08df885919a9ab 997e041042c1f9bdef18c0cfd1ffbf7a4a28695b
|
||||
2bc2660ad5cfe4d917e909663b065faef48b3fc5 5d6aa7c48a8116840ce35691cf08df885919a9ab
|
||||
a372e8150eafd25b90bd24df12c4942af56d6ccd 2bc2660ad5cfe4d917e909663b065faef48b3fc5
|
||||
8bdd0cc635765134d88d08f80c8d70c6b36235e2 a372e8150eafd25b90bd24df12c4942af56d6ccd
|
||||
8f05b979478b076b77e9e16023b7040ad07c5862 9219b2d411a40573e04bf69db37891720786e494 8bdd0cc635765134d88d08f80c8d70c6b36235e2
|
||||
583df9ee1f5d27a43c6fa121c81f974a4e9184da a372e8150eafd25b90bd24df12c4942af56d6ccd 8f05b979478b076b77e9e16023b7040ad07c5862
|
||||
388bea740bfde42852f8a4aee6c495c6d81c7251 583df9ee1f5d27a43c6fa121c81f974a4e9184da
|
||||
fd1346c5f402bae694090b409b4e38968ada7793 388bea740bfde42852f8a4aee6c495c6d81c7251
|
||||
d918fdb1373d326c4ce494168f5fa2ccc36a7dba fd1346c5f402bae694090b409b4e38968ada7793
|
||||
fced6719b10e7f26f96ed4e6d550347d032d7f04 8f05b979478b076b77e9e16023b7040ad07c5862 d918fdb1373d326c4ce494168f5fa2ccc36a7dba
|
||||
2d132913c37a42dd79086e760c14431837bc520b fd1346c5f402bae694090b409b4e38968ada7793 fced6719b10e7f26f96ed4e6d550347d032d7f04
|
||||
d98e67ac0cc9ceb4c51dcc96f80247c0bdafb1a2 2d132913c37a42dd79086e760c14431837bc520b
|
||||
2274d02be6b11cfa50d78e5689aa092f63d605f1 d98e67ac0cc9ceb4c51dcc96f80247c0bdafb1a2
|
||||
3cd97481ece8b0f0cf160819fe28187bace1c61c 2274d02be6b11cfa50d78e5689aa092f63d605f1
|
||||
89e6fb4c42d70fa5a44075a2023cc6b520c17d8c fced6719b10e7f26f96ed4e6d550347d032d7f04 3cd97481ece8b0f0cf160819fe28187bace1c61c
|
||||
7c73d1883ba2ee2a9fe39d7c1d90e19d9606b0a1 2274d02be6b11cfa50d78e5689aa092f63d605f1 89e6fb4c42d70fa5a44075a2023cc6b520c17d8c
|
||||
37
.github/workflows/build-docker.yml
vendored
Normal file
37
.github/workflows/build-docker.yml
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
on:
|
||||
release:
|
||||
types: [created]
|
||||
|
||||
name: Build docker images
|
||||
jobs:
|
||||
docker:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Get tag
|
||||
id: tag
|
||||
uses: dawidd6/action-get-tag@v1
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64,linux/arm
|
||||
build-args: |
|
||||
"VERSION=${{ steps.tag.outputs.tag }}"
|
||||
push: true
|
||||
tags: axllent/mailpit:latest,axllent/mailpit:${{ steps.tag.outputs.tag }}
|
||||
72
.github/workflows/codeql-analysis.yml
vendored
Normal file
72
.github/workflows/codeql-analysis.yml
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
#
|
||||
# ******** NOTE ********
|
||||
# We have attempted to detect the languages in your repository. Please check
|
||||
# the `language` matrix defined below to confirm you have the correct set of
|
||||
# supported CodeQL languages.
|
||||
#
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "develop" ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ "develop" ]
|
||||
schedule:
|
||||
- cron: '34 23 * * 4'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'go', 'javascript' ]
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
||||
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
|
||||
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
||||
# queries: security-extended,security-and-quality
|
||||
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||
|
||||
# If the Autobuild fails above, remove it and uncomment the following three lines.
|
||||
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
|
||||
|
||||
# - run: |
|
||||
# echo "Run, Build Application using script"
|
||||
# ./location_of_script_within_repo/buildscript.sh
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
1
.github/workflows/release-build.yml
vendored
1
.github/workflows/release-build.yml
vendored
@@ -38,6 +38,7 @@ jobs:
|
||||
goos: ${{ matrix.goos }}
|
||||
goarch: ${{ matrix.goarch }}
|
||||
binary_name: "mailpit"
|
||||
pre_command: export CGO_ENABLED=0
|
||||
asset_name: mailpit-${{ matrix.goos }}-${{ matrix.goarch }}
|
||||
extra_files: LICENSE README.md
|
||||
md5sum: false
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
name: Test
|
||||
name: Tests
|
||||
on:
|
||||
pull_request:
|
||||
branches: [ develop ]
|
||||
push:
|
||||
branches: [ develop ]
|
||||
branches: [ develop, 'feature/**' ]
|
||||
jobs:
|
||||
test:
|
||||
strategy:
|
||||
@@ -16,6 +16,14 @@ jobs:
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cache/go-build
|
||||
~/go
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-
|
||||
- run: go test ./storage -v
|
||||
- run: go test ./storage -bench=.
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -2,5 +2,6 @@
|
||||
/send
|
||||
/server/ui/dist
|
||||
/Makefile
|
||||
/mailpit
|
||||
/mailpit*
|
||||
*.old
|
||||
*.db
|
||||
|
||||
223
CHANGELOG.md
223
CHANGELOG.md
@@ -1,15 +1,226 @@
|
||||
# Changelog
|
||||
|
||||
## [0.0.3]
|
||||
Notable changes to Mailpit will be documented in this file.
|
||||
|
||||
- Bugfix: Update to clover-v2.0.0-alpha.2 to fix sorting
|
||||
## 1.1.7
|
||||
|
||||
### Fix
|
||||
- Normalize running binary name detection (Windows)
|
||||
|
||||
|
||||
## [0.0.2]
|
||||
## 1.1.6
|
||||
|
||||
- Unread message statistics & updates
|
||||
### Fix
|
||||
- Workaround for Safari source matching bug blocking event listener
|
||||
|
||||
### UI
|
||||
- Add documentation link (wiki)
|
||||
|
||||
|
||||
## 1.1.5
|
||||
|
||||
### Build
|
||||
- Switch to esbuild-sass-plugin
|
||||
|
||||
### UI
|
||||
- Support for inline images using filenames instead of cid
|
||||
|
||||
|
||||
## 1.1.4
|
||||
|
||||
### Feature
|
||||
- Add --quiet flag to display only errors
|
||||
|
||||
### Security
|
||||
- Add restrictive HTTP Content-Security-Policy
|
||||
|
||||
### UI
|
||||
- Minor UI color change & unread count position adjustment
|
||||
- Add favicon unread message counter
|
||||
- Remove left & right borders (message list)
|
||||
|
||||
|
||||
## 1.1.3
|
||||
|
||||
### Fix
|
||||
- Update message download link
|
||||
|
||||
|
||||
## 1.1.2
|
||||
|
||||
### UI
|
||||
- Allow reverse proxy subdirectories
|
||||
|
||||
|
||||
## 1.1.1
|
||||
|
||||
### UI
|
||||
- Attachment icons and image thumbnails
|
||||
|
||||
|
||||
## 1.1.0
|
||||
|
||||
### UI
|
||||
- HTML source & highlighting
|
||||
- Add previous/next message links
|
||||
|
||||
|
||||
## 1.0.0
|
||||
|
||||
### Feature
|
||||
- Multiple message selection for group actions using shift/ctrl click
|
||||
- Search parser improvements
|
||||
|
||||
### Feature
|
||||
- Search parser improvements
|
||||
|
||||
### UI
|
||||
- Post data using 'application/json'
|
||||
- Display unknown recipients as as `Undisclosed recipients`
|
||||
- Update frontend modules & esbuild
|
||||
- Update frontend modules & esbuild
|
||||
|
||||
|
||||
## 1.0.0-beta1
|
||||
|
||||
### BREAKING CHANGE
|
||||
|
||||
This release includes a major backend storage change (SQLite) that will render any previously-saved messages useless. Please delete old data to free up space. For more information see https://github.com/axllent/mailpit/issues/10
|
||||
|
||||
### Feature
|
||||
- Switch backend storage to use SQLite
|
||||
|
||||
### UI
|
||||
- Resize preview iframe on load
|
||||
|
||||
|
||||
## 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
|
||||
|
||||
### Feature
|
||||
- Email compression in storage
|
||||
|
||||
### Testing
|
||||
- Enable testing on feature branches
|
||||
- Database total/unread statistics tests
|
||||
|
||||
### UI
|
||||
- Mobile compatibility improvements & functionality
|
||||
|
||||
|
||||
## 0.1.3
|
||||
|
||||
### Feature
|
||||
- Mark all messages as read
|
||||
|
||||
### UI
|
||||
- Better error handling when connection to server is broken
|
||||
- Add reset search button
|
||||
- Minor UI tweaks
|
||||
- Update pagination values when new mail arrives when not on first page
|
||||
|
||||
### Pull Requests
|
||||
- Merge pull request [#6](https://github.com/axllent/mailpit/issues/6) from KaptinLin/develop
|
||||
|
||||
|
||||
## 0.1.2
|
||||
|
||||
### Feature
|
||||
- Optional browser notifications (HTTPS only)
|
||||
|
||||
### Security
|
||||
- Don't allow tar files containing a ".."
|
||||
- Sanitize mailbox names
|
||||
- Use strconv.Atoi() for safe string to int conversions
|
||||
|
||||
|
||||
## 0.1.1
|
||||
|
||||
### Bugfix
|
||||
- Fix env variable for MP_UI_SSL_KEY
|
||||
|
||||
|
||||
## 0.1.0
|
||||
|
||||
### Feature
|
||||
- SMTP STARTTLS & SMTP authentication support
|
||||
|
||||
|
||||
## 0.0.9
|
||||
|
||||
### Bugfix
|
||||
- Include read status in search results
|
||||
|
||||
### Feature
|
||||
- HTTPS option for web UI
|
||||
|
||||
### Testing
|
||||
- Memory & physical database tests
|
||||
|
||||
|
||||
## 0.0.8
|
||||
|
||||
### Bugfix
|
||||
- Fix total/unread count after failed message inserts
|
||||
|
||||
### UI
|
||||
- Add project links to help in CLI
|
||||
|
||||
|
||||
## 0.0.7
|
||||
|
||||
### Bugfix
|
||||
- Command flag should be `--auth-file`
|
||||
|
||||
|
||||
## 0.0.6
|
||||
|
||||
### Bugfix
|
||||
- Disable CGO when building multi-arch binaries
|
||||
|
||||
|
||||
## 0.0.5
|
||||
|
||||
### Feature
|
||||
- Basic authentication support
|
||||
|
||||
|
||||
## 0.0.4
|
||||
|
||||
### Bugfix
|
||||
- Update to clover-v2.0.0-alpha.2 to fix sorting
|
||||
|
||||
### Tests
|
||||
- Add search tests
|
||||
|
||||
### UI
|
||||
- Add date to console log
|
||||
- Add space in To fields
|
||||
- Cater for messages without From email address
|
||||
- Minor UI & logging changes
|
||||
- Add space in To fields
|
||||
- cater for messages without From email address
|
||||
|
||||
|
||||
## 0.0.3
|
||||
|
||||
### Bugfix
|
||||
- Update to clover-v2.0.0-alpha.2 to fix sorting
|
||||
|
||||
|
||||
## 0.0.2
|
||||
|
||||
### Feature
|
||||
- Unread statistics
|
||||
|
||||
|
||||
## [0.0.1-beta]
|
||||
|
||||
- First release
|
||||
|
||||
20
Dockerfile
Normal file
20
Dockerfile
Normal file
@@ -0,0 +1,20 @@
|
||||
FROM golang:alpine as builder
|
||||
|
||||
ARG VERSION=dev
|
||||
|
||||
COPY . /app
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN apk add --no-cache git npm && \
|
||||
npm install && npm run package && \
|
||||
CGO_ENABLED=0 go build -ldflags "-s -w -X github.com/axllent/mailpit/cmd.Version=${VERSION}" -o /mailpit
|
||||
|
||||
|
||||
FROM alpine:latest
|
||||
|
||||
COPY --from=builder /mailpit /mailpit
|
||||
|
||||
RUN apk add --no-cache tzdata
|
||||
|
||||
ENTRYPOINT ["/mailpit"]
|
||||
@@ -1,45 +0,0 @@
|
||||
# Building Mailpit from source
|
||||
|
||||
Go (>= version 1.8) and npm are required to compile mailpit from source.
|
||||
|
||||
```
|
||||
git clone git@github.com:axllent/mailpit.git
|
||||
cd mailpit
|
||||
```
|
||||
|
||||
## Building the UI
|
||||
|
||||
The Mailpit web user interface is built with node. In the project's root (top) directory run the following to install the required node modules:
|
||||
|
||||
|
||||
### Installing the node modules
|
||||
```
|
||||
npm install
|
||||
```
|
||||
|
||||
|
||||
### Building the web UI
|
||||
|
||||
```
|
||||
npm run build
|
||||
```
|
||||
|
||||
You can also run `npm run watch` which will watch for changes and rebuild the HTML/CSS/JS automatically when changes are detected.
|
||||
Please note that you must restart Mailpit (`go run .`) to run with the changes.
|
||||
|
||||
|
||||
## Build the mailpit binary
|
||||
|
||||
One you have the assets compiled, you can build mailpit as follows:
|
||||
```
|
||||
go build -ldflags "-s -w"
|
||||
```
|
||||
|
||||
## Building a stand-alone sendmail binary
|
||||
|
||||
This step is unnecessary, however if you do not intend to either symlink `sendmail` to mailpit or configure your existing sendmail to route mail to mailpit, you can optionally build a stand-alone sendmail binary.
|
||||
|
||||
```
|
||||
cd sendmail
|
||||
go build -ldflags "-s -w"
|
||||
```
|
||||
55
README.md
55
README.md
@@ -1,38 +1,51 @@
|
||||
# Mailpit
|
||||
# Mailpit - email testing for developers
|
||||
|
||||
Mailpit is an email testing tool for developers.
|
||||

|
||||

|
||||

|
||||

|
||||
[](https://goreportcard.com/report/github.com/axllent/mailpit)
|
||||
|
||||
Mailpit is a multi-platform email testing tool for developers.
|
||||
|
||||
It acts as both an SMTP server, and provides a web interface to view all captured emails.
|
||||
|
||||
Mailpit is inspired by [MailHog](#why-rewrite-mailhog), but much, much faster.
|
||||
|
||||

|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- Runs completely on a single binary
|
||||
- Runs entirely from a single binary, no installation required
|
||||
- SMTP server (default `0.0.0.0:1025`)
|
||||
- Web UI to view emails (HTML format, text, source and MIME attachments, default `0.0.0.0:8025`)
|
||||
- Web UI to view emails (formatted HTML, highlighted HTML source, text, raw source and MIME attachments including image thumbnails)
|
||||
- Advanced mail search ([see wiki](https://github.com/axllent/mailpit/wiki/Mail-search))
|
||||
- Real-time web UI updates using web sockets for new mail
|
||||
- Email storage in either memory or disk (using [CloverDB](https://github.com/ostafen/clover)) - note that in-memory has a physical limit of 1MB per email
|
||||
- Optional browser notifications for new mail (HTTPS only)
|
||||
- Configurable automatic email pruning (default keeps the most recent 500 emails)
|
||||
- Fast SMTP processing & storing - approximately 300-600 emails per second depending on CPU, network speed & email size
|
||||
- Can handle tens of thousands of emails
|
||||
|
||||
|
||||
## Planned features
|
||||
|
||||
- Optional HTTPS for web UI
|
||||
- Optional basic authentication for web UI
|
||||
- Optional authentication for SMTP
|
||||
- Browser notifications for new mail (HTTPS only)
|
||||
- Docker container
|
||||
- Email storage either in a temporary or persistent database ([see wiki](https://github.com/axllent/mailpit/wiki/Email-storage))
|
||||
- Fast SMTP processing & storing - approximately 70-100 emails per second depending on CPU, network speed & email size
|
||||
- Can handle hundreds of thousands of emails
|
||||
- Optional SMTP with STARTTLS & SMTP authentication ([see wiki](https://github.com/axllent/mailpit/wiki/SMTP-with-STARTTLS-and-authentication))
|
||||
- Optional HTTPS for web UI ([see wiki](https://github.com/axllent/mailpit/wiki/HTTPS))
|
||||
- Optional basic authentication for web UI ([see wiki](https://github.com/axllent/mailpit/wiki/Basic-authentication))
|
||||
- Multi-architecture [Docker images](https://github.com/axllent/mailpit/wiki/Docker-images)
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
Download a pre-built binary in the [releases](https://github.com/axllent/mailpit/releases/latest). The `mailpit` can be placed in your `$PATH`, or simply run as `./mailpit`. See `mailpit -h` for options.
|
||||
Linux & Mac users can install it directly to `/usr/local/bin/mailpit` with:
|
||||
|
||||
To build mailpit from source see [building from source](README-BUILDING.md).
|
||||
```bash
|
||||
sudo bash < <(curl -sL https://raw.githubusercontent.com/axllent/mailpit/develop/install.sh)
|
||||
```
|
||||
|
||||
Or download a pre-built binary in the [releases](https://github.com/axllent/mailpit/releases/latest). The `mailpit` can be placed in your `$PATH`, or simply run as `./mailpit`. See `mailpit -h` for options, or see [the wiki](https://github.com/axllent/mailpit/wiki/Runtime-options) for additional information.
|
||||
|
||||
To build Mailpit from source see [building from source](https://github.com/axllent/mailpit/wiki/Building-from-source).
|
||||
|
||||
The Mailpit web UI listens by default on `http://0.0.0.0:8025`, and the SMTP port on `0.0.0.0:1025`.
|
||||
|
||||
|
||||
### Configuring sendmail
|
||||
@@ -44,17 +57,17 @@ You can use `mailpit sendmail` as your sendmail configuration in `php.ini`:
|
||||
sendmail_path = /usr/local/bin/mailpit sendmail
|
||||
```
|
||||
|
||||
If mailpit is found on the same host as sendmail, you can symlink the mailpit binary to sendmail, eg: `ln -s /usr/local/bin/mailpit /usr/sbin/sendmail` (only if mailpit is running on default 1025 port).
|
||||
If Mailpit is found on the same host as sendmail, you can symlink the Mailpit binary to sendmail, eg: `ln -s /usr/local/bin/mailpit /usr/sbin/sendmail` (only if Mailpit is running on default 1025 port).
|
||||
|
||||
You can use your default system `sendmail` binary to route directly to port `1025` (configurable) by calling `/usr/sbin/sendmail -S localhost:1025`.
|
||||
|
||||
You can build a mailpit-specific sendmail binary from source ( see [building from source](README-BUILDING.md)).
|
||||
You can build a Mailpit-specific sendmail binary from source (see [building from source](https://github.com/axllent/mailpit/wiki/Building-from-source)).
|
||||
|
||||
|
||||
## Why rewrite MailHog?
|
||||
|
||||
I had been using MailHog for a few years to intercept and test emails generated from several projects. MailHog has a number of severe performance issues, many of the modules are horribly out of date, and other than a few accepted MRs, it is not actively developed.
|
||||
|
||||
Initially I started trying to upgrade a fork of MailHog (both the UI as well as the HTTP server & API), but soon discovered that it is (with all due respect) very poorly designed. It is over-engineered (split over 9 separate projects), has too many unnecessary features for my purpose, and performs exceptionally poorly when dealing with large lumbers of emails or processing any email with an attachment (a single email with a 3MB attachment can take over a minute). The API transmits a lot of duplicate and unnecessary data on every message request for all web calls, and there is no HTTP compression.
|
||||
Initially I started trying to upgrade a fork of MailHog (both the UI as well as the HTTP server & API), but soon discovered that it is (with all due respect) very poorly designed. It is over-engineered (split over 9 separate projects) and has too many unnecessary features for my purpose. It performs exceptionally poorly when dealing with large amounts of emails or processing any email with an attachment (a single email with a 3MB attachment can take over a minute to ingest). The API also transmits a lot of duplicate and unnecessary data on every message request for all web calls, and there is no HTTP compression.
|
||||
|
||||
In order to improve it I felt it needed to be completely rewritten, and so Mailpit was born.
|
||||
|
||||
85
cmd/root.go
85
cmd/root.go
@@ -1,6 +1,7 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
@@ -20,7 +21,11 @@ var rootCmd = &cobra.Command{
|
||||
Short: "Mailpit is an email testing tool for developers",
|
||||
Long: `Mailpit is an email testing tool for developers.
|
||||
|
||||
It acts as an SMTP server, and provides a web interface to view all captured emails.`,
|
||||
It acts as an SMTP server, and provides a web interface to view all captured emails.
|
||||
|
||||
Documentation:
|
||||
https://github.com/axllent/mailpit
|
||||
https://github.com/axllent/mailpit/wiki`,
|
||||
Run: func(_ *cobra.Command, _ []string) {
|
||||
if err := config.VerifyConfig(); err != nil {
|
||||
logger.Log().Error(err.Error())
|
||||
@@ -60,13 +65,16 @@ func SendmailExecute() {
|
||||
func init() {
|
||||
// hide autocompletion
|
||||
rootCmd.CompletionOptions.HiddenDefaultCmd = true
|
||||
// rootCmd.Flags().SortFlags = false
|
||||
// hide help
|
||||
rootCmd.Flags().SortFlags = false
|
||||
// hide help command
|
||||
rootCmd.SetHelpCommand(&cobra.Command{Hidden: true})
|
||||
// hide help flag
|
||||
rootCmd.PersistentFlags().BoolP("help", "h", false, "This help")
|
||||
rootCmd.PersistentFlags().Lookup("help").Hidden = true
|
||||
|
||||
// defaults from envars if provided
|
||||
if len(os.Getenv("MP_DATA_DIR")) > 0 {
|
||||
config.DataDir = os.Getenv("MP_DATA_DIR")
|
||||
if len(os.Getenv("MP_DATA_FILE")) > 0 {
|
||||
config.DataFile = os.Getenv("MP_DATA_FILE")
|
||||
}
|
||||
if len(os.Getenv("MP_SMTP_BIND_ADDR")) > 0 {
|
||||
config.SMTPListen = os.Getenv("MP_SMTP_BIND_ADDR")
|
||||
@@ -77,10 +85,73 @@ func init() {
|
||||
if len(os.Getenv("MP_MAX_MESSAGES")) > 0 {
|
||||
config.MaxMessages, _ = strconv.Atoi(os.Getenv("MP_MAX_MESSAGES"))
|
||||
}
|
||||
if len(os.Getenv("MP_UI_AUTH_FILE")) > 0 {
|
||||
config.UIAuthFile = os.Getenv("MP_UI_AUTH_FILE")
|
||||
}
|
||||
if len(os.Getenv("MP_UI_SSL_CERT")) > 0 {
|
||||
config.UISSLCert = os.Getenv("MP_UI_SSL_CERT")
|
||||
}
|
||||
if len(os.Getenv("MP_UI_SSL_KEY")) > 0 {
|
||||
config.UISSLKey = os.Getenv("MP_UI_SSL_KEY")
|
||||
}
|
||||
if len(os.Getenv("MP_SMTP_AUTH_FILE")) > 0 {
|
||||
config.SMTPAuthFile = os.Getenv("MP_SMTP_AUTH_FILE")
|
||||
}
|
||||
if len(os.Getenv("MP_SMTP_SSL_CERT")) > 0 {
|
||||
config.SMTPSSLCert = os.Getenv("MP_SMTP_SSL_CERT")
|
||||
}
|
||||
if len(os.Getenv("MP_SMTP_SSL_KEY")) > 0 {
|
||||
config.SMTPSSLKey = os.Getenv("MP_SMTP_SSL_KEY")
|
||||
}
|
||||
|
||||
rootCmd.Flags().StringVarP(&config.DataDir, "data", "d", config.DataDir, "Optional path to store peristent data")
|
||||
// deprecated 2022/08/06
|
||||
if len(os.Getenv("MP_AUTH_FILE")) > 0 {
|
||||
config.UIAuthFile = os.Getenv("MP_AUTH_FILE")
|
||||
}
|
||||
// deprecated 2022/08/06
|
||||
if len(os.Getenv("MP_SSL_CERT")) > 0 {
|
||||
config.UISSLCert = os.Getenv("MP_SSL_CERT")
|
||||
}
|
||||
// deprecated 2022/08/06
|
||||
if len(os.Getenv("MP_SSL_KEY")) > 0 {
|
||||
config.UISSLKey = os.Getenv("MP_SSL_KEY")
|
||||
}
|
||||
|
||||
// deprecated 2022/08/28
|
||||
if len(os.Getenv("MP_DATA_DIR")) > 0 {
|
||||
fmt.Println("MP_DATA_DIR has been deprecated, use MP_DATA_FILE")
|
||||
config.DataFile = os.Getenv("MP_DATA_DIR")
|
||||
}
|
||||
|
||||
rootCmd.Flags().StringVarP(&config.DataFile, "db-file", "d", config.DataFile, "Database file to store persistent data")
|
||||
rootCmd.Flags().StringVarP(&config.SMTPListen, "smtp", "s", config.SMTPListen, "SMTP bind interface and port")
|
||||
rootCmd.Flags().StringVarP(&config.HTTPListen, "listen", "l", config.HTTPListen, "HTTP bind interface and port for UI")
|
||||
rootCmd.Flags().IntVarP(&config.MaxMessages, "max", "m", config.MaxMessages, "Max number of messages per mailbox")
|
||||
rootCmd.Flags().IntVarP(&config.MaxMessages, "max", "m", config.MaxMessages, "Max number of messages to store")
|
||||
|
||||
rootCmd.Flags().StringVar(&config.UIAuthFile, "ui-auth-file", config.UIAuthFile, "A password file for web UI authentication")
|
||||
rootCmd.Flags().StringVar(&config.UISSLCert, "ui-ssl-cert", config.UISSLCert, "SSL certificate for web UI - requires ui-ssl-key")
|
||||
rootCmd.Flags().StringVar(&config.UISSLKey, "ui-ssl-key", config.UISSLKey, "SSL key for web UI - requires ui-ssl-cert")
|
||||
|
||||
rootCmd.Flags().StringVar(&config.SMTPAuthFile, "smtp-auth-file", config.SMTPAuthFile, "A password file for SMTP authentication")
|
||||
rootCmd.Flags().StringVar(&config.SMTPSSLCert, "smtp-ssl-cert", config.SMTPSSLCert, "SSL certificate for SMTP - requires smtp-ssl-key")
|
||||
rootCmd.Flags().StringVar(&config.SMTPSSLKey, "smtp-ssl-key", config.SMTPSSLKey, "SSL key for SMTP - requires smtp-ssl-cert")
|
||||
|
||||
rootCmd.Flags().BoolVarP(&config.QuietLogging, "quiet", "q", false, "Quiet logging (errors only)")
|
||||
rootCmd.Flags().BoolVarP(&config.VerboseLogging, "verbose", "v", false, "Verbose logging")
|
||||
|
||||
// deprecated 2022/08/06
|
||||
rootCmd.Flags().StringVarP(&config.UIAuthFile, "auth-file", "a", config.UIAuthFile, "A password file for web UI authentication")
|
||||
rootCmd.Flags().StringVar(&config.UISSLCert, "ssl-cert", config.UISSLCert, "SSL certificate - requires ssl-key")
|
||||
rootCmd.Flags().StringVar(&config.UISSLKey, "ssl-key", config.UISSLKey, "SSL key - requires ssl-cert")
|
||||
rootCmd.Flags().Lookup("auth-file").Hidden = true
|
||||
rootCmd.Flags().Lookup("auth-file").Deprecated = "use --ui-auth-file"
|
||||
rootCmd.Flags().Lookup("ssl-cert").Hidden = true
|
||||
rootCmd.Flags().Lookup("ssl-cert").Deprecated = "use --ui-ssl-cert"
|
||||
rootCmd.Flags().Lookup("ssl-key").Hidden = true
|
||||
rootCmd.Flags().Lookup("ssl-key").Deprecated = "use --ui-ssl-key"
|
||||
|
||||
// deprecated 2022/08/30
|
||||
rootCmd.Flags().StringVar(&config.DataFile, "data", config.DataFile, "Database file to store persistent data")
|
||||
rootCmd.Flags().Lookup("data").Hidden = true
|
||||
rootCmd.Flags().Lookup("data").Deprecated = "use --db-file"
|
||||
}
|
||||
|
||||
121
config/config.go
121
config/config.go
@@ -2,7 +2,12 @@ package config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
|
||||
"github.com/tg123/go-htpasswd"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -12,8 +17,8 @@ var (
|
||||
// HTTPListen to listen on <interface>:<port>
|
||||
HTTPListen = "0.0.0.0:8025"
|
||||
|
||||
// DataDir for mail (optional)
|
||||
DataDir string
|
||||
// DataFile for mail (optional)
|
||||
DataFile string
|
||||
|
||||
// MaxMessages is the maximum number of messages a mailbox can have (auto-pruned every minute)
|
||||
MaxMessages = 500
|
||||
@@ -21,17 +26,43 @@ var (
|
||||
// VerboseLogging for console output
|
||||
VerboseLogging = false
|
||||
|
||||
// NoLogging for testing
|
||||
// QuietLogging for console output (errors only)
|
||||
QuietLogging = false
|
||||
|
||||
// NoLogging for tests
|
||||
NoLogging = false
|
||||
|
||||
// SSLCert @TODO
|
||||
SSLCert string
|
||||
// SSLKey @TODO
|
||||
SSLKey string
|
||||
// UISSLCert file
|
||||
UISSLCert string
|
||||
|
||||
// UISSLKey file
|
||||
UISSLKey string
|
||||
|
||||
// UIAuthFile for basic authentication
|
||||
UIAuthFile string
|
||||
|
||||
// UIAuth used for euthentication
|
||||
UIAuth *htpasswd.File
|
||||
|
||||
// SMTPSSLCert file
|
||||
SMTPSSLCert string
|
||||
|
||||
// SMTPSSLKey file
|
||||
SMTPSSLKey string
|
||||
|
||||
// SMTPAuthFile for SMTP authentication
|
||||
SMTPAuthFile string
|
||||
|
||||
// SMTPAuth used for euthentication
|
||||
SMTPAuth *htpasswd.File
|
||||
)
|
||||
|
||||
// VerifyConfig wil do some basic checking
|
||||
func VerifyConfig() error {
|
||||
if DataFile != "" && isDir(DataFile) {
|
||||
DataFile = filepath.Join(DataFile, "mailpit.db")
|
||||
}
|
||||
|
||||
re := regexp.MustCompile(`^[a-zA-Z0-9\.\-]{3,}:\d{2,}$`)
|
||||
if !re.MatchString(SMTPListen) {
|
||||
return errors.New("SMTP bind should be in the format of <ip>:<port>")
|
||||
@@ -40,5 +71,81 @@ func VerifyConfig() error {
|
||||
return errors.New("HTTP bind should be in the format of <ip>:<port>")
|
||||
}
|
||||
|
||||
if UIAuthFile != "" {
|
||||
if !isFile(UIAuthFile) {
|
||||
return fmt.Errorf("HTTP password file not found: %s", UIAuthFile)
|
||||
}
|
||||
|
||||
a, err := htpasswd.New(UIAuthFile, htpasswd.DefaultSystems, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
UIAuth = a
|
||||
}
|
||||
|
||||
if UISSLCert != "" && UISSLKey == "" || UISSLCert == "" && UISSLKey != "" {
|
||||
return errors.New("you must provide both a UI SSL certificate and a key")
|
||||
}
|
||||
|
||||
if UISSLCert != "" {
|
||||
if !isFile(UISSLCert) {
|
||||
return fmt.Errorf("SSL certificate not found: %s", UISSLCert)
|
||||
}
|
||||
|
||||
if !isFile(UISSLKey) {
|
||||
return fmt.Errorf("SSL key not found: %s", UISSLKey)
|
||||
}
|
||||
}
|
||||
|
||||
if SMTPSSLCert != "" && SMTPSSLKey == "" || SMTPSSLCert == "" && SMTPSSLKey != "" {
|
||||
return errors.New("you must provide both an SMTP SSL certificate and a key")
|
||||
}
|
||||
|
||||
if SMTPSSLCert != "" {
|
||||
if !isFile(SMTPSSLCert) {
|
||||
return fmt.Errorf("SMTP SSL certificate not found: %s", SMTPSSLCert)
|
||||
}
|
||||
|
||||
if !isFile(SMTPSSLKey) {
|
||||
return fmt.Errorf("SMTP SSL key not found: %s", SMTPSSLKey)
|
||||
}
|
||||
}
|
||||
|
||||
if SMTPAuthFile != "" {
|
||||
if !isFile(SMTPAuthFile) {
|
||||
return fmt.Errorf("SMTP password file not found: %s", SMTPAuthFile)
|
||||
}
|
||||
|
||||
if SMTPSSLCert == "" {
|
||||
return errors.New("SMTP authentication requires SMTP encryption")
|
||||
}
|
||||
|
||||
a, err := htpasswd.New(SMTPAuthFile, htpasswd.DefaultSystems, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
SMTPAuth = a
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsFile returns if a path is a file
|
||||
func isFile(path string) bool {
|
||||
info, err := os.Stat(path)
|
||||
if os.IsNotExist(err) || !info.Mode().IsRegular() {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// IsDir returns whether a path is a directory
|
||||
func isDir(path string) bool {
|
||||
info, err := os.Stat(path)
|
||||
if os.IsNotExist(err) || !info.IsDir() {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// Package data contains the message & mailbox structs
|
||||
package data
|
||||
|
||||
import (
|
||||
@@ -17,9 +18,9 @@ type Message struct {
|
||||
Bcc []*mail.Address
|
||||
Subject string
|
||||
Date time.Time
|
||||
Created time.Time
|
||||
Text string
|
||||
HTML string
|
||||
HTMLSource string
|
||||
Size int
|
||||
Inline []Attachment
|
||||
Attachments []Attachment
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
const { build } = require('esbuild')
|
||||
const pluginVue = require('esbuild-plugin-vue-next')
|
||||
const sassPlugin = require("esbuild-plugin-sass");
|
||||
const { sassPlugin } = require('esbuild-sass-plugin');
|
||||
|
||||
const doWatch = process.env.WATCH == 'true' ? true : false;
|
||||
const doMinify = process.env.MINIFY == 'true' ? true : false;
|
||||
|
||||
59
go.mod
59
go.mod
@@ -3,50 +3,61 @@ module github.com/axllent/mailpit
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/GuiaBolso/darwin v0.0.0-20191218124601-fd6d2aa3d244
|
||||
github.com/axllent/semver v0.0.1
|
||||
github.com/disintegration/imaging v1.6.2
|
||||
github.com/gorilla/mux v1.8.0
|
||||
github.com/gorilla/websocket v1.5.0
|
||||
github.com/jhillyerd/enmime v0.10.0
|
||||
github.com/k3a/html2text v1.0.8
|
||||
github.com/klauspost/compress v1.15.9
|
||||
github.com/leporo/sqlf v1.3.0
|
||||
github.com/mattn/go-shellwords v1.0.12
|
||||
github.com/mhale/smtpd v0.8.0
|
||||
github.com/ostafen/clover/v2 v2.0.0-alpha.2
|
||||
github.com/satori/go.uuid v1.2.0
|
||||
github.com/sirupsen/logrus v1.9.0
|
||||
github.com/spf13/cobra v1.5.0
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/tg123/go-htpasswd v1.2.0
|
||||
golang.org/x/text v0.3.7
|
||||
modernc.org/sqlite v1.18.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.0 // indirect
|
||||
github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962 // indirect
|
||||
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a // indirect
|
||||
github.com/cespare/xxhash v1.1.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/dgraph-io/badger/v3 v3.2103.2 // indirect
|
||||
github.com/dgraph-io/ristretto v0.1.0 // indirect
|
||||
github.com/dustin/go-humanize v1.0.0 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/cznic/ql v1.2.0 // indirect
|
||||
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f // indirect
|
||||
github.com/golang/glog v1.0.0 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/flatbuffers v2.0.6+incompatible // indirect
|
||||
github.com/google/go-cmp v0.5.8 // indirect
|
||||
github.com/google/orderedcode v0.0.1 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.1 // indirect
|
||||
github.com/jaytaylor/html2text v0.0.0-20211105163654-bc68cce691ba // indirect
|
||||
github.com/klauspost/compress v1.15.9 // indirect
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||
github.com/kr/pretty v0.3.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.16 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.13 // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/rivo/uniseg v0.3.1 // indirect
|
||||
github.com/satori/go.uuid v1.2.0 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
|
||||
github.com/rivo/uniseg v0.3.4 // indirect
|
||||
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
|
||||
github.com/stretchr/testify v1.7.2 // indirect
|
||||
github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
||||
go.opencensus.io v0.23.0 // indirect
|
||||
golang.org/x/net v0.0.0-20220728211354-c7608f3a8462 // indirect
|
||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
google.golang.org/protobuf v1.28.1 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d // indirect
|
||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 // indirect
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
|
||||
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b // indirect
|
||||
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 // indirect
|
||||
golang.org/x/tools v0.1.12 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
|
||||
lukechampine.com/uint128 v1.2.0 // indirect
|
||||
modernc.org/cc/v3 v3.36.3 // indirect
|
||||
modernc.org/ccgo/v3 v3.16.9 // indirect
|
||||
modernc.org/libc v1.17.1 // indirect
|
||||
modernc.org/mathutil v1.5.0 // indirect
|
||||
modernc.org/memory v1.2.1 // indirect
|
||||
modernc.org/opt v0.1.3 // indirect
|
||||
modernc.org/strutil v1.1.3 // indirect
|
||||
modernc.org/token v1.0.1 // indirect
|
||||
)
|
||||
|
||||
274
go.sum
274
go.sum
@@ -1,100 +1,64 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60=
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
|
||||
github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962 h1:KeNholpO2xKjgaaSyd+DyQRrsQjhbSeS7qe4nEw8aQw=
|
||||
github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962/go.mod h1:kC29dT1vFpj7py2OvG1khBdQpo3kInWP+6QipLbdngo=
|
||||
github.com/GuiaBolso/darwin v0.0.0-20191218124601-fd6d2aa3d244 h1:dqzm54OhCqY8RinR/cx+Ppb0y56Ds5I3wwWhx4XybDg=
|
||||
github.com/GuiaBolso/darwin v0.0.0-20191218124601-fd6d2aa3d244/go.mod h1:3sqgkckuISJ5rs1EpOp6vCvwOUKe/z9vPmyuIlq8Q/A=
|
||||
github.com/axllent/semver v0.0.1 h1:QqF+KSGxgj8QZzSXAvKFqjGWE5792ksOnQhludToK8E=
|
||||
github.com/axllent/semver v0.0.1/go.mod h1:2xSPzvG8n9mRfdtxSvWvfTfQGWfHsMsHO1iZnKATMSc=
|
||||
github.com/brianvoe/gofakeit/v6 v6.17.0 h1:obbQTJeHfktJtiZzq0Q1bEpsNUs+yHrYlPVWt7BtmJ4=
|
||||
github.com/brianvoe/gofakeit/v6 v6.17.0/go.mod h1:Ow6qC71xtwm79anlwKRlWZW6zVq9D2XHE4QSSMP/rU8=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
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/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/cznic/b v0.0.0-20180115125044-35e9bbe41f07 h1:UHFGPvSxX4C4YBApSPvmUfL8tTvWLj2ryqvT9K4Jcuk=
|
||||
github.com/cznic/b v0.0.0-20180115125044-35e9bbe41f07/go.mod h1:URriBxXwVq5ijiJ12C7iIZqlA69nTlI+LgI6/pwftG8=
|
||||
github.com/cznic/fileutil v0.0.0-20180108211300-6a051e75936f h1:7uSNgsgcarNk4oiN/nNkO0J7KAjlsF5Yv5Gf/tFdHas=
|
||||
github.com/cznic/fileutil v0.0.0-20180108211300-6a051e75936f/go.mod h1:8S58EK26zhXSxzv7NQFpnliaOQsmDUxvoQO3rt154Vg=
|
||||
github.com/cznic/golex v0.0.0-20170803123110-4ab7c5e190e4 h1:CVAqftqbj+exlab+8KJQrE+kNIVlQfJt58j4GxCMF1s=
|
||||
github.com/cznic/golex v0.0.0-20170803123110-4ab7c5e190e4/go.mod h1:+bmmJDNmKlhWNG+gwWCkaBoTy39Fs+bzRxVBzoTQbIc=
|
||||
github.com/cznic/internal v0.0.0-20180608152220-f44710a21d00 h1:FHpbUtp2K8X53/b4aFNj4my5n+i3x+CQCZWNuHWH/+E=
|
||||
github.com/cznic/internal v0.0.0-20180608152220-f44710a21d00/go.mod h1:olo7eAdKwJdXxb55TKGLiJ6xt1H0/tiiRCWKVLmtjY4=
|
||||
github.com/cznic/lldb v1.1.0 h1:AIA+ham6TSJ+XkMe8imQ/g8KPzMUVWAwqUQQdtuMsHs=
|
||||
github.com/cznic/lldb v1.1.0/go.mod h1:FIZVUmYUVhPwRiPzL8nD/mpFcJ/G7SSXjjXYG4uRI3A=
|
||||
github.com/cznic/mathutil v0.0.0-20180504122225-ca4c9f2c1369 h1:XNT/Zf5l++1Pyg08/HV04ppB0gKxAqtZQBRYiYrUuYk=
|
||||
github.com/cznic/mathutil v0.0.0-20180504122225-ca4c9f2c1369/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM=
|
||||
github.com/cznic/ql v1.2.0 h1:lcKp95ZtdF0XkWhGnVIXGF8dVD2X+ClS08tglKtf+ak=
|
||||
github.com/cznic/ql v1.2.0/go.mod h1:FbpzhyZrqr0PVlK6ury+PoW3T0ODUV22OeWIxcaOrSE=
|
||||
github.com/cznic/sortutil v0.0.0-20150617083342-4c7342852e65 h1:hxuZop6tSoOi0sxFzoGGYdRqNrPubyaIf9KoBG9tPiE=
|
||||
github.com/cznic/sortutil v0.0.0-20150617083342-4c7342852e65/go.mod h1:q2w6Bg5jeox1B+QkJ6Wp/+Vn0G/bo3f1uY7Fn3vivIQ=
|
||||
github.com/cznic/strutil v0.0.0-20171016134553-529a34b1c186 h1:0rkFMAbn5KBKNpJyHQ6Prb95vIKanmAe62KxsrN+sqA=
|
||||
github.com/cznic/strutil v0.0.0-20171016134553-529a34b1c186/go.mod h1:AHHPPPXTw0h6pVabbcbyGRK1DckRn7r/STdZEeIDzZc=
|
||||
github.com/cznic/zappy v0.0.0-20160723133515-2533cb5b45cc h1:YKKpTb2BrXN2GYyGaygIdis1vXbE7SSAG9axGWIMClg=
|
||||
github.com/cznic/zappy v0.0.0-20160723133515-2533cb5b45cc/go.mod h1:Y1SNZ4dRUOKXshKUbwUapqNncRrho4mkjQebgEHZLj8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgraph-io/badger/v3 v3.2103.2 h1:dpyM5eCJAtQCBcMCZcT4UBZchuTJgCywerHHgmxfxM8=
|
||||
github.com/dgraph-io/badger/v3 v3.2103.2/go.mod h1:RHo4/GmYcKKh5Lxu63wLEMHJ70Pac2JqZRYGhlyAo2M=
|
||||
github.com/dgraph-io/ristretto v0.1.0 h1:Jv3CGQHp9OjuMBSne1485aDpUkTKEcUqF+jm/LuerPI=
|
||||
github.com/dgraph-io/ristretto v0.1.0/go.mod h1:fux0lOrBhrVCJd3lcTHsIJhq1T2rokOu6v9Vcb3Q9ug=
|
||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA=
|
||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
|
||||
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
|
||||
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712 h1:aaQcKT9WumO6JEJcRyTqFVq4XUZiUcKR2/GI31TOcz8=
|
||||
github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
|
||||
github.com/go-test/deep v1.0.7 h1:/VSMRlnY/JSyqxQUzQLKVMAskpY/NZKFA5j2P+0pP2M=
|
||||
github.com/go-test/deep v1.0.7/go.mod h1:QV8Hv/iy04NyLBxAdO9njL0iVPN1S4d/A3NVv1V36o8=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/gogs/chardet v0.0.0-20191104214054-4b6791f73a28/go.mod h1:Pcatq5tYkCW2Q6yrR2VRHlbHpZ/R4/7qyL1TCF7vl14=
|
||||
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f h1:3BSP1Tbs2djlpprl7wCLuiqMaUh5SJkkzI2gDs+FgLs=
|
||||
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f/go.mod h1:Pcatq5tYkCW2Q6yrR2VRHlbHpZ/R4/7qyL1TCF7vl14=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ=
|
||||
github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/flatbuffers v1.12.1/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
|
||||
github.com/google/flatbuffers v2.0.6+incompatible h1:XHFReMv7nFFusa+CEokzWbzaYocKXI6C7hdU5Kgh9Lw=
|
||||
github.com/google/flatbuffers v2.0.6+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
||||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/orderedcode v0.0.1 h1:UzfcAexk9Vhv8+9pNOgRu41f16lHq725vPwnSeiG/Us=
|
||||
github.com/google/orderedcode v0.0.1/go.mod h1:iVyU4/qPKHY5h/wSd6rZZCDcLJNxiWO6dvsYES2Sb20=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/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/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
|
||||
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/jaytaylor/html2text v0.0.0-20200412013138-3577fbdbcff7/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk=
|
||||
github.com/jaytaylor/html2text v0.0.0-20211105163654-bc68cce691ba h1:QFQpJdgbON7I0jr2hYW7Bs+XV0qjc3d5tZoDnRFnqTg=
|
||||
github.com/jaytaylor/html2text v0.0.0-20211105163654-bc68cce691ba/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk=
|
||||
@@ -104,45 +68,46 @@ github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/k3a/html2text v1.0.8 h1:rVanLhKilpnJUJs/CNKWzMC4YaQINGxK0rSG8ssmnV0=
|
||||
github.com/k3a/html2text v1.0.8/go.mod h1:ieEXykM67iT8lTvEWBh6fhpH4B23kB9OMKPdIBmgUqA=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
|
||||
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.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY=
|
||||
github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/leporo/sqlf v1.3.0 h1:nAkuPYxMIJg/sUmcd1h4avV5iYo8tBTGEGOIR4BIZO8=
|
||||
github.com/leporo/sqlf v1.3.0/go.mod h1:f4dHqIi1+nLl6k1IsNQ8QIEbGWK49th2ei1IxTXk+2E=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
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/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/mattn/go-sqlite3 v1.14.10/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/mattn/go-sqlite3 v1.14.14 h1:qZgc/Rwetq+MtyE18WhzjokPD93dNqLGNT3QJuLvBGw=
|
||||
github.com/mhale/smtpd v0.8.0 h1:5JvdsehCg33PQrZBvFyDMMUDQmvbzVpZgKob7eYBJc0=
|
||||
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/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
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/ostafen/clover/v2 v2.0.0-alpha.2 h1:PgOWohvpj4qNCyASJ7Q8Ke8ld/wsoi+dQJ05b1ebwus=
|
||||
github.com/ostafen/clover/v2 v2.0.0-alpha.2/go.mod h1:7UyIG46NglzTDRKB4LJiS/enXpuo67Lj05eM8mdhL6M=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.3.1 h1:SDPP7SHNl1L7KrEFCSJslJ/DM9DT02Nq2C61XrfHMmk=
|
||||
github.com/rivo/uniseg v0.3.1/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/rivo/uniseg v0.3.4 h1:3Z3Eu6FGHZWSfNKJTOUiPatWwfc7DzJRU04jFUqJODw=
|
||||
github.com/rivo/uniseg v0.3.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
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 v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
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=
|
||||
@@ -152,136 +117,109 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykE
|
||||
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/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
||||
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||
github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU=
|
||||
github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
||||
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf h1:pvbZ0lM0XWPBqUKqFU8cmavspvIl9nulOYwdy6IFRRo=
|
||||
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02nZ62WenDCkgHFerpIOmW0iT7GKmXM=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s=
|
||||
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU=
|
||||
github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/tg123/go-htpasswd v1.2.0 h1:UKp34m9H467/xklxUxU15wKRru7fwXoTojtxg25ITF0=
|
||||
github.com/tg123/go-htpasswd v1.2.0/go.mod h1:h7IzlfpvIWnVJhNZ0nQ9HaFxHb7pn5uFJYLlEUJa2sM=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||
go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
|
||||
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
|
||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d h1:3qF+Z8Hkrw9sOhrFHti9TlB1Hkac1x+DNRkv0XQiFjo=
|
||||
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 h1:hVwzHzIUGRjiF7EcUjqNxk3NCfkPxbDKRdnNE1Rpg0U=
|
||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
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-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210501142056-aec3718b3fa0/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
golang.org/x/net v0.0.0-20220728211354-c7608f3a8462 h1:UreQrH7DbFXSi9ZFox6FNT3WBooWmdANpU+IfkT1T4I=
|
||||
golang.org/x/net v0.0.0-20220728211354-c7608f3a8462/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b h1:ZmngSVLe/wycRns9MKikG9OWIEjGcGAkacif7oYQaUY=
|
||||
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg=
|
||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 h1:UiNENfZ8gDvpiWw7IpOMQ27spWmThO1RwwdQVbJahJM=
|
||||
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
|
||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
|
||||
lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI=
|
||||
lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
|
||||
modernc.org/cc/v3 v3.36.2/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI=
|
||||
modernc.org/cc/v3 v3.36.3 h1:uISP3F66UlixxWEcKuIWERa4TwrZENHSL8tWxZz8bHg=
|
||||
modernc.org/cc/v3 v3.36.3/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI=
|
||||
modernc.org/ccgo/v3 v3.16.9 h1:AXquSwg7GuMk11pIdw7fmO1Y/ybgazVkMhsZWCV0mHM=
|
||||
modernc.org/ccgo/v3 v3.16.9/go.mod h1:zNMzC9A9xeNUepy6KuZBbugn3c0Mc9TeiJO4lgvkJDo=
|
||||
modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk=
|
||||
modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
|
||||
modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM=
|
||||
modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
|
||||
modernc.org/libc v1.17.0/go.mod h1:XsgLldpP4aWlPlsjqKRdHPqCxCjISdHfM/yeWC5GyW0=
|
||||
modernc.org/libc v1.17.1 h1:Q8/Cpi36V/QBfuQaFVeisEBs3WqoGAJprZzmf7TfEYI=
|
||||
modernc.org/libc v1.17.1/go.mod h1:FZ23b+8LjxZs7XtFMbSzL/EhPxNbfZbErxEHc7cbD9s=
|
||||
modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||
modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
|
||||
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||
modernc.org/memory v1.2.0/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw=
|
||||
modernc.org/memory v1.2.1 h1:dkRh86wgmq/bJu2cAS2oqBCz/KsMZU7TUM4CibQ7eBs=
|
||||
modernc.org/memory v1.2.1/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
|
||||
modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
|
||||
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
|
||||
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
|
||||
modernc.org/sqlite v1.18.1 h1:ko32eKt3jf7eqIkCgPAeHMBXw3riNSLhl2f3loEF7o8=
|
||||
modernc.org/sqlite v1.18.1/go.mod h1:6ho+Gow7oX5V+OiOQ6Tr4xeqbx13UZ6t+Fw9IRUG4d4=
|
||||
modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw=
|
||||
modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY=
|
||||
modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw=
|
||||
modernc.org/tcl v1.13.1 h1:npxzTwFTZYM8ghWicVIX1cRWzj7Nd8i6AqqX2p+IYao=
|
||||
modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||
modernc.org/token v1.0.1 h1:A3qvTqOwexpfZZeyI0FeGPDlSWX5pjZu9hF4lU+EKWg=
|
||||
modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||
modernc.org/z v1.5.1 h1:RTNHdsrOpeoSeOF4FbzTo8gBYByaJ5xT7NgZ9ZqRiJM=
|
||||
|
||||
98
install.sh
Normal file
98
install.sh
Normal file
@@ -0,0 +1,98 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
GH_REPO="axllent/mailpit"
|
||||
TIMEOUT=90
|
||||
|
||||
set -e
|
||||
|
||||
VERSION=$(curl --silent --location --max-time "${TIMEOUT}" "https://api.github.com/repos/${GH_REPO}/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')
|
||||
if [ $? -ne 0 ]; then
|
||||
echo -ne "\nThere was an error trying to check what is the latest version of ssbak.\nPlease try again later.\n"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# detect the platform
|
||||
OS="$(uname)"
|
||||
case $OS in
|
||||
Linux)
|
||||
OS='linux'
|
||||
;;
|
||||
FreeBSD)
|
||||
OS='freebsd'
|
||||
echo 'OS not supported'
|
||||
exit 2
|
||||
;;
|
||||
NetBSD)
|
||||
OS='netbsd'
|
||||
echo 'OS not supported'
|
||||
exit 2
|
||||
;;
|
||||
OpenBSD)
|
||||
OS='openbsd'
|
||||
echo 'OS not supported'
|
||||
exit 2
|
||||
;;
|
||||
Darwin)
|
||||
OS='darwin'
|
||||
;;
|
||||
SunOS)
|
||||
OS='solaris'
|
||||
echo 'OS not supported'
|
||||
exit 2
|
||||
;;
|
||||
*)
|
||||
echo 'OS not supported'
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
|
||||
# detect the arch
|
||||
OS_type="$(uname -m)"
|
||||
case "$OS_type" in
|
||||
x86_64 | amd64)
|
||||
OS_type='amd64'
|
||||
;;
|
||||
i?86 | x86)
|
||||
OS_type='386'
|
||||
;;
|
||||
aarch64 | arm64)
|
||||
OS_type='arm64'
|
||||
;;
|
||||
*)
|
||||
echo 'OS type not supported'
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
|
||||
GH_REPO_BIN="mailpit-${OS}-${OS_type}.tar.gz"
|
||||
|
||||
#create tmp directory and move to it with macOS compatibility fallback
|
||||
tmp_dir=$(mktemp -d 2>/dev/null || mktemp -d -t 'mailpit-install.XXXXXXXXXX')
|
||||
cd "$tmp_dir"
|
||||
|
||||
echo "Downloading Mailpit $VERSION"
|
||||
LINK="https://github.com/${GH_REPO}/releases/download/${VERSION}/${GH_REPO_BIN}"
|
||||
|
||||
curl --silent --location --max-time "${TIMEOUT}" "${LINK}" | tar zxf - || {
|
||||
echo "Error downloading"
|
||||
exit 2
|
||||
}
|
||||
|
||||
mkdir -p /usr/local/bin || exit 2
|
||||
cp mailpit /usr/local/bin/ || exit 2
|
||||
chmod 755 /usr/local/bin/mailpit || exit 2
|
||||
case "$OS" in
|
||||
'linux')
|
||||
chown root:root /usr/local/bin/mailpit || exit 2
|
||||
;;
|
||||
'freebsd' | 'openbsd' | 'netbsd' | 'darwin')
|
||||
chown root:wheel /usr/local/bin/mailpit || exit 2
|
||||
;;
|
||||
*)
|
||||
echo 'OS not supported'
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
|
||||
rm -rf "$tmp_dir"
|
||||
echo "Installed successfully to /usr/local/bin/mailpit"
|
||||
@@ -19,16 +19,20 @@ func Log() *logrus.Logger {
|
||||
log = logrus.New()
|
||||
log.SetLevel(logrus.InfoLevel)
|
||||
if config.VerboseLogging {
|
||||
// verbose logging (debug)
|
||||
log.SetLevel(logrus.DebugLevel)
|
||||
}
|
||||
if config.NoLogging {
|
||||
} else if config.QuietLogging {
|
||||
// show errors only
|
||||
log.SetLevel(logrus.ErrorLevel)
|
||||
} else if config.NoLogging {
|
||||
// disable all logging (tests)
|
||||
log.SetLevel(logrus.PanicLevel)
|
||||
}
|
||||
|
||||
log.Out = os.Stdout
|
||||
log.SetFormatter(&logrus.TextFormatter{
|
||||
FullTimestamp: true,
|
||||
TimestampFormat: "15:04:05",
|
||||
TimestampFormat: "2006/01/02 15:04:05",
|
||||
ForceColors: true,
|
||||
})
|
||||
}
|
||||
|
||||
12
main.go
12
main.go
@@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/axllent/mailpit/cmd"
|
||||
sendmail "github.com/axllent/mailpit/sendmail/cmd"
|
||||
@@ -15,10 +16,19 @@ func main() {
|
||||
}
|
||||
|
||||
// running directly
|
||||
if filepath.Base(exec) == filepath.Base(os.Args[0]) {
|
||||
if normalize(filepath.Base(exec)) == normalize(filepath.Base(os.Args[0])) {
|
||||
cmd.Execute()
|
||||
} else {
|
||||
// symlinked
|
||||
sendmail.Run()
|
||||
}
|
||||
}
|
||||
|
||||
// Normalize returns a lowercase string stripped of the file extension (if exists).
|
||||
// Used for detecting Windows commands which ignores letter casing and `.exe`.
|
||||
// eg: "MaIlpIT.Exe" returns "mailpit"
|
||||
func normalize(s string) string {
|
||||
s = strings.ToLower(s)
|
||||
|
||||
return strings.TrimSuffix(s, filepath.Ext(s))
|
||||
}
|
||||
|
||||
1254
package-lock.json
generated
1254
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -12,14 +12,15 @@
|
||||
"bootstrap": "^5.2.0",
|
||||
"bootstrap-icons": "^1.9.1",
|
||||
"moment": "^2.29.4",
|
||||
"remove": "^0.1.5",
|
||||
"prismjs": "^1.29.0",
|
||||
"tinycon": "^0.6.8",
|
||||
"vue": "^3.2.13"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@popperjs/core": "^2.11.5",
|
||||
"@vue/compiler-sfc": "^3.2.37",
|
||||
"esbuild": "^0.14.50",
|
||||
"esbuild-plugin-sass": "^1.0.1",
|
||||
"esbuild-plugin-vue-next": "^0.1.4"
|
||||
"esbuild-plugin-vue-next": "^0.1.4",
|
||||
"esbuild-sass-plugin": "^2.3.2"
|
||||
}
|
||||
}
|
||||
|
||||
BIN
screenshot.png
Normal file
BIN
screenshot.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 63 KiB |
179
server/api.go
179
server/api.go
@@ -20,37 +20,25 @@ type messagesResult struct {
|
||||
}
|
||||
|
||||
// Return a list of available mailboxes
|
||||
func apiListMailboxes(w http.ResponseWriter, _ *http.Request) {
|
||||
res, err := storage.ListMailboxes()
|
||||
if err != nil {
|
||||
httpError(w, err.Error())
|
||||
return
|
||||
}
|
||||
func apiMailboxStats(w http.ResponseWriter, _ *http.Request) {
|
||||
res := storage.StatsGet()
|
||||
|
||||
bytes, _ := json.Marshal(res)
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.Write(bytes)
|
||||
_, _ = w.Write(bytes)
|
||||
}
|
||||
|
||||
func apiListMailbox(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
|
||||
mailbox := vars["mailbox"]
|
||||
|
||||
if !storage.MailboxExists(mailbox) {
|
||||
fourOFour(w)
|
||||
return
|
||||
}
|
||||
|
||||
// List messages
|
||||
func apiListMessages(w http.ResponseWriter, r *http.Request) {
|
||||
start, limit := getStartLimit(r)
|
||||
|
||||
messages, err := storage.List(mailbox, start, limit)
|
||||
messages, err := storage.List(start, limit)
|
||||
if err != nil {
|
||||
httpError(w, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
stats := storage.StatsGet(mailbox)
|
||||
stats := storage.StatsGet()
|
||||
|
||||
var res messagesResult
|
||||
|
||||
@@ -62,39 +50,28 @@ func apiListMailbox(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
bytes, _ := json.Marshal(res)
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.Write(bytes)
|
||||
_, _ = w.Write(bytes)
|
||||
}
|
||||
|
||||
func apiSearchMailbox(w http.ResponseWriter, r *http.Request) {
|
||||
// Search all messages
|
||||
func apiSearchMessages(w http.ResponseWriter, r *http.Request) {
|
||||
search := strings.TrimSpace(r.URL.Query().Get("query"))
|
||||
if search == "" {
|
||||
fourOFour(w)
|
||||
return
|
||||
}
|
||||
|
||||
vars := mux.Vars(r)
|
||||
mailbox := vars["mailbox"]
|
||||
|
||||
if !storage.MailboxExists(mailbox) {
|
||||
fourOFour(w)
|
||||
return
|
||||
}
|
||||
|
||||
// we will only return up to 200 results
|
||||
start := 0
|
||||
limit := 200
|
||||
|
||||
messages, err := storage.Search(mailbox, search, start, limit)
|
||||
messages, err := storage.Search(search)
|
||||
if err != nil {
|
||||
httpError(w, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
stats := storage.StatsGet(mailbox)
|
||||
stats := storage.StatsGet()
|
||||
|
||||
var res messagesResult
|
||||
|
||||
res.Start = start
|
||||
res.Start = 0
|
||||
res.Items = messages
|
||||
res.Count = len(messages)
|
||||
res.Total = stats.Total
|
||||
@@ -102,36 +79,34 @@ func apiSearchMailbox(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
bytes, _ := json.Marshal(res)
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.Write(bytes)
|
||||
_, _ = w.Write(bytes)
|
||||
}
|
||||
|
||||
// Open a message
|
||||
func apiOpenMessage(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
|
||||
mailbox := vars["mailbox"]
|
||||
id := vars["id"]
|
||||
|
||||
msg, err := storage.GetMessage(mailbox, id)
|
||||
msg, err := storage.GetMessage(id)
|
||||
if err != nil {
|
||||
httpError(w, err.Error())
|
||||
httpError(w, "Message not found")
|
||||
return
|
||||
}
|
||||
|
||||
bytes, _ := json.Marshal(msg)
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.Write(bytes)
|
||||
_, _ = w.Write(bytes)
|
||||
}
|
||||
|
||||
// Download/view an attachment
|
||||
func apiDownloadAttachment(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
|
||||
mailbox := vars["mailbox"]
|
||||
id := vars["id"]
|
||||
partID := vars["partID"]
|
||||
|
||||
a, err := storage.GetAttachmentPart(mailbox, id, partID)
|
||||
a, err := storage.GetAttachmentPart(id, partID)
|
||||
if err != nil {
|
||||
httpError(w, err.Error())
|
||||
return
|
||||
@@ -143,19 +118,18 @@ func apiDownloadAttachment(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
w.Header().Add("Content-Type", a.ContentType)
|
||||
w.Header().Set("Content-Disposition", "filename=\""+fileName+"\"")
|
||||
w.Write(a.Content)
|
||||
_, _ = w.Write(a.Content)
|
||||
}
|
||||
|
||||
// View the full email source as plain text
|
||||
func apiDownloadSource(w http.ResponseWriter, r *http.Request) {
|
||||
// Download the full email source as plain text
|
||||
func apiDownloadRaw(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
|
||||
mailbox := vars["mailbox"]
|
||||
id := vars["id"]
|
||||
|
||||
dl := r.FormValue("dl")
|
||||
|
||||
data, err := storage.GetMessageRaw(mailbox, id)
|
||||
data, err := storage.GetMessageRaw(id)
|
||||
if err != nil {
|
||||
httpError(w, err.Error())
|
||||
return
|
||||
@@ -165,57 +139,138 @@ func apiDownloadSource(w http.ResponseWriter, r *http.Request) {
|
||||
if dl == "1" {
|
||||
w.Header().Set("Content-Disposition", "attachment; filename=\""+id+".eml\"")
|
||||
}
|
||||
w.Write(data)
|
||||
_, _ = w.Write(data)
|
||||
}
|
||||
|
||||
// Delete all messages in the mailbox
|
||||
// Delete all messages
|
||||
func apiDeleteAll(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
|
||||
mailbox := vars["mailbox"]
|
||||
|
||||
err := storage.DeleteAllMessages(mailbox)
|
||||
err := storage.DeleteAllMessages()
|
||||
if err != nil {
|
||||
httpError(w, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Add("Content-Type", "text/plain")
|
||||
w.Write([]byte("ok"))
|
||||
_, _ = w.Write([]byte("ok"))
|
||||
}
|
||||
|
||||
// Delete all selected messages
|
||||
func apiDeleteSelected(w http.ResponseWriter, r *http.Request) {
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
|
||||
var data struct {
|
||||
IDs []string
|
||||
}
|
||||
err := decoder.Decode(&data)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
ids := data.IDs
|
||||
|
||||
for _, id := range ids {
|
||||
if err := storage.DeleteOneMessage(id); err != nil {
|
||||
httpError(w, err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
w.Header().Add("Content-Type", "text/plain")
|
||||
_, _ = w.Write([]byte("ok"))
|
||||
}
|
||||
|
||||
// Delete a single message
|
||||
func apiDeleteOne(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
|
||||
mailbox := vars["mailbox"]
|
||||
id := vars["id"]
|
||||
|
||||
err := storage.DeleteOneMessage(mailbox, id)
|
||||
err := storage.DeleteOneMessage(id)
|
||||
if err != nil {
|
||||
httpError(w, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Add("Content-Type", "text/plain")
|
||||
w.Write([]byte("ok"))
|
||||
_, _ = w.Write([]byte("ok"))
|
||||
}
|
||||
|
||||
// Mark single message as unread
|
||||
func apiUnreadOne(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
|
||||
mailbox := vars["mailbox"]
|
||||
id := vars["id"]
|
||||
|
||||
err := storage.UnreadMessage(mailbox, id)
|
||||
err := storage.MarkUnread(id)
|
||||
if err != nil {
|
||||
httpError(w, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Add("Content-Type", "text/plain")
|
||||
w.Write([]byte("ok"))
|
||||
_, _ = w.Write([]byte("ok"))
|
||||
}
|
||||
|
||||
// Mark all messages as read
|
||||
func apiMarkAllRead(w http.ResponseWriter, r *http.Request) {
|
||||
err := storage.MarkAllRead()
|
||||
if err != nil {
|
||||
httpError(w, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Add("Content-Type", "text/plain")
|
||||
_, _ = w.Write([]byte("ok"))
|
||||
}
|
||||
|
||||
// Mark selected message as read
|
||||
func apiMarkSelectedRead(w http.ResponseWriter, r *http.Request) {
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
|
||||
var data struct {
|
||||
IDs []string
|
||||
}
|
||||
err := decoder.Decode(&data)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
ids := data.IDs
|
||||
|
||||
for _, id := range ids {
|
||||
if err := storage.MarkRead(id); err != nil {
|
||||
httpError(w, err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
w.Header().Add("Content-Type", "text/plain")
|
||||
_, _ = w.Write([]byte("ok"))
|
||||
}
|
||||
|
||||
// Mark selected message as unread
|
||||
func apiMarkSelectedUnread(w http.ResponseWriter, r *http.Request) {
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
|
||||
var data struct {
|
||||
IDs []string
|
||||
}
|
||||
err := decoder.Decode(&data)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
ids := data.IDs
|
||||
|
||||
for _, id := range ids {
|
||||
if err := storage.MarkUnread(id); err != nil {
|
||||
httpError(w, err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
w.Header().Add("Content-Type", "text/plain")
|
||||
_, _ = w.Write([]byte("ok"))
|
||||
}
|
||||
|
||||
// Websocket to broadcast changes
|
||||
|
||||
106
server/server.go
106
server/server.go
@@ -21,6 +21,8 @@ import (
|
||||
//go:embed ui
|
||||
var embeddedFS embed.FS
|
||||
|
||||
var contentSecurityPolicy = "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; frame-src 'self'; img-src * data: blob:; font-src 'self' data:; media-src 'self'; connect-src 'self' ws: wss:; object-src 'none'; base-uri 'self';"
|
||||
|
||||
// Listen will start the httpd
|
||||
func Listen() {
|
||||
serverRoot, err := fs.Sub(embeddedFS, "ui")
|
||||
@@ -34,28 +36,42 @@ func Listen() {
|
||||
go websockets.MessageHub.Run()
|
||||
|
||||
r := mux.NewRouter()
|
||||
r.HandleFunc("/api/mailboxes", gzipHandlerFunc(apiListMailboxes))
|
||||
r.HandleFunc("/api/{mailbox}/messages", gzipHandlerFunc(apiListMailbox))
|
||||
r.HandleFunc("/api/{mailbox}/search", gzipHandlerFunc(apiSearchMailbox))
|
||||
r.HandleFunc("/api/{mailbox}/delete", gzipHandlerFunc(apiDeleteAll))
|
||||
r.HandleFunc("/api/{mailbox}/events", apiWebsocket)
|
||||
r.HandleFunc("/api/{mailbox}/{id}/source", gzipHandlerFunc(apiDownloadSource))
|
||||
r.HandleFunc("/api/{mailbox}/{id}/part/{partID}", gzipHandlerFunc(apiDownloadAttachment))
|
||||
r.HandleFunc("/api/{mailbox}/{id}/delete", gzipHandlerFunc(apiDeleteOne))
|
||||
r.HandleFunc("/api/{mailbox}/{id}/unread", gzipHandlerFunc(apiUnreadOne))
|
||||
r.HandleFunc("/api/{mailbox}/{id}", gzipHandlerFunc(apiOpenMessage))
|
||||
r.HandleFunc("/api/{mailbox}/search", gzipHandlerFunc(apiSearchMailbox))
|
||||
r.PathPrefix("/").Handler(gzipHandler(http.FileServer(http.FS(serverRoot))))
|
||||
r.HandleFunc("/api/stats", middleWareFunc(apiMailboxStats)).Methods("GET")
|
||||
r.HandleFunc("/api/messages", middleWareFunc(apiListMessages)).Methods("GET")
|
||||
r.HandleFunc("/api/search", middleWareFunc(apiSearchMessages)).Methods("GET")
|
||||
r.HandleFunc("/api/delete", middleWareFunc(apiDeleteAll)).Methods("GET")
|
||||
r.HandleFunc("/api/delete", middleWareFunc(apiDeleteSelected)).Methods("POST")
|
||||
r.HandleFunc("/api/events", apiWebsocket).Methods("GET")
|
||||
r.HandleFunc("/api/read", apiMarkAllRead).Methods("GET")
|
||||
r.HandleFunc("/api/read", apiMarkSelectedRead).Methods("POST")
|
||||
r.HandleFunc("/api/unread", apiMarkSelectedUnread).Methods("POST")
|
||||
r.HandleFunc("/api/{id}/raw", middleWareFunc(apiDownloadRaw)).Methods("GET")
|
||||
r.HandleFunc("/api/{id}/part/{partID}", middleWareFunc(apiDownloadAttachment)).Methods("GET")
|
||||
r.HandleFunc("/api/{id}/part/{partID}/thumb", middleWareFunc(apiAttachmentThumbnail)).Methods("GET")
|
||||
r.HandleFunc("/api/{id}/delete", middleWareFunc(apiDeleteOne)).Methods("GET")
|
||||
r.HandleFunc("/api/{id}/unread", middleWareFunc(apiUnreadOne)).Methods("GET")
|
||||
r.HandleFunc("/api/{id}", middleWareFunc(apiOpenMessage)).Methods("GET")
|
||||
r.PathPrefix("/").Handler(middlewareHandler(http.FileServer(http.FS(serverRoot))))
|
||||
http.Handle("/", r)
|
||||
|
||||
if config.SSLCert != "" && config.SSLKey != "" {
|
||||
if config.UIAuthFile != "" {
|
||||
logger.Log().Info("[http] enabling web UI basic authentication")
|
||||
}
|
||||
|
||||
if config.UISSLCert != "" && config.UISSLKey != "" {
|
||||
logger.Log().Infof("[http] starting secure server on https://%s", config.HTTPListen)
|
||||
log.Fatal(http.ListenAndServeTLS(config.HTTPListen, config.SSLCert, config.SSLKey, nil))
|
||||
log.Fatal(http.ListenAndServeTLS(config.HTTPListen, config.UISSLCert, config.UISSLKey, nil))
|
||||
} else {
|
||||
logger.Log().Infof("[http] starting server on http://%s", config.HTTPListen)
|
||||
log.Fatal(http.ListenAndServe(config.HTTPListen, nil))
|
||||
}
|
||||
}
|
||||
|
||||
// BasicAuthResponse returns an basic auth response to the browser
|
||||
func basicAuthResponse(w http.ResponseWriter) {
|
||||
w.Header().Set("WWW-Authenticate", `Basic realm="Login"`)
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
_, _ = w.Write([]byte("Unauthorised.\n"))
|
||||
}
|
||||
|
||||
type gzipResponseWriter struct {
|
||||
@@ -67,9 +83,27 @@ func (w gzipResponseWriter) Write(b []byte) (int, error) {
|
||||
return w.Writer.Write(b)
|
||||
}
|
||||
|
||||
// GzipHandlerFunc http middleware
|
||||
func gzipHandlerFunc(fn http.HandlerFunc) http.HandlerFunc {
|
||||
// MiddleWareFunc http middleware adds optional basic authentication
|
||||
// and gzip compression.
|
||||
func middleWareFunc(fn http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Referrer-Policy", "no-referrer")
|
||||
w.Header().Set("Content-Security-Policy", contentSecurityPolicy)
|
||||
|
||||
if config.UIAuthFile != "" {
|
||||
user, pass, ok := r.BasicAuth()
|
||||
|
||||
if !ok {
|
||||
basicAuthResponse(w)
|
||||
return
|
||||
}
|
||||
|
||||
if !config.UIAuth.Match(user, pass) {
|
||||
basicAuthResponse(w)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
|
||||
fn(w, r)
|
||||
return
|
||||
@@ -82,8 +116,27 @@ func gzipHandlerFunc(fn http.HandlerFunc) http.HandlerFunc {
|
||||
}
|
||||
}
|
||||
|
||||
func gzipHandler(h http.Handler) http.Handler {
|
||||
// MiddlewareHandler http middleware adds optional basic authentication
|
||||
// and gzip compression
|
||||
func middlewareHandler(h http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Referrer-Policy", "no-referrer")
|
||||
w.Header().Set("Content-Security-Policy", contentSecurityPolicy)
|
||||
|
||||
if config.UIAuthFile != "" {
|
||||
user, pass, ok := r.BasicAuth()
|
||||
|
||||
if !ok {
|
||||
basicAuthResponse(w)
|
||||
return
|
||||
}
|
||||
|
||||
if !config.UIAuth.Match(user, pass) {
|
||||
basicAuthResponse(w)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
|
||||
h.ServeHTTP(w, r)
|
||||
return
|
||||
@@ -95,15 +148,19 @@ func gzipHandler(h http.Handler) http.Handler {
|
||||
})
|
||||
}
|
||||
|
||||
// FourOFour returns a standard 404 meesage
|
||||
// FourOFour returns a basic 404 message
|
||||
func fourOFour(w http.ResponseWriter) {
|
||||
w.Header().Set("Referrer-Policy", "no-referrer")
|
||||
w.Header().Set("Content-Security-Policy", contentSecurityPolicy)
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
w.Header().Set("Content-Type", "text/plain")
|
||||
fmt.Fprint(w, "404 page not found")
|
||||
}
|
||||
|
||||
// HTTPError returns a standard 404 meesage
|
||||
// HTTPError returns a basic error message (400 response)
|
||||
func httpError(w http.ResponseWriter, msg string) {
|
||||
w.Header().Set("Referrer-Policy", "no-referrer")
|
||||
w.Header().Set("Content-Security-Policy", contentSecurityPolicy)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.Header().Set("Content-Type", "text/plain")
|
||||
fmt.Fprint(w, msg)
|
||||
@@ -115,16 +172,13 @@ func getStartLimit(req *http.Request) (start int, limit int) {
|
||||
limit = 50
|
||||
|
||||
s := req.URL.Query().Get("start")
|
||||
if n, e := strconv.ParseInt(s, 10, 64); e == nil && n > 0 {
|
||||
start = int(n)
|
||||
if n, err := strconv.Atoi(s); err == nil && n > 0 {
|
||||
start = n
|
||||
}
|
||||
|
||||
l := req.URL.Query().Get("limit")
|
||||
if n, e := strconv.ParseInt(l, 10, 64); e == nil && n > 0 {
|
||||
if n > 500 {
|
||||
n = 500
|
||||
}
|
||||
limit = int(n)
|
||||
if n, err := strconv.Atoi(l); err == nil && n > 0 {
|
||||
limit = n
|
||||
}
|
||||
|
||||
return start, limit
|
||||
|
||||
107
server/thumbnails.go
Normal file
107
server/thumbnails.go
Normal file
@@ -0,0 +1,107 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"image"
|
||||
"image/color"
|
||||
"image/draw"
|
||||
"image/jpeg"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/axllent/mailpit/logger"
|
||||
"github.com/axllent/mailpit/storage"
|
||||
"github.com/disintegration/imaging"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/jhillyerd/enmime"
|
||||
)
|
||||
|
||||
var (
|
||||
thumbWidth = 180
|
||||
thumbHeight = 120
|
||||
)
|
||||
|
||||
// Attachment thumbnail (images only)
|
||||
func apiAttachmentThumbnail(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
|
||||
id := vars["id"]
|
||||
partID := vars["partID"]
|
||||
|
||||
a, err := storage.GetAttachmentPart(id, partID)
|
||||
if err != nil {
|
||||
httpError(w, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
fileName := a.FileName
|
||||
if fileName == "" {
|
||||
fileName = a.ContentID
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(a.ContentType, "image/") {
|
||||
blankImage(a, w)
|
||||
return
|
||||
}
|
||||
|
||||
buf := bytes.NewBuffer(a.Content)
|
||||
|
||||
img, err := imaging.Decode(buf)
|
||||
if err != nil {
|
||||
// it's not an image, return default
|
||||
logger.Log().Warning(err)
|
||||
blankImage(a, w)
|
||||
return
|
||||
}
|
||||
|
||||
var b bytes.Buffer
|
||||
foo := bufio.NewWriter(&b)
|
||||
|
||||
var dstImageFill *image.NRGBA
|
||||
|
||||
if img.Bounds().Dx() < thumbWidth || img.Bounds().Dy() < thumbHeight {
|
||||
dstImageFill = imaging.Fit(img, thumbWidth, thumbHeight, imaging.Lanczos)
|
||||
} else {
|
||||
dstImageFill = imaging.Fill(img, thumbWidth, thumbHeight, imaging.Center, imaging.Lanczos)
|
||||
}
|
||||
// create white image and paste image over the top
|
||||
// preventing black backgrounds for transparent GIF/PNG images
|
||||
dst := imaging.New(thumbWidth, thumbHeight, color.White)
|
||||
// paste the original over the top
|
||||
dst = imaging.OverlayCenter(dst, dstImageFill, 1.0)
|
||||
|
||||
if err := jpeg.Encode(foo, dst, &jpeg.Options{Quality: 70}); err != nil {
|
||||
logger.Log().Warning(err)
|
||||
blankImage(a, w)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Add("Content-Type", "image/jpeg")
|
||||
w.Header().Set("Content-Disposition", "filename=\""+fileName+"\"")
|
||||
_, _ = w.Write(b.Bytes())
|
||||
}
|
||||
|
||||
// Return a blank image instead of an error when file or image not supported
|
||||
func blankImage(a *enmime.Part, w http.ResponseWriter) {
|
||||
rect := image.Rect(0, 0, thumbWidth, thumbHeight)
|
||||
img := image.NewRGBA(rect)
|
||||
background := color.RGBA{255, 255, 255, 255}
|
||||
draw.Draw(img, img.Bounds(), &image.Uniform{background}, image.ZP, draw.Src)
|
||||
var b bytes.Buffer
|
||||
foo := bufio.NewWriter(&b)
|
||||
dstImageFill := imaging.Fill(img, thumbWidth, thumbHeight, imaging.Center, imaging.Lanczos)
|
||||
|
||||
if err := jpeg.Encode(foo, dstImageFill, &jpeg.Options{Quality: 70}); err != nil {
|
||||
logger.Log().Warning(err)
|
||||
}
|
||||
|
||||
fileName := a.FileName
|
||||
if fileName == "" {
|
||||
fileName = a.ContentID
|
||||
}
|
||||
|
||||
w.Header().Add("Content-Type", "image/jpeg")
|
||||
w.Header().Set("Content-Disposition", "filename=\""+fileName+"\"")
|
||||
_, _ = w.Write(b.Bytes())
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
<script>
|
||||
import commonMixins from './mixins.js'
|
||||
import commonMixins from './mixins.js';
|
||||
import Message from './templates/Message.vue';
|
||||
import moment from 'moment'
|
||||
import moment from 'moment';
|
||||
import Tinycon from 'tinycon';
|
||||
|
||||
export default {
|
||||
mixins: [commonMixins],
|
||||
@@ -11,17 +12,23 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
currentPath: window.location.hash,
|
||||
mailbox: "catchall",
|
||||
items: [],
|
||||
limit: 50,
|
||||
total: 0,
|
||||
unread: 0,
|
||||
start: 0,
|
||||
count: 0,
|
||||
search: "",
|
||||
searching: false,
|
||||
isConnected: false,
|
||||
scrollInPlace: false,
|
||||
message: false
|
||||
message: false,
|
||||
messagePrev: false,
|
||||
messageNext: false,
|
||||
notificationsSupported: false,
|
||||
notificationsEnabled: false,
|
||||
selected: [],
|
||||
tcStatus: 0
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -31,6 +38,17 @@ export default {
|
||||
} else {
|
||||
this.message = false;
|
||||
}
|
||||
},
|
||||
unread(v, old) {
|
||||
if (v == this.tcStatus) {
|
||||
return;
|
||||
}
|
||||
this.tcStatus = v;
|
||||
if (v == 0) {
|
||||
Tinycon.reset();
|
||||
} else {
|
||||
Tinycon.setBubble(v);
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -47,19 +65,30 @@ export default {
|
||||
this.currentPath = window.location.hash.slice(1);
|
||||
});
|
||||
|
||||
this.connect();
|
||||
this.notificationsSupported = 'https:' == document.location.protocol
|
||||
&& ("Notification" in window && Notification.permission !== "denied");
|
||||
this.notificationsEnabled = this.notificationsSupported && Notification.permission == "granted";
|
||||
|
||||
Tinycon.setOptions({
|
||||
height: 11,
|
||||
background: '#dd0000',
|
||||
fallback: false
|
||||
});
|
||||
|
||||
this.loadMessages();
|
||||
this.connect();
|
||||
},
|
||||
methods: {
|
||||
loadMessages: function () {
|
||||
let self = this;
|
||||
let params = {};
|
||||
this.selected = [];
|
||||
|
||||
let uri = 'api/'+self.mailbox+'/messages';
|
||||
let uri = 'api/messages';
|
||||
if (self.search) {
|
||||
self.searching = true;
|
||||
self.items = [];
|
||||
uri = 'api/'+self.mailbox+'/search'
|
||||
uri = 'api/search'
|
||||
self.start = 0; // search is displayed on one page
|
||||
params['query'] = self.search;
|
||||
} else {
|
||||
@@ -93,6 +122,13 @@ export default {
|
||||
this.loadMessages();
|
||||
},
|
||||
|
||||
resetSearch: function(e) {
|
||||
e.preventDefault();
|
||||
this.search = '';
|
||||
this.scrollInPlace = true;
|
||||
this.loadMessages();
|
||||
},
|
||||
|
||||
reloadMessages: function() {
|
||||
this.search = "";
|
||||
this.start = 0;
|
||||
@@ -115,10 +151,10 @@ export default {
|
||||
|
||||
openMessage: function(id) {
|
||||
let self = this;
|
||||
let params = {};
|
||||
self.selected = [];
|
||||
|
||||
let uri = 'api/' + self.mailbox + '/' + self.currentPath
|
||||
self.get(uri, params, function(response) {
|
||||
let uri = 'api/' + self.currentPath
|
||||
self.get(uri, false, function(response) {
|
||||
for (let i in self.items) {
|
||||
if (self.items[i].ID == self.currentPath) {
|
||||
if (!self.items[i].Read) {
|
||||
@@ -135,7 +171,14 @@ export default {
|
||||
if (a.ContentID != '') {
|
||||
d.HTML = d.HTML.replace(
|
||||
new RegExp('cid:'+a.ContentID, 'g'),
|
||||
window.location.origin+'/api/'+self.mailbox+'/'+d.ID+'/part/'+a.PartID
|
||||
window.location.origin+'/api/'+d.ID+'/part/'+a.PartID
|
||||
);
|
||||
}
|
||||
if (a.FileName.match(/^[a-zA-Z0-9\_\-\.]+$/)) {
|
||||
// some old email clients use the filename
|
||||
d.HTML = d.HTML.replace(
|
||||
new RegExp('src=(\'|")'+a.FileName+'(\'|")', 'g'),
|
||||
'src="'+window.location.origin+'/api/'+d.ID+'/part/'+a.PartID+'"'
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -147,20 +190,42 @@ export default {
|
||||
if (a.ContentID != '') {
|
||||
d.HTML = d.HTML.replace(
|
||||
new RegExp('cid:'+a.ContentID, 'g'),
|
||||
window.location.origin+'/api/'+self.mailbox+'/'+d.ID+'/part/'+a.PartID
|
||||
window.location.origin+'/api/'+d.ID+'/part/'+a.PartID
|
||||
);
|
||||
}
|
||||
if (a.FileName.match(/^[a-zA-Z0-9\_\-\.]+$/)) {
|
||||
// some old email clients use the filename
|
||||
d.HTML = d.HTML.replace(
|
||||
new RegExp('src=(\'|")'+a.FileName+'(\'|")', 'g'),
|
||||
'src="'+window.location.origin+'/api/'+d.ID+'/part/'+a.PartID+'"'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.message = d;
|
||||
// generate the prev/next links based on current message list
|
||||
self.messagePrev = false;
|
||||
self.messageNext = false;
|
||||
let found = false;
|
||||
for (let i in self.items) {
|
||||
if (self.items[i].ID == self.message.ID) {
|
||||
found = true;
|
||||
} else if (found && !self.messageNext) {
|
||||
self.messageNext = self.items[i].ID;
|
||||
break;
|
||||
} else {
|
||||
self.messagePrev = self.items[i].ID;
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
deleteAll: function() {
|
||||
let self = this;
|
||||
let uri = 'api/' + self.mailbox + '/delete'
|
||||
let uri = 'api/delete'
|
||||
self.get(uri, false, function(response) {
|
||||
window.location.hash = "";
|
||||
self.reloadMessages();
|
||||
});
|
||||
},
|
||||
@@ -170,12 +235,24 @@ export default {
|
||||
if (!self.message) {
|
||||
return false;
|
||||
}
|
||||
let uri = 'api/' + self.mailbox + '/' + self.message.ID + '/delete'
|
||||
let uri = 'api/' + self.message.ID + '/delete'
|
||||
self.get(uri, false, function(response) {
|
||||
window.location.hash = "";
|
||||
self.scrollInPlace = true;
|
||||
self.loadMessages();
|
||||
});
|
||||
},
|
||||
|
||||
deleteSelected: function() {
|
||||
let self = this;
|
||||
if (!self.selected.length) {
|
||||
return false;
|
||||
}
|
||||
let uri = 'api/delete'
|
||||
self.post(uri, {'ids': self.selected}, function(response) {
|
||||
window.location.hash = "";
|
||||
self.scrollInPlace = true;
|
||||
self.loadMessages();
|
||||
});
|
||||
},
|
||||
|
||||
@@ -184,7 +261,7 @@ export default {
|
||||
if (!self.message) {
|
||||
return false;
|
||||
}
|
||||
let uri = 'api/' + self.mailbox + '/' + self.message.ID + '/unread'
|
||||
let uri = 'api/' + self.message.ID + '/unread'
|
||||
self.get(uri, false, function(response) {
|
||||
window.location.hash = "";
|
||||
self.scrollInPlace = true;
|
||||
@@ -192,10 +269,48 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
markAllRead: function() {
|
||||
let self = this;
|
||||
let uri = 'api/read'
|
||||
self.get(uri, false, function(response) {
|
||||
window.location.hash = "";
|
||||
self.scrollInPlace = true;
|
||||
self.loadMessages();
|
||||
});
|
||||
},
|
||||
|
||||
markSelectedRead: function() {
|
||||
let self = this;
|
||||
if (!self.selected.length) {
|
||||
return false;
|
||||
}
|
||||
let uri = 'api/read'
|
||||
self.post(uri, {'ids': self.selected}, function(response) {
|
||||
window.location.hash = "";
|
||||
self.scrollInPlace = true;
|
||||
self.loadMessages();
|
||||
});
|
||||
},
|
||||
|
||||
markSelectedUnread: function() {
|
||||
let self = this;
|
||||
if (!self.selected.length) {
|
||||
return false;
|
||||
}
|
||||
let uri = 'api/unread'
|
||||
self.post(uri, {'ids': self.selected}, function(response) {
|
||||
window.location.hash = "";
|
||||
self.scrollInPlace = true;
|
||||
self.loadMessages();
|
||||
});
|
||||
},
|
||||
|
||||
// websocket connect
|
||||
connect: function () {
|
||||
let wsproto = location.protocol == 'https:' ? 'wss' : 'ws';
|
||||
let ws = new WebSocket(wsproto + "://" + document.location.host + "/api/"+this.mailbox+"/events");
|
||||
let ws = new WebSocket(
|
||||
wsproto + "://" + document.location.host + document.location.pathname + "api/events"
|
||||
);
|
||||
let self = this;
|
||||
ws.onmessage = function (e) {
|
||||
let response = JSON.parse(e.data);
|
||||
@@ -204,22 +319,24 @@ export default {
|
||||
}
|
||||
// new messages
|
||||
if (response.Type == "new" && response.Data) {
|
||||
if (self.start < 1) {
|
||||
if (!self.searching) {
|
||||
if (!self.searching) {
|
||||
if (self.start < 1) {
|
||||
self.items.unshift(response.Data);
|
||||
if (self.items.length > self.limit) {
|
||||
self.items.pop();
|
||||
}
|
||||
} else {
|
||||
self.start++;
|
||||
}
|
||||
}
|
||||
self.total++;
|
||||
self.unread++;
|
||||
self.browserNotify("New mail from: " + response.Data.From.Address, response.Data.Subject);
|
||||
} else if (response.Type == "prune") {
|
||||
// messages have been deleted, reload messages to adjust
|
||||
self.scrollInPlace = true;
|
||||
self.loadMessages();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ws.onopen = function () {
|
||||
@@ -245,52 +362,158 @@ export default {
|
||||
return message.To[i].Address;
|
||||
}
|
||||
|
||||
return '[ Unknown ]';
|
||||
return '[ Undisclosed recipients ]';
|
||||
},
|
||||
|
||||
getRelativeCreated: function(message) {
|
||||
let d = new Date(message.Created)
|
||||
return moment(d).fromNow().toString();
|
||||
},
|
||||
|
||||
browserNotify: function(title, message) {
|
||||
if (!("Notification" in window)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Notification.permission === "granted") {
|
||||
let b = message.Subject;
|
||||
let options = {
|
||||
body: message,
|
||||
icon: 'mailpit.png'
|
||||
}
|
||||
new Notification(title, options);
|
||||
}
|
||||
},
|
||||
|
||||
requestNotifications: function() {
|
||||
// check if the browser supports notifications
|
||||
if (!("Notification" in window)) {
|
||||
alert("This browser does not support desktop notification");
|
||||
}
|
||||
|
||||
// we need to ask the user for permission
|
||||
else if (Notification.permission !== "denied") {
|
||||
let self = this;
|
||||
Notification.requestPermission().then(function (permission) {
|
||||
// If the user accepts, let's create a notification
|
||||
if (permission === "granted") {
|
||||
self.browserNotify("Notifications enabled", "You will receive notifications when new mails are received.");
|
||||
self.notificationsEnabled = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
toggleSelected: function(e, id) {
|
||||
e.preventDefault();
|
||||
|
||||
if (this.isSelected(id)) {
|
||||
this.selected = this.selected.filter(function(ele){
|
||||
return ele != id;
|
||||
});
|
||||
} else {
|
||||
this.selected.push(id);
|
||||
}
|
||||
},
|
||||
|
||||
selectRange: function(e, id) {
|
||||
e.preventDefault();
|
||||
|
||||
let selecting = false;
|
||||
let lastSelected = this.selected.length > 0 && this.selected[this.selected.length - 1];
|
||||
if (lastSelected == id) {
|
||||
this.selected = this.selected.filter(function(ele){
|
||||
return ele != id;
|
||||
});
|
||||
return
|
||||
}
|
||||
|
||||
if (lastSelected === false) {
|
||||
this.selected.push(id);
|
||||
return;
|
||||
}
|
||||
|
||||
for (let d of this.items) {
|
||||
if (selecting) {
|
||||
if (!this.isSelected(d.ID)) {
|
||||
this.selected.push(d.ID);
|
||||
}
|
||||
if (d.ID == lastSelected || d.ID == id) {
|
||||
// reached backwards select
|
||||
break;
|
||||
}
|
||||
} else if (d.ID == id || d.ID == lastSelected) {
|
||||
if (!this.isSelected(d.ID)) {
|
||||
this.selected.push(d.ID);
|
||||
}
|
||||
selecting = true;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
isSelected: function(id) {
|
||||
return this.selected.indexOf(id) != -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="navbar navbar-expand-lg navbar-light row flex-shrink-0 bg-light">
|
||||
<div class="col-lg-2 col-md-3 col-auto">
|
||||
<div class="navbar navbar-expand-lg navbar-light row flex-shrink-0 bg-light shadow-sm">
|
||||
<div class="col-lg-2 col-md-3 d-none d-md-block">
|
||||
<a class="navbar-brand" href="#" v-on:click="reloadMessages">
|
||||
<img src="mailpit.svg" alt="Mailpit">
|
||||
<span class="d-none d-md-inline-block ms-2">Mailpit</span>
|
||||
<span class="ms-2">Mailpit</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="col col-md-9 col-lg-8" v-if="message">
|
||||
<div class="col col-md-9 col-lg-10" v-if="message">
|
||||
<a class="btn btn-outline-secondary me-4 px-3" href="#" v-on:click="message=false" title="Return to messages">
|
||||
<i class="bi bi-arrow-return-left"></i>
|
||||
</a>
|
||||
<button class="btn btn-outline-secondary me-2" title="Delete message" v-on:click="deleteOne">
|
||||
<i class="bi bi-trash-fill"></i>
|
||||
<i class="bi bi-trash-fill"></i> <span class="d-none d-md-inline">Delete</span>
|
||||
</button>
|
||||
<button class="btn btn-outline-secondary me-2" title="Mark unread" v-on:click="markUnread">
|
||||
<i class="bi bi-envelope"></i>
|
||||
<i class="bi bi-eye-slash"></i> <span class="d-none d-md-inline">Mark unread</span>
|
||||
</button>
|
||||
<a :href="'api/' + mailbox + '/' + message.ID + '/source?dl=1'" class="btn btn-outline-secondary me-2" title="Download message">
|
||||
<i class="bi bi-file-arrow-down-fill"></i>
|
||||
<a class="btn btn-outline-secondary float-end" :class="messageNext ? '':'disabled'" :href="'#'+messageNext" title="View next message">
|
||||
<i class="bi bi-caret-right-fill"></i>
|
||||
</a>
|
||||
<a class="btn btn-outline-secondary ms-2 me-1 float-end" :class="messagePrev ? '': 'disabled'" :href="'#'+messagePrev" title="View previous message">
|
||||
<i class="bi bi-caret-left-fill"></i>
|
||||
</a>
|
||||
<a :href="'api/' + message.ID + '/raw?dl=1'" class="btn btn-outline-secondary me-2 float-end" title="Download message">
|
||||
<i class="bi bi-file-arrow-down-fill"></i> <span class="d-none d-md-inline">Download</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="col col-md-9 col-lg-5" v-if="!message && total">
|
||||
<div class="col col-md-9 col-lg-5 LOL" v-if="!message">
|
||||
<form v-on:submit="doSearch">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" v-model.trim="search" placeholder="Search mailbox">
|
||||
<button class="btn btn-outline-secondary" type="submit"><i class="bi bi-search"></i></button>
|
||||
<a class="navbar-brand d-md-none" href="#" v-on:click="reloadMessages">
|
||||
<img src="mailpit.svg" alt="Mailpit">
|
||||
<span v-if="!total" class="ms-2">Mailpit</span>
|
||||
</a>
|
||||
<div v-if="total" class="d-flex bg-white border rounded-start flex-fill position-relative">
|
||||
<input type="text" class="form-control border-0" v-model.trim="search" placeholder="Search mailbox">
|
||||
<span class="btn btn-link position-absolute end-0 text-muted" v-if="search" v-on:click="resetSearch"><i class="bi bi-x-circle"></i></span>
|
||||
</div>
|
||||
<button v-if="total" class="btn btn-outline-secondary" type="submit"><i class="bi bi-search"></i></button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-12 col-lg-5 text-end" v-if="!message && total">
|
||||
<div class="col-12 col-lg-5 text-end mt-2 mt-lg-0" v-if="!message && total">
|
||||
<button v-if="total" class="btn btn-outline-danger float-start d-md-none me-2" data-bs-toggle="modal" data-bs-target="#DeleteAllModal" title="Delete all messages">
|
||||
<i class="bi bi-trash-fill"></i>
|
||||
</button>
|
||||
|
||||
<button v-if="unread" class="btn btn-outline-primary float-start d-md-none" data-bs-toggle="modal" data-bs-target="#MarkAllReadModal" title="Mark all read">
|
||||
<i class="bi bi-check2-square"></i>
|
||||
</button>
|
||||
|
||||
<select v-model="limit" v-on:change="loadMessages"
|
||||
class="form-select form-select-sm d-inline w-auto me-1" v-if="!searching">
|
||||
class="form-select form-select-sm d-inline w-auto me-2" v-if="!searching">
|
||||
<option value="25">25</option>
|
||||
<option value="50">50</option>
|
||||
<option value="100">100</option>
|
||||
@@ -303,11 +526,10 @@ export default {
|
||||
<small>
|
||||
<b>{{ formatNumber(start + 1) }}-{{ formatNumber(start + items.length) }}</b> of <b>{{ formatNumber(total) }}</b>
|
||||
</small>
|
||||
<button class="btn btn-outline-secondary ms-3 me-1" :disabled="!canPrev" v-on:click="viewPrev"
|
||||
v-if="!searching">
|
||||
<button class="btn btn-outline-secondary ms-2 me-1" :disabled="!canPrev" v-on:click="viewPrev" v-if="!searching" :title="'View previous '+limit+' messages'">
|
||||
<i class="bi bi-caret-left-fill"></i>
|
||||
</button>
|
||||
<button class="btn btn-outline-secondary" :disabled="!canNext" v-on:click="viewNext" v-if="!searching">
|
||||
<button class="btn btn-outline-secondary" :disabled="!canNext" v-on:click="viewNext" v-if="!searching" :title="'View next '+limit+' messages'">
|
||||
<i class="bi bi-caret-right-fill"></i>
|
||||
</button>
|
||||
</span>
|
||||
@@ -315,36 +537,76 @@ export default {
|
||||
</div>
|
||||
<div class="row flex-fill" style="min-height:0">
|
||||
<div class="d-none d-md-block col-lg-2 col-md-3 mh-100 position-relative" style="overflow-y: auto;">
|
||||
<ul class="list-unstyled mt-3">
|
||||
<li v-if="isConnected" title="Messages will auto-load">
|
||||
<ul class="list-unstyled mt-3 mb-5">
|
||||
<li v-if="isConnected" title="Messages will auto-load" class="mb-3">
|
||||
<i class="bi bi-power text-success"></i>
|
||||
Connected
|
||||
</li>
|
||||
<li v-else title="Messages will auto-load">
|
||||
<li v-else title="You need to manually refresh your mailbox" class="mb-3">
|
||||
<i class="bi bi-power text-danger"></i>
|
||||
Disconnected
|
||||
</li>
|
||||
<li class="mt-3">
|
||||
<li class="mb-5">
|
||||
<a class="position-relative ps-0" href="#" v-on:click="reloadMessages">
|
||||
<i class="bi bi-envelope me-1" v-if="isConnected"></i>
|
||||
<i class="bi bi-arrow-clockwise me-1" v-else></i>
|
||||
Inbox
|
||||
<span class="position-absolute mt-2 ms-4 start-100 translate-middle badge rounded-pill text-bg-secondary" title="Unread messages" v-if="unread">
|
||||
<span class="badge rounded-pill text-bg-primary ms-1" title="Unread messages" v-if="unread">
|
||||
{{ formatNumber(unread) }}
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="mt-3 mb-5">
|
||||
<a v-if="total" href="#" data-bs-toggle="modal" data-bs-target="#deleteAllModal">
|
||||
<li class="my-3" v-if="unread && !selected.length">
|
||||
<a href="#" data-bs-toggle="modal" data-bs-target="#MarkAllReadModal">
|
||||
<i class="bi bi-eye-fill"></i>
|
||||
Mark all read
|
||||
</a>
|
||||
</li>
|
||||
<li class="my-3" v-if="total && !selected.length">
|
||||
<a href="#" data-bs-toggle="modal" data-bs-target="#DeleteAllModal">
|
||||
<i class="bi bi-trash-fill me-1 text-danger"></i>
|
||||
Delete all
|
||||
</a>
|
||||
</li>
|
||||
<li class="mt-5 position-fixed bottom-0 w-100">
|
||||
<a href="https://github.com/axllent/mailpit" target="_blank" class="text-muted w-100 d-block bg-white py-2">
|
||||
|
||||
<li class="my-3" v-if="selected.length > 0">
|
||||
<b class="me-2">Selected {{selected.length}}</b>
|
||||
<button class="btn btn-sm text-muted" v-on:click="selected=[]" title="Unselect messages"><i class="bi bi-x-circle"></i></button>
|
||||
</li>
|
||||
<li class="my-3 ms-2" v-if="unread && selected.length > 0">
|
||||
<a href="#" v-on:click="markSelectedRead">
|
||||
<i class="bi bi-eye-fill"></i>
|
||||
Mark read
|
||||
</a>
|
||||
</li>
|
||||
<li class="my-3 ms-2" v-if="selected.length > 0">
|
||||
<a href="#" v-on:click="markSelectedUnread">
|
||||
<i class="bi bi-eye-slash"></i>
|
||||
Mark unread
|
||||
</a>
|
||||
</li>
|
||||
<li class="my-3 ms-2" v-if="total && selected.length > 0">
|
||||
<a href="#" v-on:click="deleteSelected">
|
||||
<i class="bi bi-trash-fill me-1 text-danger"></i>
|
||||
Delete
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="my-3" v-if="notificationsSupported && !notificationsEnabled">
|
||||
<a href="#" data-bs-toggle="modal" data-bs-target="#EnableNotificationsModal" title="Enable browser notifications">
|
||||
<i class="bi bi-bell"></i>
|
||||
Enable alerts
|
||||
</a>
|
||||
</li>
|
||||
<li class="mt-5 position-fixed bottom-0 bg-white py-2 text-muted">
|
||||
<a href="https://github.com/axllent/mailpit" target="_blank" class="text-muted me-1">
|
||||
<i class="bi bi-github"></i>
|
||||
GitHub
|
||||
</a>
|
||||
/
|
||||
<a href="https://github.com/axllent/mailpit/wiki" target="_blank" class="text-muted ms-1">
|
||||
Docs
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -352,43 +614,51 @@ export default {
|
||||
<div class="col-lg-10 col-md-9 mh-100 pe-0">
|
||||
<div class="mh-100" style="overflow-y: auto;" :class="message ? 'd-none':''" id="message-page">
|
||||
<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"
|
||||
:class="message.Read ? 'read':''" XXXv-on:click="openMessage(message)">
|
||||
<div class="col-md-3">
|
||||
<div class="d-md-none float-end text-muted text-nowrap small">
|
||||
<a v-for="message in items" :href="'#'+message.ID"
|
||||
v-on:click.ctrl="toggleSelected($event, message.ID)" v-on:click.shift="selectRange($event, message.ID)"
|
||||
class="row message d-flex small list-group-item list-group-item-action border-start-0 border-end-0"
|
||||
:class="message.Read ? 'read':'', isSelected(message.ID) ? 'selected':''">
|
||||
<div class="col-lg-3">
|
||||
<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>
|
||||
{{ getRelativeCreated(message) }}
|
||||
</div>
|
||||
|
||||
<div class="text-truncate d-md-none">
|
||||
<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>
|
||||
</div>
|
||||
<div class="text-truncate d-none d-md-block">
|
||||
<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>
|
||||
</div>
|
||||
<div class="d-none d-md-block text-truncate text-muted small">
|
||||
<div class="d-none d-lg-block text-truncate text-muted small privacy">
|
||||
{{ getPrimaryEmailTo(message) }}
|
||||
<span v-if="message.To && message.To.length > 1">
|
||||
[+{{message.To.length - 1}}]
|
||||
</span>
|
||||
</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>
|
||||
</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>
|
||||
{{ getFileSize(message.Size) }}
|
||||
</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) }}
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div v-else class="text-muted py-3">No messages</div>
|
||||
<div v-else class="text-muted my-3">
|
||||
<span v-if="searching">
|
||||
No results matching your search
|
||||
</span>
|
||||
<span v-else>
|
||||
There are no emails in your mailbox
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Message v-if="message" :message="message" :mailbox="mailbox"></Message>
|
||||
<Message v-if="message" :message="message"></Message>
|
||||
</div>
|
||||
<div id="loading" v-if="loading">
|
||||
<div class="d-flex justify-content-center align-items-center h-100">
|
||||
@@ -400,23 +670,64 @@ export default {
|
||||
</div>
|
||||
|
||||
<!-- Modal -->
|
||||
<div class="modal fade" id="deleteAllModal" tabindex="-1" aria-labelledby="deleteAllModalLabel" aria-hidden="true">
|
||||
<div class="modal fade" id="DeleteAllModal" tabindex="-1" aria-labelledby="DeleteAllModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="deleteAllModalLabel">Delete all messages?</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
This will permanently delete all messages.
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
||||
<button type="button" class="btn btn-danger" data-bs-dismiss="modal" v-on:click="deleteAll">Delete</button>
|
||||
</div>
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="DeleteAllModalLabel">Delete all messages?</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
This will permanently delete {{ formatNumber(total) }} message<span v-if="total > 1">s</span>.
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
||||
<button type="button" class="btn btn-danger" data-bs-dismiss="modal" v-on:click="deleteAll">Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal -->
|
||||
<div class="modal fade" id="MarkAllReadModal" tabindex="-1" aria-labelledby="MarkAllReadModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="MarkAllReadModalLabel">Mark all messages as read?</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
This will mark {{ formatNumber(unread) }} message<span v-if="unread > 1">s</span> as read.
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
||||
<button type="button" class="btn btn-primary" data-bs-dismiss="modal" v-on:click="markAllRead">Confirm</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal -->
|
||||
<div class="modal fade" id="EnableNotificationsModal" tabindex="-1" aria-labelledby="EnableNotificationsModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="EnableNotificationsModalLabel">Enable browser notifications?</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p class="h4">Get browser notifications when Mailpit receives a new mail?</p>
|
||||
<p>
|
||||
Note that your browser will ask you for confirmation when you click <code>enable notifications</code>,
|
||||
and that you must have Mailpit open in a browser tab to be able to receive the notifications.
|
||||
</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
||||
<button type="button" class="btn btn-primary" data-bs-dismiss="modal" v-on:click="requestNotifications">Enable notifications</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
$link-decoration: none;
|
||||
$primary: #3465b5;
|
||||
|
||||
@@ -1,16 +1,26 @@
|
||||
// @import "../../../node_modules/bootstrap-icons"; ///scss/root";
|
||||
|
||||
@import "bootstrap";
|
||||
|
||||
[v-cloak] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.navbar-brand {
|
||||
color: #2d4a5d;
|
||||
.navbar {
|
||||
z-index: 99;
|
||||
|
||||
img {
|
||||
width: 40px;
|
||||
.navbar-brand {
|
||||
color: #2d4a5d;
|
||||
|
||||
img {
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
@include media-breakpoint-down(md) {
|
||||
padding: 0;
|
||||
|
||||
img {
|
||||
width: 35px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,18 +34,18 @@
|
||||
z-index: 1500;
|
||||
}
|
||||
|
||||
.message.read:not(.active) {
|
||||
// background: $gray-100;
|
||||
.message.read:not(.active):not(.selected) {
|
||||
color: $gray-500;
|
||||
}
|
||||
|
||||
#nav-plain-text,
|
||||
#nav-plain-text .text-view,
|
||||
#nav-source {
|
||||
white-space: pre;
|
||||
font-family: Courier New, Courier, System, fixed-width;
|
||||
font-size: 0.85em;
|
||||
}
|
||||
#nav-plain-text {
|
||||
|
||||
#nav-plain-text .text-view {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
@@ -43,7 +53,7 @@
|
||||
margin: 15px 0 0;
|
||||
|
||||
th {
|
||||
padding-right: 10px;
|
||||
padding-right: 1.5rem;
|
||||
font-weight: normal;
|
||||
vertical-align: top;
|
||||
}
|
||||
@@ -52,3 +62,225 @@
|
||||
vertical-align: top;
|
||||
}
|
||||
}
|
||||
|
||||
#nav-html {
|
||||
padding-right: 1.5rem;
|
||||
}
|
||||
|
||||
#preview-html {
|
||||
min-height: 300px;
|
||||
}
|
||||
|
||||
.list-group-item:first-child {
|
||||
border-top: 0;
|
||||
}
|
||||
|
||||
.message.selected {
|
||||
background: $primary;
|
||||
color: #fff;
|
||||
|
||||
.text-muted {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
&.read {
|
||||
b {
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
body.blur {
|
||||
.privacy {
|
||||
filter: blur(3px);
|
||||
}
|
||||
}
|
||||
|
||||
.card.attachment {
|
||||
color: $gray-800;
|
||||
|
||||
.icon {
|
||||
position: absolute;
|
||||
top: 18px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
font-size: 3.5rem;
|
||||
text-align: center;
|
||||
color: $gray-300;
|
||||
}
|
||||
|
||||
.card-body {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
overflow: hidden;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.card-footer {
|
||||
background: $gray-300;
|
||||
|
||||
.bi {
|
||||
font-size: 1.3em;
|
||||
margin-left: -10px;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.card-body {
|
||||
opacity: 1;
|
||||
background: $gray-300;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* PrismJS 1.29.0 - modified!
|
||||
https://prismjs.com/download.html#themes=prism-coy&languages=markup+css */
|
||||
code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
color: #000;
|
||||
background: 0 0;
|
||||
font-size: 0.85em;
|
||||
text-align: left;
|
||||
white-space: pre;
|
||||
word-spacing: normal;
|
||||
word-break: normal;
|
||||
word-wrap: normal;
|
||||
line-height: 1.5;
|
||||
-moz-tab-size: 4;
|
||||
-o-tab-size: 4;
|
||||
tab-size: 4;
|
||||
-webkit-hyphens: none;
|
||||
-moz-hyphens: none;
|
||||
-ms-hyphens: none;
|
||||
hyphens: none;
|
||||
}
|
||||
pre[class*="language-"] {
|
||||
position: relative;
|
||||
overflow: visible;
|
||||
}
|
||||
pre[class*="language-"] > code {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
code[class*="language-"] {
|
||||
max-height: inherit;
|
||||
height: inherit;
|
||||
padding: 0 1em;
|
||||
display: block;
|
||||
overflow: auto;
|
||||
}
|
||||
:not(pre) > code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
background-color: #fdfdfd;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
:not(pre) > code[class*="language-"] {
|
||||
position: relative;
|
||||
padding: 0.2em;
|
||||
border-radius: 0.3em;
|
||||
color: #c92c2c;
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
display: inline;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.token.block-comment,
|
||||
.token.cdata,
|
||||
.token.comment,
|
||||
.token.doctype,
|
||||
.token.prolog {
|
||||
color: #7d8b99;
|
||||
}
|
||||
.token.punctuation {
|
||||
color: #5f6364;
|
||||
}
|
||||
.token.boolean,
|
||||
.token.constant,
|
||||
.token.deleted,
|
||||
.token.function-name,
|
||||
.token.number,
|
||||
.token.property,
|
||||
.token.symbol,
|
||||
.token.tag {
|
||||
color: #c92c2c;
|
||||
}
|
||||
.token.attr-name,
|
||||
.token.builtin,
|
||||
.token.char,
|
||||
.token.function,
|
||||
.token.inserted,
|
||||
.token.selector,
|
||||
.token.string {
|
||||
color: #2f9c0a;
|
||||
}
|
||||
.token.entity,
|
||||
.token.operator,
|
||||
.token.url,
|
||||
.token.variable {
|
||||
color: #a67f59;
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
.token.atrule,
|
||||
.token.attr-value,
|
||||
.token.class-name,
|
||||
.token.keyword {
|
||||
color: #1990b8;
|
||||
}
|
||||
.token.important,
|
||||
.token.regex {
|
||||
color: #e90;
|
||||
}
|
||||
.language-css .token.string,
|
||||
.style .token.string {
|
||||
color: #a67f59;
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
.token.important {
|
||||
font-weight: 400;
|
||||
}
|
||||
.token.bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
.token.italic {
|
||||
font-style: italic;
|
||||
}
|
||||
.token.entity {
|
||||
cursor: help;
|
||||
}
|
||||
.token.namespace {
|
||||
opacity: 0.7;
|
||||
}
|
||||
@media screen and (max-width: 767px) {
|
||||
pre[class*="language-"]:after,
|
||||
pre[class*="language-"]:before {
|
||||
bottom: 14px;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
pre[class*="language-"].line-numbers.line-numbers {
|
||||
padding-left: 0;
|
||||
}
|
||||
pre[class*="language-"].line-numbers.line-numbers code {
|
||||
padding-left: 3.8em;
|
||||
}
|
||||
pre[class*="language-"].line-numbers.line-numbers .line-numbers-rows {
|
||||
left: 0;
|
||||
}
|
||||
pre[class*="language-"][data-line] {
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
pre[data-line] code {
|
||||
position: relative;
|
||||
padding-left: 4em;
|
||||
}
|
||||
pre .line-highlight {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
@@ -8,131 +8,183 @@ FakeModal.prototype.show = function () { alert('open fake modal') }
|
||||
|
||||
/* Common mixin functions used in apps */
|
||||
const commonMixins = {
|
||||
data() {
|
||||
return {
|
||||
loading: 0,
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: 0,
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
getFileSize: function (bytes) {
|
||||
var i = Math.floor(Math.log(bytes) / Math.log(1024));
|
||||
return (bytes / Math.pow(1024, i)).toFixed(1) * 1 + ' ' + ['B', 'kB', 'MB', 'GB', 'TB'][i];
|
||||
},
|
||||
methods: {
|
||||
getFileSize: function (bytes) {
|
||||
var i = Math.floor(Math.log(bytes) / Math.log(1024));
|
||||
return (bytes / Math.pow(1024, i)).toFixed(1) * 1 + ' ' + ['B', 'kB', 'MB', 'GB', 'TB'][i];
|
||||
},
|
||||
|
||||
formatNumber: function (nr) {
|
||||
return new Intl.NumberFormat().format(nr);
|
||||
},
|
||||
formatNumber: function (nr) {
|
||||
return new Intl.NumberFormat().format(nr);
|
||||
},
|
||||
|
||||
// Ajax error message
|
||||
handleError: function (error) {
|
||||
// handle error
|
||||
if (error.response) {
|
||||
// The request was made and the server responded with a status code
|
||||
// that falls out of the range of 2xx
|
||||
if (error.response.data.Error) {
|
||||
alert(error.response.data.Error)
|
||||
} else {
|
||||
alert(error.response.data);
|
||||
}
|
||||
} else if (error.request) {
|
||||
// The request was made but no response was received
|
||||
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
|
||||
// http.ClientRequest in node.js
|
||||
alert('Error sending data to the server. Please try again.');
|
||||
} else {
|
||||
// Something happened in setting up the request that triggered an Error
|
||||
alert(error.message);
|
||||
}
|
||||
},
|
||||
// Ajax error message
|
||||
handleError: function (error) {
|
||||
// handle error
|
||||
if (error.response && error.response.data) {
|
||||
// The request was made and the server responded with a status code
|
||||
// that falls out of the range of 2xx
|
||||
if (error.response.data.Error) {
|
||||
alert(error.response.data.Error)
|
||||
} else {
|
||||
alert(error.response.data);
|
||||
}
|
||||
} else if (error.request) {
|
||||
// The request was made but no response was received
|
||||
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
|
||||
// http.ClientRequest in node.js
|
||||
alert('Error sending data to the server. Please try again.');
|
||||
} else {
|
||||
// Something happened in setting up the request that triggered an Error
|
||||
alert(error.message);
|
||||
}
|
||||
},
|
||||
|
||||
// generic modal get/set function
|
||||
modal: function (id) {
|
||||
let e = document.getElementById(id);
|
||||
if (e) {
|
||||
return bootstrap.Modal.getOrCreateInstance(e);
|
||||
}
|
||||
// in case there are open/close actions
|
||||
return new FakeModal();
|
||||
},
|
||||
// generic modal get/set function
|
||||
modal: function (id) {
|
||||
let e = document.getElementById(id);
|
||||
if (e) {
|
||||
return bootstrap.Modal.getOrCreateInstance(e);
|
||||
}
|
||||
// in case there are open/close actions
|
||||
return new FakeModal();
|
||||
},
|
||||
|
||||
// generic modal get/set function
|
||||
offcanvas: function (id) {
|
||||
var e = document.getElementById(id);
|
||||
if (e) {
|
||||
return bootstrap.Offcanvas.getOrCreateInstance(e);
|
||||
}
|
||||
// in case there are open/close actions
|
||||
return new FakeModal();
|
||||
},
|
||||
// generic modal get/set function
|
||||
offcanvas: function (id) {
|
||||
var e = document.getElementById(id);
|
||||
if (e) {
|
||||
return bootstrap.Offcanvas.getOrCreateInstance(e);
|
||||
}
|
||||
// in case there are open/close actions
|
||||
return new FakeModal();
|
||||
},
|
||||
|
||||
/**
|
||||
* Axios GET request
|
||||
*
|
||||
* @params string url
|
||||
* @params array array parameters Object/array
|
||||
* @params function callback function
|
||||
*/
|
||||
get: function (url, values, callback) {
|
||||
let self = this;
|
||||
self.loading++;
|
||||
axios.get(url, { params: values })
|
||||
.then(callback)
|
||||
.catch(self.handleError)
|
||||
.then(function () {
|
||||
// always executed
|
||||
if (self.loading > 0) {
|
||||
self.loading--;
|
||||
}
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Axios GET request
|
||||
*
|
||||
* @params string url
|
||||
* @params array array parameters Object/array
|
||||
* @params function callback function
|
||||
*/
|
||||
get: function (url, values, callback) {
|
||||
let self = this;
|
||||
self.loading++;
|
||||
axios.get(url, { params: values })
|
||||
.then(callback)
|
||||
.catch(self.handleError)
|
||||
.then(function () {
|
||||
// always executed
|
||||
if (self.loading > 0) {
|
||||
self.loading--;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Axios Post request
|
||||
*
|
||||
* @params string url
|
||||
* @params array array parameters Object/array
|
||||
* @params function callback function
|
||||
*/
|
||||
post: function (url, values, callback) {
|
||||
let self = this;
|
||||
const params = new URLSearchParams();
|
||||
for (const [key, value] of Object.entries(values)) {
|
||||
params.append(key, value);
|
||||
}
|
||||
self.loading++;
|
||||
axios.post(url, params)
|
||||
.then(callback)
|
||||
.catch(self.handleError)
|
||||
.then(function () {
|
||||
// always executed
|
||||
if (self.loading > 0) {
|
||||
self.loading--;
|
||||
}
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Axios Post request
|
||||
*
|
||||
* @params string url
|
||||
* @params array array parameters Object/array
|
||||
* @params function callback function
|
||||
*/
|
||||
post: function (url, values, callback) {
|
||||
let self = this;
|
||||
self.loading++;
|
||||
axios.post(url, values)
|
||||
.then(callback)
|
||||
.catch(self.handleError)
|
||||
.then(function () {
|
||||
// always executed
|
||||
if (self.loading > 0) {
|
||||
self.loading--;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Axios DELETE request (REST only)
|
||||
*
|
||||
* @params string url
|
||||
* @params array array parameters Object/array
|
||||
* @params function callback function
|
||||
*/
|
||||
delete: function (url, values, callback) {
|
||||
let self = this;
|
||||
self.loading++;
|
||||
axios.delete(url, { data: values })
|
||||
.then(callback)
|
||||
.catch(self.handleError)
|
||||
.then(function () {
|
||||
// always executed
|
||||
if (self.loading > 0) {
|
||||
self.loading--;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Axios DELETE request (REST only)
|
||||
*
|
||||
* @params string url
|
||||
* @params array array parameters Object/array
|
||||
* @params function callback function
|
||||
*/
|
||||
delete: function (url, values, callback) {
|
||||
let self = this;
|
||||
self.loading++;
|
||||
axios.delete(url, { data: values })
|
||||
.then(callback)
|
||||
.catch(self.handleError)
|
||||
.then(function () {
|
||||
// always executed
|
||||
if (self.loading > 0) {
|
||||
self.loading--;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
allAttachments: function (message) {
|
||||
let a = [];
|
||||
for (let i in message.Attachments) {
|
||||
a.push(message.Attachments[i]);
|
||||
}
|
||||
for (let i in message.OtherParts) {
|
||||
a.push(message.OtherParts[i]);
|
||||
}
|
||||
for (let i in message.Inline) {
|
||||
a.push(message.Inline[i]);
|
||||
}
|
||||
|
||||
return a.length ? a : false;
|
||||
},
|
||||
|
||||
isImage(a) {
|
||||
return a.ContentType.match(/^image\//);
|
||||
},
|
||||
|
||||
attachmentIcon: function (a) {
|
||||
let ext = a.FileName.split('.').pop().toLowerCase();
|
||||
|
||||
if (a.ContentType.match(/^image\//)) {
|
||||
return 'bi-file-image-fill';
|
||||
}
|
||||
if (a.ContentType.match(/\/pdf$/) || ext == 'pdf') {
|
||||
return 'bi-file-pdf-fill';
|
||||
}
|
||||
if (['doc', 'docx', 'odt', 'rtf'].includes(ext)) {
|
||||
return 'bi-file-word-fill';
|
||||
}
|
||||
if (['xls', 'xlsx', 'ods'].includes(ext)) {
|
||||
return 'bi-file-spreadsheet-fill';
|
||||
}
|
||||
if (['ppt', 'pptx', 'key', 'ppt', 'odp'].includes(ext)) {
|
||||
return 'bi-file-slides-fill';
|
||||
}
|
||||
if (['zip', 'tar', 'rar', 'bz2', 'gz', 'xz'].includes(ext)) {
|
||||
return 'bi-file-zip-fill';
|
||||
}
|
||||
if (a.ContentType.match(/^audio\//)) {
|
||||
return 'bi-file-music-fill';
|
||||
}
|
||||
if (a.ContentType.match(/^video\//)) {
|
||||
return 'bi-file-play-fill';
|
||||
}
|
||||
if (a.ContentType.match(/\/calendar$/)) {
|
||||
return 'bi-file-check-fill';
|
||||
}
|
||||
if (a.ContentType.match(/^text\//) || ['txt', 'sh', 'log'].includes(ext)) {
|
||||
return 'bi-file-text-fill';
|
||||
}
|
||||
|
||||
return 'bi-file-arrow-down-fill';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
37
server/ui-src/templates/Attachments.vue
Normal file
37
server/ui-src/templates/Attachments.vue
Normal file
@@ -0,0 +1,37 @@
|
||||
|
||||
<script>
|
||||
import commonMixins from '../mixins.js';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
message: Object,
|
||||
attachments: Object
|
||||
},
|
||||
|
||||
mixins: [commonMixins]
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="mt-4 border-top pt-4">
|
||||
<a v-for="part in attachments" :href="'api/'+message.ID+'/part/'+part.PartID" class="card attachment float-start me-3 mb-3" target="_blank" style="width: 180px">
|
||||
<img v-if="isImage(part)" :src="'api/'+message.ID+'/part/'+part.PartID+'/thumb'" class="card-img-top" alt="">
|
||||
<img v-else src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALQAAAB4AQMAAABhKUq+AAAAA1BMVEX///+nxBvIAAAAGUlEQVQYGe3BgQAAAADDoPtTT+EA1QAAgFsLQAAB12s2WgAAAABJRU5ErkJggg==" class="card-img-top" alt="">
|
||||
<div class="icon" v-if="!isImage(part)">
|
||||
<i class="bi" :class="attachmentIcon(part)"></i>
|
||||
</div>
|
||||
<div class="card-body border-0">
|
||||
<p class="mb-1 text-muted">
|
||||
<i class="bi me-1" :class="attachmentIcon(part)"></i>
|
||||
<small>{{ getFileSize(part.Size) }}</small>
|
||||
</p>
|
||||
<p class="card-text mb-0 small">
|
||||
{{ part.FileName != '' ? part.FileName : '[ unknown ]' }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="card-footer small border-0 text-center text-truncate">
|
||||
{{ part.FileName != '' ? part.FileName : '[ unknown ]' }}
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user