Compare commits
2 commits
9f873e0257
...
986d3b58ea
Author | SHA1 | Date | |
---|---|---|---|
![]() |
986d3b58ea | ||
![]() |
263eab2d9c |
11 changed files with 600 additions and 358 deletions
524
Cargo.lock
generated
524
Cargo.lock
generated
|
@ -19,6 +19,21 @@ dependencies = [
|
|||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "actix-cors"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0346d8c1f762b41b458ed3145eea914966bb9ad20b9be0d6d463b20d45586370"
|
||||
dependencies = [
|
||||
"actix-utils",
|
||||
"actix-web",
|
||||
"derive_more 0.99.20",
|
||||
"futures-util",
|
||||
"log",
|
||||
"once_cell",
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "actix-http"
|
||||
version = "3.11.0"
|
||||
|
@ -144,7 +159,7 @@ dependencies = [
|
|||
"futures-core",
|
||||
"futures-util",
|
||||
"mio",
|
||||
"socket2",
|
||||
"socket2 0.5.10",
|
||||
"tokio",
|
||||
"tracing",
|
||||
]
|
||||
|
@ -206,7 +221,7 @@ dependencies = [
|
|||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"smallvec",
|
||||
"socket2",
|
||||
"socket2 0.5.10",
|
||||
"time",
|
||||
"tracing",
|
||||
"url",
|
||||
|
@ -248,7 +263,6 @@ dependencies = [
|
|||
"cfg-if",
|
||||
"getrandom 0.3.3",
|
||||
"once_cell",
|
||||
"serde",
|
||||
"version_check",
|
||||
"zerocopy",
|
||||
]
|
||||
|
@ -348,6 +362,12 @@ dependencies = [
|
|||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
|
||||
|
||||
[[package]]
|
||||
name = "async-attributes"
|
||||
version = "1.1.2"
|
||||
|
@ -389,7 +409,7 @@ checksum = "bb812ffb58524bdd10860d7d974e2f01cc0950c2438a74ee5ec2e2280c6c4ffa"
|
|||
dependencies = [
|
||||
"async-task",
|
||||
"concurrent-queue",
|
||||
"fastrand",
|
||||
"fastrand 2.3.0",
|
||||
"futures-lite",
|
||||
"pin-project-lite",
|
||||
"slab",
|
||||
|
@ -635,6 +655,12 @@ dependencies = [
|
|||
"alloc-stdlib",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bufstream"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40e38929add23cdf8a366df9b0e088953150724bcbe5fc330b0d8eb3b328eec8"
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.18.1"
|
||||
|
@ -679,6 +705,16 @@ version = "1.0.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
|
||||
|
||||
[[package]]
|
||||
name = "charset"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1f927b07c74ba84c7e5fe4db2baeb3e996ab2688992e39ac68ce3220a677c7e"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"encoding_rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.41"
|
||||
|
@ -970,6 +1006,22 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "email-encoding"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a87260449b06739ee78d6281c68d2a0ff3e3af64a78df63d3a1aeb3c06997c8a"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "email_address"
|
||||
version = "0.2.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e079f19b08ca6239f47f8ba8509c11cf3ea30095831f7fed61441475edd8c449"
|
||||
|
||||
[[package]]
|
||||
name = "encoding_rs"
|
||||
version = "0.8.35"
|
||||
|
@ -1056,6 +1108,15 @@ dependencies = [
|
|||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be"
|
||||
dependencies = [
|
||||
"instant",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.3.0"
|
||||
|
@ -1184,7 +1245,7 @@ version = "2.6.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"fastrand 2.3.0",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"parking",
|
||||
|
@ -1236,12 +1297,17 @@ dependencies = [
|
|||
name = "gbserver"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"actix-cors",
|
||||
"actix-multipart",
|
||||
"actix-web",
|
||||
"chrono",
|
||||
"dotenv",
|
||||
"jmap-client",
|
||||
"imap",
|
||||
"lettre",
|
||||
"log",
|
||||
"mailparse",
|
||||
"minio",
|
||||
"native-tls",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sqlx",
|
||||
|
@ -1414,6 +1480,17 @@ dependencies = [
|
|||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hostname"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"match_cfg",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "0.2.12"
|
||||
|
@ -1436,17 +1513,6 @@ dependencies = [
|
|||
"itoa",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http-body"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"http 0.2.12",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http-body"
|
||||
version = "1.0.1"
|
||||
|
@ -1466,7 +1532,7 @@ dependencies = [
|
|||
"bytes",
|
||||
"futures-core",
|
||||
"http 1.3.1",
|
||||
"http-body 1.0.1",
|
||||
"http-body",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
|
@ -1482,30 +1548,6 @@ version = "1.0.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "0.14.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"h2 0.3.26",
|
||||
"http 0.2.12",
|
||||
"http-body 0.4.6",
|
||||
"httparse",
|
||||
"httpdate",
|
||||
"itoa",
|
||||
"pin-project-lite",
|
||||
"socket2",
|
||||
"tokio",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
"want",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "1.6.0"
|
||||
|
@ -1517,7 +1559,7 @@ dependencies = [
|
|||
"futures-util",
|
||||
"h2 0.4.10",
|
||||
"http 1.3.1",
|
||||
"http-body 1.0.1",
|
||||
"http-body",
|
||||
"httparse",
|
||||
"httpdate",
|
||||
"itoa",
|
||||
|
@ -1527,20 +1569,6 @@ dependencies = [
|
|||
"want",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-rustls"
|
||||
version = "0.24.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"http 0.2.12",
|
||||
"hyper 0.14.32",
|
||||
"rustls 0.21.12",
|
||||
"tokio",
|
||||
"tokio-rustls 0.24.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-tls"
|
||||
version = "0.6.0"
|
||||
|
@ -1549,7 +1577,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0"
|
|||
dependencies = [
|
||||
"bytes",
|
||||
"http-body-util",
|
||||
"hyper 1.6.0",
|
||||
"hyper",
|
||||
"hyper-util",
|
||||
"native-tls",
|
||||
"tokio",
|
||||
|
@ -1569,13 +1597,13 @@ dependencies = [
|
|||
"futures-core",
|
||||
"futures-util",
|
||||
"http 1.3.1",
|
||||
"http-body 1.0.1",
|
||||
"hyper 1.6.0",
|
||||
"http-body",
|
||||
"hyper",
|
||||
"ipnet",
|
||||
"libc",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"socket2",
|
||||
"socket2 0.5.10",
|
||||
"tokio",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
|
@ -1697,6 +1725,16 @@ version = "1.0.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6"
|
||||
dependencies = [
|
||||
"unicode-bidi",
|
||||
"unicode-normalization",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "1.0.3"
|
||||
|
@ -1718,6 +1756,31 @@ dependencies = [
|
|||
"icu_properties",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "imap"
|
||||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c617c55def8c42129e0dd503f11d7ee39d73f5c7e01eff55768b3879ff1d107d"
|
||||
dependencies = [
|
||||
"base64 0.13.1",
|
||||
"bufstream",
|
||||
"chrono",
|
||||
"imap-proto",
|
||||
"lazy_static",
|
||||
"native-tls",
|
||||
"nom 5.1.3",
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "imap-proto"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "16a6def1d5ac8975d70b3fd101d57953fe3278ef2ee5d7816cba54b1d1dfc22f"
|
||||
dependencies = [
|
||||
"nom 5.1.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "impl-more"
|
||||
version = "0.1.9"
|
||||
|
@ -1734,6 +1797,15 @@ dependencies = [
|
|||
"hashbrown 0.15.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "instant"
|
||||
version = "0.1.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipnet"
|
||||
version = "2.11.0"
|
||||
|
@ -1786,28 +1858,6 @@ dependencies = [
|
|||
"syn 2.0.103",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jmap-client"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12c697483ad894a8184d0fd61848e057f86b16642049993b3e6a80c959dbc90a"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"async-stream",
|
||||
"base64 0.13.1",
|
||||
"chrono",
|
||||
"futures-util",
|
||||
"maybe-async",
|
||||
"parking_lot",
|
||||
"reqwest 0.11.27",
|
||||
"rustls 0.22.4",
|
||||
"rustls-pki-types",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tokio",
|
||||
"tokio-tungstenite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jobserver"
|
||||
version = "0.1.33"
|
||||
|
@ -1852,6 +1902,45 @@ dependencies = [
|
|||
"spin",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lettre"
|
||||
version = "0.10.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76bd09637ae3ec7bd605b8e135e757980b3968430ff2b1a4a94fb7769e50166d"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"base64 0.21.7",
|
||||
"email-encoding",
|
||||
"email_address",
|
||||
"fastrand 1.9.0",
|
||||
"futures-io",
|
||||
"futures-util",
|
||||
"hostname",
|
||||
"httpdate",
|
||||
"idna 0.3.0",
|
||||
"mime",
|
||||
"native-tls",
|
||||
"nom 7.1.3",
|
||||
"once_cell",
|
||||
"quoted_printable",
|
||||
"socket2 0.4.10",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lexical-core"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"bitflags 1.3.2",
|
||||
"cfg-if",
|
||||
"ryu",
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.174"
|
||||
|
@ -1924,16 +2013,22 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "maybe-async"
|
||||
version = "0.2.10"
|
||||
name = "mailparse"
|
||||
version = "0.13.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5cf92c10c7e361d6b99666ec1c6f9805b0bea2c3bd8c78dc6fe98ac5bd78db11"
|
||||
checksum = "8cae768a50835557749599277fc59f7c728118724eb34185e8feb633ef266a32"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"charset",
|
||||
"data-encoding",
|
||||
"quoted_printable",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "match_cfg"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4"
|
||||
|
||||
[[package]]
|
||||
name = "md-5"
|
||||
version = "0.10.6"
|
||||
|
@ -1990,7 +2085,7 @@ dependencies = [
|
|||
"hex",
|
||||
"hmac",
|
||||
"http 1.3.1",
|
||||
"hyper 1.6.0",
|
||||
"hyper",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"md5",
|
||||
|
@ -1998,7 +2093,7 @@ dependencies = [
|
|||
"percent-encoding",
|
||||
"rand 0.8.5",
|
||||
"regex",
|
||||
"reqwest 0.12.20",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
|
@ -2053,6 +2148,17 @@ dependencies = [
|
|||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "5.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08959a387a676302eebf4ddbcbc611da04285579f76f88ee0506c63b1a61dd4b"
|
||||
dependencies = [
|
||||
"lexical-core",
|
||||
"memchr",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "7.1.3"
|
||||
|
@ -2272,7 +2378,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066"
|
||||
dependencies = [
|
||||
"atomic-waker",
|
||||
"fastrand",
|
||||
"fastrand 2.3.0",
|
||||
"futures-io",
|
||||
]
|
||||
|
||||
|
@ -2375,6 +2481,12 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quoted_printable"
|
||||
version = "0.4.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a3866219251662ec3b26fc217e3e05bf9c4f84325234dfb96bf0bf840889e49"
|
||||
|
||||
[[package]]
|
||||
name = "r-efi"
|
||||
version = "5.3.0"
|
||||
|
@ -2484,49 +2596,6 @@ version = "0.8.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.11.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62"
|
||||
dependencies = [
|
||||
"base64 0.21.7",
|
||||
"bytes",
|
||||
"encoding_rs",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"h2 0.3.26",
|
||||
"http 0.2.12",
|
||||
"http-body 0.4.6",
|
||||
"hyper 0.14.32",
|
||||
"hyper-rustls",
|
||||
"ipnet",
|
||||
"js-sys",
|
||||
"log",
|
||||
"mime",
|
||||
"once_cell",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"rustls 0.21.12",
|
||||
"rustls-pemfile",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"sync_wrapper 0.1.2",
|
||||
"system-configuration",
|
||||
"tokio",
|
||||
"tokio-rustls 0.24.1",
|
||||
"tokio-util",
|
||||
"tower-service",
|
||||
"url",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"wasm-streams",
|
||||
"web-sys",
|
||||
"webpki-roots 0.25.4",
|
||||
"winreg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.12.20"
|
||||
|
@ -2538,9 +2607,9 @@ dependencies = [
|
|||
"futures-core",
|
||||
"futures-util",
|
||||
"http 1.3.1",
|
||||
"http-body 1.0.1",
|
||||
"http-body",
|
||||
"http-body-util",
|
||||
"hyper 1.6.0",
|
||||
"hyper",
|
||||
"hyper-tls",
|
||||
"hyper-util",
|
||||
"js-sys",
|
||||
|
@ -2552,7 +2621,7 @@ dependencies = [
|
|||
"serde",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"sync_wrapper 1.0.2",
|
||||
"sync_wrapper",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
"tokio-util",
|
||||
|
@ -2634,26 +2703,11 @@ version = "0.21.12"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e"
|
||||
dependencies = [
|
||||
"log",
|
||||
"ring",
|
||||
"rustls-webpki 0.101.7",
|
||||
"rustls-webpki",
|
||||
"sct",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.22.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432"
|
||||
dependencies = [
|
||||
"log",
|
||||
"ring",
|
||||
"rustls-pki-types",
|
||||
"rustls-webpki 0.102.8",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pemfile"
|
||||
version = "1.0.4"
|
||||
|
@ -2682,17 +2736,6 @@ dependencies = [
|
|||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-webpki"
|
||||
version = "0.102.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"rustls-pki-types",
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.21"
|
||||
|
@ -2880,6 +2923,16 @@ version = "1.15.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.4.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.5.10"
|
||||
|
@ -2915,7 +2968,7 @@ version = "0.2.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7bba3a93db0cc4f7bdece8bb09e77e2e785c20bfebf79eb8340ed80708048790"
|
||||
dependencies = [
|
||||
"nom",
|
||||
"nom 7.1.3",
|
||||
"unicode_categories",
|
||||
]
|
||||
|
||||
|
@ -2959,7 +3012,7 @@ dependencies = [
|
|||
"once_cell",
|
||||
"paste",
|
||||
"percent-encoding",
|
||||
"rustls 0.21.12",
|
||||
"rustls",
|
||||
"rustls-pemfile",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
@ -2971,7 +3024,7 @@ dependencies = [
|
|||
"tokio-stream",
|
||||
"tracing",
|
||||
"url",
|
||||
"webpki-roots 0.25.4",
|
||||
"webpki-roots",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3122,6 +3175,12 @@ version = "1.2.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "stringprep"
|
||||
version = "0.1.5"
|
||||
|
@ -3167,12 +3226,6 @@ dependencies = [
|
|||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sync_wrapper"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
|
||||
|
||||
[[package]]
|
||||
name = "sync_wrapper"
|
||||
version = "1.0.2"
|
||||
|
@ -3193,34 +3246,13 @@ dependencies = [
|
|||
"syn 2.0.103",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "system-configuration"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"core-foundation",
|
||||
"system-configuration-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "system-configuration-sys"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.20.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"fastrand 2.3.0",
|
||||
"getrandom 0.3.3",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
|
@ -3325,7 +3357,7 @@ dependencies = [
|
|||
"parking_lot",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"socket2",
|
||||
"socket2 0.5.10",
|
||||
"tokio-macros",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
@ -3351,27 +3383,6 @@ dependencies = [
|
|||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-rustls"
|
||||
version = "0.24.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081"
|
||||
dependencies = [
|
||||
"rustls 0.21.12",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-rustls"
|
||||
version = "0.25.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f"
|
||||
dependencies = [
|
||||
"rustls 0.22.4",
|
||||
"rustls-pki-types",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-stream"
|
||||
version = "0.1.17"
|
||||
|
@ -3383,22 +3394,6 @@ dependencies = [
|
|||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-tungstenite"
|
||||
version = "0.21.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"log",
|
||||
"rustls 0.22.4",
|
||||
"rustls-pki-types",
|
||||
"tokio",
|
||||
"tokio-rustls 0.25.0",
|
||||
"tungstenite",
|
||||
"webpki-roots 0.26.11",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-util"
|
||||
version = "0.7.15"
|
||||
|
@ -3421,7 +3416,7 @@ dependencies = [
|
|||
"futures-core",
|
||||
"futures-util",
|
||||
"pin-project-lite",
|
||||
"sync_wrapper 1.0.2",
|
||||
"sync_wrapper",
|
||||
"tokio",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
|
@ -3437,7 +3432,7 @@ dependencies = [
|
|||
"bytes",
|
||||
"futures-util",
|
||||
"http 1.3.1",
|
||||
"http-body 1.0.1",
|
||||
"http-body",
|
||||
"iri-string",
|
||||
"pin-project-lite",
|
||||
"tower",
|
||||
|
@ -3521,27 +3516,6 @@ version = "0.2.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
||||
|
||||
[[package]]
|
||||
name = "tungstenite"
|
||||
version = "0.21.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"bytes",
|
||||
"data-encoding",
|
||||
"http 1.3.1",
|
||||
"httparse",
|
||||
"log",
|
||||
"rand 0.8.5",
|
||||
"rustls 0.22.4",
|
||||
"rustls-pki-types",
|
||||
"sha1",
|
||||
"thiserror",
|
||||
"url",
|
||||
"utf-8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.18.0"
|
||||
|
@ -3606,7 +3580,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"idna",
|
||||
"idna 1.0.3",
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
|
@ -3616,12 +3590,6 @@ version = "2.1.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
|
||||
|
||||
[[package]]
|
||||
name = "utf-8"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
|
||||
|
||||
[[package]]
|
||||
name = "utf8_iter"
|
||||
version = "1.0.4"
|
||||
|
@ -3788,24 +3756,6 @@ version = "0.25.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1"
|
||||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
version = "0.26.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9"
|
||||
dependencies = [
|
||||
"webpki-roots 1.0.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8782dd5a41a24eed3a4f40b606249b3e236ca61adf1f25ea4d45c73de122b502"
|
||||
dependencies = [
|
||||
"rustls-pki-types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "whoami"
|
||||
version = "1.6.0"
|
||||
|
@ -4118,16 +4068,6 @@ version = "0.53.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
|
||||
|
||||
[[package]]
|
||||
name = "winreg"
|
||||
version = "0.50.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rt"
|
||||
version = "0.39.0"
|
||||
|
|
|
@ -8,12 +8,17 @@ license = "AGPL"
|
|||
repository = "https://alm.pragmatismo.com.br/generalbots/gbserver"
|
||||
|
||||
[dependencies]
|
||||
actix-cors = "0.6"
|
||||
actix-multipart = "0.6"
|
||||
actix-web = "4"
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
dotenv = "0.15"
|
||||
jmap-client = "0.3.2"
|
||||
imap = "2.0"
|
||||
lettre = { version = "0.10", features = ["smtp-transport", "builder", "tokio1", "tokio1-native-tls"] }
|
||||
log = "0.4"
|
||||
mailparse = "0.13"
|
||||
minio = { git = "https://github.com/minio/minio-rs", branch = "master" }
|
||||
native-tls = "0.2"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
sqlx = { version = "0.7", features = ["runtime-tokio-rustls", "postgres"] }
|
||||
|
@ -21,4 +26,4 @@ tempfile = "3"
|
|||
tokio = { version = "1", features = ["full"] }
|
||||
tokio-stream = "0.1.17"
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = { version = "0.3", features = ["fmt"] }
|
||||
tracing-subscriber = { version = "0.3", features = ["fmt"] }
|
||||
|
|
27
src/main.rs
27
src/main.rs
|
@ -1,3 +1,5 @@
|
|||
use actix_cors::Cors;
|
||||
use actix_web::http::header;
|
||||
use actix_web::{web, App, HttpServer};
|
||||
use dotenv::dotenv;
|
||||
use sqlx::PgPool;
|
||||
|
@ -14,27 +16,36 @@ async fn main() -> std::io::Result<()> {
|
|||
dotenv().ok();
|
||||
let config = AppConfig::from_env();
|
||||
|
||||
let db_url = config.database_url();
|
||||
let db = PgPool::connect(&db_url).await.unwrap();
|
||||
let db_url = config.database_url();
|
||||
//let db = PgPool::connect(&db_url).await.unwrap();
|
||||
|
||||
let minio_client = init_minio(&config)
|
||||
.await
|
||||
.expect("Failed to initialize Minio");
|
||||
// let minio_client = init_minio(&config)
|
||||
// .await
|
||||
// .expect("Failed to initialize Minio");
|
||||
|
||||
let app_state = web::Data::new(AppState {
|
||||
db: Some(db.clone()),
|
||||
db: None,
|
||||
config: Some(config.clone()),
|
||||
minio_client: Some(minio_client),
|
||||
minio_client: None,
|
||||
});
|
||||
|
||||
|
||||
// Start HTTP server
|
||||
HttpServer::new(move || {
|
||||
let cors = Cors::default()
|
||||
.allowed_origin("http://localhost:3000") // Your Next.js port
|
||||
.allowed_methods(vec!["GET", "POST", "PUT", "DELETE"])
|
||||
.allowed_headers(vec![header::AUTHORIZATION, header::ACCEPT])
|
||||
.allowed_header(header::CONTENT_TYPE)
|
||||
.max_age(3600);
|
||||
App::new()
|
||||
.wrap(cors)
|
||||
.app_data(app_state.clone())
|
||||
.service(upload_file)
|
||||
.service(list_file)
|
||||
.service(save_click)
|
||||
.service(get_emails)
|
||||
.service(list_emails)
|
||||
.service(send_email)
|
||||
})
|
||||
.bind((config.server.host.clone(), config.server.port))?
|
||||
.run()
|
||||
|
|
2
src/prompts/business/send-proposal-v0.bas
Normal file
2
src/prompts/business/send-proposal-v0.bas
Normal file
|
@ -0,0 +1,2 @@
|
|||
Based on this ${history}, generate the response for
|
||||
${to}, signed by ${user}
|
|
@ -6,20 +6,20 @@ company = QUERY "SELECT Company FROM Opportunities WHERE Id = ${opportunity}"
|
|||
|
||||
doc = FILL template
|
||||
|
||||
# Generate email subject and content based on conversation history
|
||||
' Generate email subject and content based on conversation history
|
||||
subject = REWRITE "Based on this ${history}, generate a subject for a proposal email to ${company}"
|
||||
contents = REWRITE "Based on this ${history}, and ${subject}, generate the e-mail body for ${to}, signed by ${user}, including key points from our proposal"
|
||||
|
||||
# Add proposal to CRM
|
||||
' Add proposal to CRM
|
||||
CALL "/files/upload", ".gbdrive/Proposals/${company}-proposal.docx", doc
|
||||
CALL "/files/permissions", ".gbdrive/Proposals/${company}-proposal.docx", "sales-team", "edit"
|
||||
|
||||
# Record activity in CRM
|
||||
' Record activity in CRM
|
||||
CALL "/crm/activities/create", opportunity, "email_sent", {
|
||||
"subject": subject,
|
||||
"description": "Proposal sent to " + company,
|
||||
"date": NOW()
|
||||
}
|
||||
|
||||
# Send the email
|
||||
' Send the email
|
||||
CALL "/comm/email/send", to, subject, contents, doc
|
||||
|
|
|
@ -55,6 +55,7 @@ if command -v lxc >/dev/null 2>&1; then
|
|||
rm -rf /tmp/* /var/tmp/*
|
||||
|
||||
echo 'Cleaning logs...'
|
||||
rm -rf /opt/gbo/logs/*
|
||||
journalctl --vacuum-time=1d 2>/dev/null || true
|
||||
|
||||
echo 'Cleaning thumbnail cache...'
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
pub mod config;
|
||||
pub mod file;
|
||||
pub mod state;
|
||||
pub mod email;
|
||||
pub mod email;
|
||||
pub mod llm;
|
|
@ -5,11 +5,12 @@ pub struct AppConfig {
|
|||
pub minio: MinioConfig,
|
||||
pub server: ServerConfig,
|
||||
pub database: DatabaseConfig,
|
||||
pub email: EmailConfig,
|
||||
pub ai: AIConfig,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct DatabaseConfig {
|
||||
|
||||
pub username: String,
|
||||
pub password: String,
|
||||
pub server: String,
|
||||
|
@ -32,6 +33,27 @@ pub struct ServerConfig {
|
|||
pub port: u16,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct EmailConfig {
|
||||
pub from: String,
|
||||
pub server: String,
|
||||
pub port: u16,
|
||||
pub username: String,
|
||||
pub password: String,
|
||||
pub reject_unauthorized: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AIConfig {
|
||||
pub image_model: String,
|
||||
pub embedding_model: String,
|
||||
pub instance: String,
|
||||
pub key: String,
|
||||
pub llm_model: String,
|
||||
pub version: String,
|
||||
pub endpoint: String,
|
||||
}
|
||||
|
||||
impl AppConfig {
|
||||
pub fn database_url(&self) -> String {
|
||||
format!(
|
||||
|
@ -66,6 +88,32 @@ impl AppConfig {
|
|||
.unwrap_or(false),
|
||||
bucket: env::var("DRIVE_ORG_PREFIX").unwrap_or_else(|_| "".to_string()),
|
||||
};
|
||||
|
||||
let email = EmailConfig {
|
||||
from: env::var("EMAIL_FROM").expect("EMAIL_FROM not set"),
|
||||
server: env::var("EMAIL_SERVER").expect("EMAIL_SERVER not set"),
|
||||
port: env::var("EMAIL_PORT")
|
||||
.expect("EMAIL_PORT not set")
|
||||
.parse()
|
||||
.expect("EMAIL_PORT must be a number"),
|
||||
username: env::var("EMAIL_USER").expect("EMAIL_USER not set"),
|
||||
password: env::var("EMAIL_PASS").expect("EMAIL_PASS not set"),
|
||||
reject_unauthorized: env::var("EMAIL_REJECT_UNAUTHORIZED")
|
||||
.unwrap_or_else(|_| "false".to_string())
|
||||
.parse()
|
||||
.unwrap_or(false),
|
||||
};
|
||||
|
||||
let ai = AIConfig {
|
||||
image_model: env::var("AI_IMAGE_MODEL").expect("AI_IMAGE_MODEL not set"),
|
||||
embedding_model: env::var("AI_EMBEDDING_MODEL").expect("AI_EMBEDDING_MODEL not set"),
|
||||
instance: env::var("AI_INSTANCE").expect("AI_INSTANCE not set"),
|
||||
key: env::var("AI_KEY").expect("AI_KEY not set"),
|
||||
llm_model: env::var("AI_LLM_MODEL").expect("AI_LLM_MODEL not set"),
|
||||
version: env::var("AI_VERSION").expect("AI_VERSION not set"),
|
||||
endpoint: env::var("AI_ENDPOINT").expect("AI_ENDPOINT not set"),
|
||||
};
|
||||
|
||||
AppConfig {
|
||||
minio,
|
||||
server: ServerConfig {
|
||||
|
@ -76,6 +124,8 @@ impl AppConfig {
|
|||
.unwrap_or(8080),
|
||||
},
|
||||
database,
|
||||
email,
|
||||
ai,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,61 +1,279 @@
|
|||
use actix_web::web;
|
||||
use actix_web::{http::header::ContentType, HttpResponse};
|
||||
use jmap_client::{
|
||||
client::Client,
|
||||
core::query::Filter,
|
||||
email::{self, Property},
|
||||
mailbox::{self, Role},
|
||||
};
|
||||
use crate::services::{config::EmailConfig, state::AppState};
|
||||
use actix_web::error::ErrorInternalServerError;
|
||||
use actix_web::http::header::ContentType;
|
||||
use actix_web::{web, HttpResponse, Result};
|
||||
use lettre::{transport::smtp::authentication::Credentials, Message, SmtpTransport, Transport};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio_stream::StreamExt;
|
||||
use mailparse::{parse_mail, MailHeaderMap}; // Added MailHeaderMap import
|
||||
use imap::types::{Seq};
|
||||
|
||||
use crate::services::state::AppState;
|
||||
|
||||
#[actix_web::post("/emails/list")]
|
||||
pub async fn list_emails() -> Result<web::Json<Vec<email::Email>>, actix_web::Error> {
|
||||
// 1. Authenticate with JMAP server
|
||||
let client = Client::new()
|
||||
.credentials(("test@", ""))
|
||||
.connect("https://mail/jmap/")
|
||||
.await
|
||||
.map_err(|e| actix_web::error::ErrorInternalServerError(e))?;
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct EmailResponse {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub email: String,
|
||||
pub subject: String,
|
||||
pub text: String,
|
||||
date: String,
|
||||
read: bool,
|
||||
labels: Vec<String>,
|
||||
}
|
||||
|
||||
let inbox_id = client
|
||||
.mailbox_query(
|
||||
mailbox::query::Filter::role(Role::Inbox).into(),
|
||||
None::<Vec<_>>,
|
||||
)
|
||||
.await
|
||||
.map_err(|e| actix_web::error::ErrorInternalServerError(e))?
|
||||
.take_ids()
|
||||
.pop()
|
||||
.ok_or_else(|| actix_web::error::ErrorInternalServerError("No inbox found"))?;
|
||||
async fn internal_send_email(config: &EmailConfig, to: &str, subject: &str, body: &str) {
|
||||
let email = Message::builder()
|
||||
.from(config.from.parse().unwrap())
|
||||
.to(to.parse().unwrap())
|
||||
.subject(subject)
|
||||
.body(body.to_string())
|
||||
.unwrap();
|
||||
|
||||
let mut emails = client
|
||||
.email_query(
|
||||
Filter::and([email::query::Filter::in_mailbox(inbox_id)]).into(),
|
||||
[email::query::Comparator::from()].into(),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| actix_web::error::ErrorInternalServerError(e))?;
|
||||
let creds = Credentials::new(config.username.clone(), config.password.clone());
|
||||
|
||||
SmtpTransport::relay(&config.server)
|
||||
.unwrap()
|
||||
.port(config.port)
|
||||
.credentials(creds)
|
||||
.build()
|
||||
.send(&email)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[actix_web::get("/emails/list")]
|
||||
pub async fn list_emails(
|
||||
state: web::Data<AppState>,
|
||||
) -> Result<web::Json<Vec<EmailResponse>>, actix_web::Error> {
|
||||
let _config = state
|
||||
.config
|
||||
.as_ref()
|
||||
.ok_or_else(|| ErrorInternalServerError("Configuration not available"))?;
|
||||
|
||||
// Establish connection
|
||||
let tls = native_tls::TlsConnector::builder().build().map_err(|e| {
|
||||
ErrorInternalServerError(format!("Failed to create TLS connector: {:?}", e))
|
||||
})?;
|
||||
|
||||
let client = imap::connect(
|
||||
(_config.email.server.as_str(), 993),
|
||||
_config.email.server.as_str(),
|
||||
&tls,
|
||||
)
|
||||
.map_err(|e| ErrorInternalServerError(format!("Failed to connect to IMAP: {:?}", e)))?;
|
||||
|
||||
// Login
|
||||
let mut session = client
|
||||
.login(&_config.email.username, &_config.email.password)
|
||||
.map_err(|e| ErrorInternalServerError(format!("Login failed: {:?}", e)))?;
|
||||
|
||||
// Select INBOX
|
||||
session
|
||||
.select("INBOX")
|
||||
.map_err(|e| ErrorInternalServerError(format!("Failed to select INBOX: {:?}", e)))?;
|
||||
|
||||
// Search for all messages
|
||||
let messages = session
|
||||
.search("ALL")
|
||||
.map_err(|e| ErrorInternalServerError(format!("Failed to search emails: {:?}", e)))?;
|
||||
|
||||
let email_ids = emails.take_ids();
|
||||
let mut email_list = Vec::new();
|
||||
|
||||
for email_id in email_ids {
|
||||
if let Some(email) = client
|
||||
.email_get(
|
||||
&email_id,
|
||||
[Property::Subject, Property::Preview, Property::Keywords].into(),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| actix_web::error::ErrorInternalServerError(e))?
|
||||
{
|
||||
email_list.push(email);
|
||||
// Get last 20 messages
|
||||
let recent_messages: Vec<_> = messages.iter().cloned().collect(); // Collect items into a Vec
|
||||
let recent_messages: Vec<Seq> = recent_messages.into_iter().rev().take(20).collect(); // Now you can reverse and take the last 20
|
||||
for seq in recent_messages {
|
||||
// Fetch the entire message (headers + body)
|
||||
let fetch_result = session.fetch(seq.to_string(), "RFC822");
|
||||
let messages = fetch_result
|
||||
.map_err(|e| ErrorInternalServerError(format!("Failed to fetch email: {:?}", e)))?;
|
||||
|
||||
for msg in messages.iter() {
|
||||
let body = msg
|
||||
.body()
|
||||
.ok_or_else(|| ErrorInternalServerError("No body found"))?;
|
||||
|
||||
// Parse the complete email message
|
||||
let parsed = parse_mail(body)
|
||||
.map_err(|e| ErrorInternalServerError(format!("Failed to parse email: {:?}", e)))?;
|
||||
|
||||
// Extract headers
|
||||
let headers = parsed.get_headers();
|
||||
let subject = headers.get_first_value("Subject").unwrap_or_default();
|
||||
let from = headers.get_first_value("From").unwrap_or_default();
|
||||
let date = headers.get_first_value("Date").unwrap_or_default();
|
||||
|
||||
// Extract body text (handles both simple and multipart emails)
|
||||
let body_text = if let Some(body_part) = parsed.subparts.iter().find(|p| p.ctype.mimetype == "text/plain") {
|
||||
body_part.get_body().unwrap_or_default()
|
||||
} else {
|
||||
parsed.get_body().unwrap_or_default()
|
||||
};
|
||||
|
||||
// Create preview
|
||||
let preview = body_text.lines().take(3).collect::<Vec<_>>().join(" ");
|
||||
let preview_truncated = if preview.len() > 150 {
|
||||
format!("{}...", &preview[..150])
|
||||
} else {
|
||||
preview
|
||||
};
|
||||
|
||||
// Parse From field
|
||||
let (from_name, from_email) = parse_from_field(&from);
|
||||
|
||||
email_list.push(EmailResponse {
|
||||
id: seq.to_string(),
|
||||
name: from_name,
|
||||
email: from_email,
|
||||
subject: if subject.is_empty() {
|
||||
"(No Subject)".to_string()
|
||||
} else {
|
||||
subject
|
||||
},
|
||||
text: preview_truncated,
|
||||
date: if date.is_empty() {
|
||||
chrono::Utc::now().format("%Y-%m-%d %H:%M:%S").to_string()
|
||||
} else {
|
||||
date
|
||||
},
|
||||
read: false,
|
||||
labels: Vec::new(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
session
|
||||
.logout()
|
||||
.map_err(|e| ErrorInternalServerError(format!("Failed to logout: {:?}", e)))?;
|
||||
|
||||
Ok(web::Json(email_list))
|
||||
}
|
||||
|
||||
// Helper function to parse From field
|
||||
fn parse_from_field(from: &str) -> (String, String) {
|
||||
if let Some(start) = from.find('<') {
|
||||
if let Some(end) = from.find('>') {
|
||||
let email = from[start+1..end].trim().to_string();
|
||||
let name = from[..start].trim().trim_matches('"').to_string();
|
||||
return (name, email);
|
||||
}
|
||||
}
|
||||
("Unknown".to_string(), from.to_string())
|
||||
}
|
||||
|
||||
// #[actix_web::post("/emails/suggest-answer/{email_id}")]
|
||||
// pub async fn suggest_answer(
|
||||
// path: web::Path<String>,
|
||||
// state: web::Data<AppState>,
|
||||
// ) -> Result<HttpResponse, actix_web::Error> {
|
||||
// let email_id = path.into_inner();
|
||||
// let config = state
|
||||
// .config
|
||||
// .as_ref()
|
||||
// .ok_or_else(|| ErrorInternalServerError("Configuration not available"))?;
|
||||
|
||||
// // let mut session = create_imap_session(&config.email).await?;
|
||||
|
||||
// // session
|
||||
// // .select("INBOX")
|
||||
// // .await
|
||||
// // .map_err(|e| ErrorInternalServerError(format!("Failed to select INBOX: {:?}", e)))?;
|
||||
|
||||
// // let messages = session
|
||||
// // .fetch(&email_id, "RFC822.HEADER BODY[TEXT]")
|
||||
// // .await
|
||||
// // .map_err(|e| ErrorInternalServerError(format!("Failed to fetch email: {:?}", e)))?;
|
||||
|
||||
// // let msg = messages
|
||||
// // .iter()
|
||||
// // .next()
|
||||
// // .ok_or_else(|| actix_web::error::ErrorNotFound("Email not found"))?;
|
||||
|
||||
// // let header = msg
|
||||
// // .header()
|
||||
// // .ok_or_else(|| ErrorInternalServerError("No header found"))?;
|
||||
|
||||
// // let body = msg
|
||||
// // .text()
|
||||
// // .ok_or_else(|| ErrorInternalServerError("No body found"))?;
|
||||
|
||||
// // let header_str = String::from_utf8_lossy(header);
|
||||
// // let mut subject = String::new();
|
||||
// // let mut from_info = String::new();
|
||||
|
||||
// // for line in header_str.lines() {
|
||||
// // if line.starts_with("Subject: ") {
|
||||
// // subject = line.strip_prefix("Subject: ").unwrap_or("").to_string();
|
||||
// // } else if line.starts_with("From: ") {
|
||||
// // from_info = line.strip_prefix("From: ").unwrap_or("").to_string();
|
||||
// // }
|
||||
// // }
|
||||
|
||||
// // let body_text = String::from_utf8_lossy(body);
|
||||
|
||||
// // let response = serde_json::json!({
|
||||
// // "suggested_response": "Thank you for your email. I will review this and get back to you shortly.",
|
||||
// // "prompt": format!(
|
||||
// // "Email from: {}\nSubject: {}\n\nBody:\n{}\n\n---\n\nPlease draft a professional response to this email.",
|
||||
// // from_info, subject, body_text.lines().take(20).collect::<Vec<_>>().join("\n")
|
||||
// // )
|
||||
// // });
|
||||
|
||||
// // session.logout().await.ok();
|
||||
// //Ok(HttpResponse::Ok().json(response))
|
||||
// }
|
||||
|
||||
// #[actix_web::post("/emails/archive/{email_id}")]
|
||||
// pub async fn archive_email(
|
||||
// path: web::Path<String>,
|
||||
// state: web::Data<AppState>,
|
||||
// ) -> Result<HttpResponse, actix_web::Error> {
|
||||
// let email_id = path.into_inner();
|
||||
// let config = state
|
||||
// .config
|
||||
// .as_ref()
|
||||
// .ok_or_else(|| ErrorInternalServerError("Configuration not available"))?;
|
||||
|
||||
// let mut session = create_imap_session(&config.email).await?;
|
||||
|
||||
// session
|
||||
// .select("INBOX")
|
||||
// .await
|
||||
// .map_err(|e| ErrorInternalServerError(format!("Failed to select INBOX: {:?}", e)))?;
|
||||
|
||||
// // Create Archive folder if it doesn't exist
|
||||
// session.create("Archive").await.ok(); // Ignore error if folder exists
|
||||
|
||||
// // Move email to Archive folder
|
||||
// session.mv(&email_id, "Archive").await.map_err(|e| {
|
||||
// ErrorInternalServerError(format!("Failed to move email to archive: {:?}", e))
|
||||
// })?;
|
||||
|
||||
// session.logout().await.ok();
|
||||
|
||||
// Ok(HttpResponse::Ok().json(serde_json::json!({
|
||||
// "message": "Email archived successfully",
|
||||
// "email_id": email_id,
|
||||
// "archive_folder": "Archive"
|
||||
// })))
|
||||
// }
|
||||
|
||||
#[actix_web::post("/emails/send")]
|
||||
pub async fn send_email(
|
||||
payload: web::Json<(String, String, String)>,
|
||||
state: web::Data<AppState>,
|
||||
) -> Result<HttpResponse, actix_web::Error> {
|
||||
let (to, subject, body) = payload.into_inner();
|
||||
|
||||
println!("To: {}", to);
|
||||
println!("Subject: {}", subject);
|
||||
println!("Body: {}", body);
|
||||
|
||||
// Send via SMTP
|
||||
internal_send_email(&state.config.clone().unwrap().email, &to, &subject, &body).await;
|
||||
|
||||
Ok(HttpResponse::Ok().finish())
|
||||
}
|
||||
|
||||
#[actix_web::get("/campaigns/{campaign_id}/click/{email}")]
|
||||
pub async fn save_click(
|
||||
path: web::Path<(String, String)>,
|
||||
|
|
15
src/services/llm.rs
Normal file
15
src/services/llm.rs
Normal file
|
@ -0,0 +1,15 @@
|
|||
use actix_web::http::Error;
|
||||
|
||||
|
||||
// You'll need to add this to your AppState
|
||||
pub struct LLM {
|
||||
// Your AI client implementation
|
||||
}
|
||||
|
||||
impl LLM {
|
||||
pub async fn generate_response(&self, prompt: &str) -> Result<String, Error> {
|
||||
// Implement your AI service call here
|
||||
Ok("Suggested response".to_string())
|
||||
}
|
||||
}
|
||||
|
|
@ -42,7 +42,6 @@ sudo apt install gnome-tweaks
|
|||
QT_IM_MODULE=cedilla
|
||||
|
||||
"
|
||||
|
||||
|
||||
sudo iptables -t nat -A PREROUTING -p tcp --dport 3389 -j DNAT --to-destination $CONTAINER_IP:3389
|
||||
sudo iptables -A FORWARD -p tcp -d $CONTAINER_IP --dport 3389 -j ACCEPT
|
||||
port=3389
|
||||
lxc config device remove "$PARAM_TENANT"-desktop "port-$port" 2>/dev/null || true
|
||||
lxc config device add "$PARAM_TENANT"-desktop "port-$port" proxy listen=tcp:0.0.0.0:$port connect=tcp:127.0.0.1:$port
|
||||
|
|
Loading…
Add table
Reference in a new issue