From 818dbc37e0cfeb14d3841243675246fb1a0b7f9c Mon Sep 17 00:00:00 2001 From: zhaorunqi Date: Thu, 7 Apr 2022 10:12:29 +0800 Subject: [PATCH] =?UTF-8?q?=E9=82=AE=E7=AE=B1=E5=88=97=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mail2list/db/mail_list.sql | 24 +- mail2list/mail2list_backend/Cargo.toml | 3 +- .../mail2list_archive_unsubscribe/Cargo.toml | 61 ++++ .../application.yml | 36 +++ .../auth_config/rbac_with_domains_model.conf | 15 + .../src/config/config.rs | 57 ++++ .../src/config/log.rs | 82 ++++++ .../src/config/mod.rs | 1 + .../archive_mail_list_controller.rs | 9 + .../src/controller/mod.rs | 2 + .../subscribe_mail_list_controller.rs | 5 + .../src/dao/interceptor.rs | 21 ++ .../src/dao/mapper.rs | 0 .../src/dao/mod.rs | 27 ++ .../src/dto/archive_mail_list_dto.rs | 63 ++++ .../src/dto/mod.rs | 14 + .../src/dto/subscribe_mail_list_dto.rs | 39 +++ .../src/entity/mod.rs | 1 + .../src/entity/sys_entitys.rs | 51 ++++ .../mail2list_archive_unsubscribe/src/lib.rs | 34 +++ .../mail2list_archive_unsubscribe/src/main.rs | 24 ++ .../src/request/mod.rs | 11 + .../src/request/request_model.rs | 11 + .../src/service/archive_mail_list_service.rs | 274 ++++++++++++++++++ .../src/service/crud_service.rs | 117 ++++++++ .../src/service/mod.rs | 22 ++ .../service/subscribe_mail_list_service.rs | 82 ++++++ .../mail2list_common/src/utils/mod.rs | 1 - .../src/utils/receive_mail.rs | 6 +- .../mail2list_common/src/utils/send_email.rs | 2 +- .../mail2list_web/Cargo.toml | 10 +- .../mail2list_web/application.yml | 19 +- .../mail2list_web/src/config/config.rs | 15 + .../archive_mail_list_controller.rs | 35 +++ .../mail2list_web/src/controller/mod.rs | 4 +- .../subscribe_mail_list_controller.rs | 44 +++ .../src/dto/archive_mail_list_dto.rs | 59 ++++ .../mail2list_web/src/dto/mod.rs | 2 + .../src/dto/subscribe_mail_list_dto.rs | 39 +++ .../mail2list_web/src/entity/sys_entitys.rs | 41 +++ .../mail2list_web/src/main.rs | 15 +- .../src/request/request_model.rs | 10 + .../mail2list_web/src/routers/maillist.rs | 7 + .../src/service/archive_mail_list_service.rs | 45 +++ .../mail2list_web/src/service/crud_service.rs | 53 ++-- .../mail2list_web/src/service/mod.rs | 8 + .../service/subscribe_mail_list_service.rs | 163 +++++++++++ .../src/assets/language/en-US.js | 1 + .../src/assets/language/zh-CN.js | 1 + .../src/components/compon/Footer.vue | 2 +- .../src/components/pages/archive/index.scss | 98 +++++++ .../src/components/pages/archive/index.vue | 167 +++++++++++ .../src/components/pages/mailList/index.scss | 49 ++-- .../src/components/pages/mailList/index.vue | 233 ++++++++++++--- mail2list/vue-mail2list-web/src/main.js | 2 +- .../vue-mail2list-web/src/router/index.js | 5 + 56 files changed, 2109 insertions(+), 113 deletions(-) create mode 100644 mail2list/mail2list_backend/mail2list_archive_unsubscribe/Cargo.toml create mode 100644 mail2list/mail2list_backend/mail2list_archive_unsubscribe/application.yml create mode 100644 mail2list/mail2list_backend/mail2list_archive_unsubscribe/auth_config/rbac_with_domains_model.conf create mode 100644 mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/config/config.rs create mode 100644 mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/config/log.rs create mode 100644 mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/config/mod.rs create mode 100644 mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/controller/archive_mail_list_controller.rs create mode 100644 mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/controller/mod.rs create mode 100644 mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/controller/subscribe_mail_list_controller.rs create mode 100644 mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/dao/interceptor.rs create mode 100644 mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/dao/mapper.rs create mode 100644 mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/dao/mod.rs create mode 100644 mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/dto/archive_mail_list_dto.rs create mode 100644 mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/dto/mod.rs create mode 100644 mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/dto/subscribe_mail_list_dto.rs create mode 100644 mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/entity/mod.rs create mode 100644 mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/entity/sys_entitys.rs create mode 100644 mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/lib.rs create mode 100644 mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/main.rs create mode 100644 mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/request/mod.rs create mode 100644 mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/request/request_model.rs create mode 100644 mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/service/archive_mail_list_service.rs create mode 100644 mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/service/crud_service.rs create mode 100644 mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/service/mod.rs create mode 100644 mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/service/subscribe_mail_list_service.rs create mode 100644 mail2list/mail2list_backend/mail2list_web/src/controller/archive_mail_list_controller.rs create mode 100644 mail2list/mail2list_backend/mail2list_web/src/controller/subscribe_mail_list_controller.rs create mode 100644 mail2list/mail2list_backend/mail2list_web/src/dto/archive_mail_list_dto.rs create mode 100644 mail2list/mail2list_backend/mail2list_web/src/dto/subscribe_mail_list_dto.rs create mode 100644 mail2list/mail2list_backend/mail2list_web/src/service/archive_mail_list_service.rs create mode 100644 mail2list/mail2list_backend/mail2list_web/src/service/subscribe_mail_list_service.rs create mode 100644 mail2list/vue-mail2list-web/src/components/pages/archive/index.scss create mode 100644 mail2list/vue-mail2list-web/src/components/pages/archive/index.vue diff --git a/mail2list/db/mail_list.sql b/mail2list/db/mail_list.sql index 5a1778ef..0bdefb6f 100644 --- a/mail2list/db/mail_list.sql +++ b/mail2list/db/mail_list.sql @@ -5,13 +5,27 @@ create database mail2list; BEGIN; +CREATE SEQUENCE sq_mail_id START 1; CREATE TABLE mail_list ( - id INT PRIMARY KEY, - name varchar(20) NOT NULL, - email varchar(20), - archive varchar(20), - description varchar(100) + id INT PRIMARY KEY, + name varchar(20) NOT NULL, + email varchar(20), + archive varchar(20), + description varchar(100) +); + +CREATE TABLE "archive_mail_list" ( + id integer NOT NULL DEFAULT nextval('sq_mail_id'), + name character varying, + from_email character varying, + create_time character varying, + subject character varying, + body character varying, + filename character varying, + message_id character varying, + in_reply_to character varying, + reference character varying ); diff --git a/mail2list/mail2list_backend/Cargo.toml b/mail2list/mail2list_backend/Cargo.toml index 8a30dd24..419d1c23 100644 --- a/mail2list/mail2list_backend/Cargo.toml +++ b/mail2list/mail2list_backend/Cargo.toml @@ -2,5 +2,6 @@ members = [ "mail2list_common", "mail2list_web", - "mail2list_clap" + "mail2list_clap", + "mail2list_archive_unsubscribe" ] \ No newline at end of file diff --git a/mail2list/mail2list_backend/mail2list_archive_unsubscribe/Cargo.toml b/mail2list/mail2list_backend/mail2list_archive_unsubscribe/Cargo.toml new file mode 100644 index 00000000..6cf29020 --- /dev/null +++ b/mail2list/mail2list_backend/mail2list_archive_unsubscribe/Cargo.toml @@ -0,0 +1,61 @@ +[package] +name = "mail2list_archive_unsubscribe" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +axum ={version="0.4.8",features = ["multipart"] } +tokio = { version = "1", features = ["full", "fs", "io-util", "sync"] } +tower-http = { version = "0.2.0", features = ["cors"] } +#serde and json +serde_yaml = "0.8" +serde = { version = "1", features = ["derive"] } +serde_json = "1.0" + +chrono = "0.4" + + +rbson = "2.0" +rbatis = "3.0" + +async-std = "1.10" + +#static +lazy_static = "1.4.0" + + +#http client,use rust-tls replace to native tls +reqwest = { version = "0.11", default-features = false, features = ["json", "rustls-tls","blocking"] } + +#async trait +async-trait = "0.1" +#map +indexmap = "1.7.0" + + +#futures +futures = "0.3" +mail2list_common = { path = "../mail2list_common" } + +casbin = { version = "2.0.9", default-features = false, features = ["runtime-async-std", "cached", "logging"] } + +validator = "0.14.0" +validator_derive = "0.14.0" +thread_local = { version = "1.1.3" } +#jwt +jsonwebtoken = "7" + +#log +log = "0.4" +fast_log = { version="1.4", features = ["lz4","zip", "gzip"]} + +percent-encoding = "2.1.0" + +rand = "0.7.3" +imap = "3.0.0-alpha.6" +mailparse = "0.13.8" +lettre = "0.10.0-rc.5" +lettre_email = "0.9" +mime = "0.3.13" \ No newline at end of file diff --git a/mail2list/mail2list_backend/mail2list_archive_unsubscribe/application.yml b/mail2list/mail2list_backend/mail2list_archive_unsubscribe/application.yml new file mode 100644 index 00000000..62cd45b0 --- /dev/null +++ b/mail2list/mail2list_backend/mail2list_archive_unsubscribe/application.yml @@ -0,0 +1,36 @@ +#debug模式,true输出控制台日志,false只输出文件日志.生产环境需要关闭debug,rbatis也需移除"debug_mode" +debug: true +#名称 +server_name: "mail2listWeb" +#server 地址 +server: + host: "0.0.0.0" + port: "9999" +#数据库地址 +database_url: "**********" +#逻辑删除字段 +logic_column: "del" +logic_un_deleted: 0 +logic_deleted: 1 +#日志文件存放目录 +log_dir: "target/logs/" +#日志分割尺寸-单位KB,MB,GB +log_temp_size: "100MB" +#日志打包格式可选“”(空-不压缩)“gzip”(gz压缩包)“zip”(zip压缩包)“lz4”(lz4压缩包(非常快)) +log_pack_compress: "zip" +#日志滚动保留-保留全部: All,按时间保留(秒): KeepTime(i64),按版本保留: KeepNum(i64) +log_rolling_type: "KeepNum(20)" +#日志等级-info,error,warn,debug,trace +log_level: "info" + +email: + mine_email: "**********" + smtp_server: "**********" + password: "**********" + imap_server: "**********" + #一个退订邮箱管理一个社区 + leave_email : "**********" + leave_email_password : "**********" + leave_name: "A-Tune" + leave_smtp_server: "**********" + leave_imap_server: "**********" diff --git a/mail2list/mail2list_backend/mail2list_archive_unsubscribe/auth_config/rbac_with_domains_model.conf b/mail2list/mail2list_backend/mail2list_archive_unsubscribe/auth_config/rbac_with_domains_model.conf new file mode 100644 index 00000000..e8d0bd6e --- /dev/null +++ b/mail2list/mail2list_backend/mail2list_archive_unsubscribe/auth_config/rbac_with_domains_model.conf @@ -0,0 +1,15 @@ +[request_definition] +r = sub, dom, obj, act + +[policy_definition] +p = sub, dom, obj, act + +[role_definition] +g = _, _, _ + +[policy_effect] +e = some(where (p.eft == allow)) + +[matchers] +m = ciciMatch(r.sub,r.obj) || (g(r.sub, p.sub, r.dom) && r.dom == p.dom && keyMatch2(r.obj, p.obj) && r.act == p.act) + diff --git a/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/config/config.rs b/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/config/config.rs new file mode 100644 index 00000000..ac69e524 --- /dev/null +++ b/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/config/config.rs @@ -0,0 +1,57 @@ +#[derive(Debug, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct ServerConfig{ + ///当前服务地址 + pub host: String, + pub port: String, +} + +#[derive(Debug, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct MailConfig{ + pub mine_email : String, + pub password : String, + pub smtp_server : String, + pub imap_server : String, + pub leave_email : String, + pub leave_email_password : String, + pub leave_smtp_server : String, + pub leave_imap_server : String, + pub leave_name : String, +} + +///服务启动配置 +#[derive(Debug, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct ApplicationConfig { + pub debug: bool, + /// 数据库地址 + pub database_url: String, + /// 逻辑删除字段 + pub logic_column: String, + pub logic_un_deleted: i64, + pub logic_deleted: i64, + ///日志目录 "target/logs/" + pub log_dir: String, + /// "100MB" 日志分割尺寸-单位KB,MB,GB + pub log_temp_size: String, + /// 日志打包格式可选“”(空-不压缩)“gzip”(gz压缩包)“zip”(zip压缩包)“lz4”(lz4压缩包(非常快)) + pub log_pack_compress: String, + ///日志滚动配置 保留全部:All,按时间保留:KeepTime(Duration),按版本保留:KeepNum(i64) + pub log_rolling_type: String, + ///日志等级 + pub log_level: String, + //server 配置 + pub server:ServerConfig, + //邮箱收发配置 + pub email:MailConfig, +} + +///默认配置 +impl Default for ApplicationConfig { + fn default() -> Self { + let yml_data = include_str!("../../application.yml"); + //读取配置 + let result: ApplicationConfig = + serde_yaml::from_str(yml_data).expect("配置文件加载失败"); + + result + } +} diff --git a/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/config/log.rs b/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/config/log.rs new file mode 100644 index 00000000..2b875422 --- /dev/null +++ b/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/config/log.rs @@ -0,0 +1,82 @@ +use fast_log::consts::LogSize; +use fast_log::plugin::file_split::{Packer, RollingType}; +use fast_log::plugin::packer::{LZ4Packer, ZipPacker, LogPacker, GZipPacker}; +use std::time::Duration; + +use crate::MAIL2LIST_CONFIG; + + +pub fn init_log() { + //create log dir + std::fs::create_dir_all(&MAIL2LIST_CONFIG.log_dir); + //init fast log + // fast_log::init_split_log( + // &MAIL2LIST_CONFIG.log_dir, + // str_to_temp_size(&MAIL2LIST_CONFIG.log_temp_size), + // str_to_rolling(&MAIL2LIST_CONFIG.log_rolling_type), + // str_to_log_level(&MAIL2LIST_CONFIG.log_level), + // None, + // choose_packer(&MAIL2LIST_CONFIG.log_pack_compress), + // MAIL2LIST_CONFIG.debug, + // ); + if MAIL2LIST_CONFIG.debug == false { + println!("[cassie] release_mode is up! [file_log] open,[console_log] disabled!"); + } +} + +fn choose_packer(packer: &str) -> Box { + match packer { + "lz4" => Box::new(LZ4Packer {}), + "zip" => Box::new(ZipPacker {}), + "gzip" => Box::new(GZipPacker {}), + _ => Box::new(LogPacker {}), + } +} + +fn str_to_temp_size(arg: &str) -> LogSize { + match arg { + arg if arg.ends_with("MB") => { + let end = arg.find("MB").unwrap(); + let num = arg[0..end].to_string(); + LogSize::MB(num.parse::().unwrap()) + } + arg if arg.ends_with("KB") => { + let end = arg.find("KB").unwrap(); + let num = arg[0..end].to_string(); + LogSize::KB(num.parse::().unwrap()) + } + arg if arg.ends_with("GB") => { + let end = arg.find("GB").unwrap(); + let num = arg[0..end].to_string(); + LogSize::GB(num.parse::().unwrap()) + } + _ => LogSize::MB(100), + } +} + +fn str_to_rolling(arg: &str) -> RollingType { + match arg { + arg if arg.starts_with("KeepNum(") => { + let end = arg.find(")").unwrap(); + let num = arg["KeepNum(".len()..end].to_string(); + RollingType::KeepNum(num.parse::().unwrap()) + } + arg if arg.starts_with("KeepTime(") => { + let end = arg.find(")").unwrap(); + let num = arg["KeepTime(".len()..end].to_string(); + RollingType::KeepTime(Duration::from_secs(num.parse::().unwrap())) + } + _ => RollingType::All, + } +} + +fn str_to_log_level(arg: &str) -> log::Level { + return match arg { + "warn" => log::Level::Warn, + "error" => log::Level::Error, + "trace" => log::Level::Trace, + "info" => log::Level::Info, + "debug" => log::Level::Debug, + _ => log::Level::Info, + }; +} \ No newline at end of file diff --git a/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/config/mod.rs b/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/config/mod.rs new file mode 100644 index 00000000..a1059337 --- /dev/null +++ b/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/config/mod.rs @@ -0,0 +1 @@ +pub mod config; \ No newline at end of file diff --git a/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/controller/archive_mail_list_controller.rs b/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/controller/archive_mail_list_controller.rs new file mode 100644 index 00000000..8aab3ab9 --- /dev/null +++ b/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/controller/archive_mail_list_controller.rs @@ -0,0 +1,9 @@ +use crate::CONTEXT; + +pub async fn delete(imap_server: &str, mine_email :&str,smtp_server: &str, password :&str, name :&str) { + CONTEXT.subscribe_mail_list_service.delete(imap_server, mine_email, smtp_server,password, "退订成功","",name).await; +} + +pub async fn save(mine_email :&str,smtp_server: &str, password :&str, name :&str) { + CONTEXT.archive_mail_list_service.save_info(smtp_server, mine_email, password,name).await; +} \ No newline at end of file diff --git a/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/controller/mod.rs b/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/controller/mod.rs new file mode 100644 index 00000000..6fdf400f --- /dev/null +++ b/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/controller/mod.rs @@ -0,0 +1,2 @@ +pub mod subscribe_mail_list_controller; +pub mod archive_mail_list_controller; \ No newline at end of file diff --git a/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/controller/subscribe_mail_list_controller.rs b/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/controller/subscribe_mail_list_controller.rs new file mode 100644 index 00000000..f644eb4e --- /dev/null +++ b/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/controller/subscribe_mail_list_controller.rs @@ -0,0 +1,5 @@ +use crate::CONTEXT; + +pub async fn delete(imap_server: &str, mine_email :&str,smtp_server: &str, password :&str, name :&str) { + CONTEXT.subscribe_mail_list_service.delete(imap_server, mine_email, smtp_server,password, "退订成功","",name).await; +} \ No newline at end of file diff --git a/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/dao/interceptor.rs b/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/dao/interceptor.rs new file mode 100644 index 00000000..757c244a --- /dev/null +++ b/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/dao/interceptor.rs @@ -0,0 +1,21 @@ +use rbatis::plugin::intercept::SqlIntercept; +use rbatis::rbatis::Rbatis; +use rbatis::Error; +use rbson::Bson; + +#[derive(Debug)] +pub struct AgencyInterceptor {} + +impl SqlIntercept for AgencyInterceptor { + fn do_intercept( + &self, + rb: &Rbatis, + sql: &mut String, + args: &mut Vec, + is_prepared_sql: bool, + ) -> Result<(), Error> { + println!("sql:{}", sql.clone()); + println!("args:{:?}", args.clone()); + return Ok(()); + } +} diff --git a/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/dao/mapper.rs b/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/dao/mapper.rs new file mode 100644 index 00000000..e69de29b diff --git a/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/dao/mod.rs b/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/dao/mod.rs new file mode 100644 index 00000000..3607aab2 --- /dev/null +++ b/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/dao/mod.rs @@ -0,0 +1,27 @@ +use crate::MAIL2LIST_CONFIG; +use rbatis::rbatis::Rbatis; + +pub mod interceptor; +use interceptor::*; + +///实例化 rbatis orm 连接池 +pub async fn init_rbatis() -> Rbatis { + let mut rbatis = Rbatis::new(); + + if MAIL2LIST_CONFIG.debug.eq(&false) && rbatis.is_debug_mode() { + panic!( + r#"已使用release模式,但是rbatis仍使用debug模式!请删除 Cargo.toml 中 rbatis的配置 features = ["debug_mode"]"# + ); + } + rbatis.add_sql_intercept(AgencyInterceptor{}); + //连接数据库 + println!("rbatis link database ({})...", MAIL2LIST_CONFIG.database_url.clone()); + rbatis + .link(&MAIL2LIST_CONFIG.database_url) + .await + .expect("rbatis link database fail!"); + println!("rbatis link database success!"); + + return rbatis; +} + diff --git a/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/dto/archive_mail_list_dto.rs b/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/dto/archive_mail_list_dto.rs new file mode 100644 index 00000000..4b085313 --- /dev/null +++ b/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/dto/archive_mail_list_dto.rs @@ -0,0 +1,63 @@ +use serde::{Deserialize, Serialize}; +use crate::entity::sys_entitys::ArchiveMailList; +use validator_derive::Validate; + +#[derive(Clone, Debug, Serialize, Deserialize,Validate)] +pub struct ArchiveMailListDTO { + pub id: Option, + pub name: Option, + #[validate(email)] + pub from_email: Option, + pub create_time: Option, + pub subject: Option, + pub body: Option, + pub message_id: Option, + pub in_reply_to: Option, + pub reference: Option, + pub filename: Option, +} +impl_field_name_method!(ArchiveMailListDTO { + id, + name, + from_email, + create_time, + subject, + body, + message_id, + in_reply_to, + reference, + filename, +}); +impl Into for ArchiveMailListDTO { + fn into(self) -> ArchiveMailList { + ArchiveMailList { + id: self.id, + name: self.name, + from_email: self.from_email, + create_time: self.create_time, + subject: self.subject, + body: self.body, + message_id: self.message_id, + in_reply_to: self.in_reply_to, + reference: self.reference, + filename: self.filename, + } + } +} + +impl From for ArchiveMailListDTO { + fn from(arg: ArchiveMailList) -> Self { + Self { + id: arg.id, + name: arg.name, + from_email: arg.from_email, + create_time: arg.create_time, + subject: arg.subject, + body: arg.body, + message_id: arg.message_id, + in_reply_to: arg.in_reply_to, + reference: arg.reference, + filename: arg.filename, + } + } +} diff --git a/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/dto/mod.rs b/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/dto/mod.rs new file mode 100644 index 00000000..dbfca64b --- /dev/null +++ b/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/dto/mod.rs @@ -0,0 +1,14 @@ + +pub mod subscribe_mail_list_dto; +pub mod archive_mail_list_dto; + +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct EmptyDTO {} + +/// IdDTO +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct IdDTO { + pub id: String, +} diff --git a/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/dto/subscribe_mail_list_dto.rs b/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/dto/subscribe_mail_list_dto.rs new file mode 100644 index 00000000..e8849271 --- /dev/null +++ b/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/dto/subscribe_mail_list_dto.rs @@ -0,0 +1,39 @@ +use serde::{Deserialize, Serialize}; +use crate::entity::sys_entitys::SubscribeMailList; +use validator_derive::Validate; + +#[derive(Clone, Debug, Serialize, Deserialize,Validate)] +pub struct SubscribeMailListDTO { + pub id: Option, + pub name: Option, + #[validate(email)] + pub email: Option, + pub username: Option, +} +impl_field_name_method!(SubscribeMailListDTO { + id, + name, + email, + username +}); +impl Into for SubscribeMailListDTO { + fn into(self) -> SubscribeMailList { + SubscribeMailList { + id: self.id, + name: self.name, + email: self.email, + username: self.username + } + } +} + +impl From for SubscribeMailListDTO { + fn from(arg: SubscribeMailList) -> Self { + Self { + id: arg.id, + name: arg.name, + email: arg.email, + username: arg.username, + } + } +} diff --git a/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/entity/mod.rs b/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/entity/mod.rs new file mode 100644 index 00000000..8aab88cf --- /dev/null +++ b/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/entity/mod.rs @@ -0,0 +1 @@ +pub mod sys_entitys; diff --git a/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/entity/sys_entitys.rs b/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/entity/sys_entitys.rs new file mode 100644 index 00000000..5ae7c061 --- /dev/null +++ b/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/entity/sys_entitys.rs @@ -0,0 +1,51 @@ +#[crud_table(table_name:subscribe_mail_list)] +#[derive(Clone, Debug)] +pub struct SubscribeMailList { + pub id: Option, + pub name: Option, + pub email: Option, + pub username: Option, +} +impl_field_name_method!(SubscribeMailList { + id, + name, + email, + username, +}); + +#[crud_table(table_name:archive_mail_list)] +#[derive(Clone, Debug)] +pub struct ArchiveMailList { + pub id: Option, + pub name: Option, + pub from_email: Option, + pub create_time: Option, + pub subject: Option, + pub body: Option, + pub message_id: Option, + pub in_reply_to: Option, + pub reference:Option, + pub filename: Option, +} +impl_field_name_method!(ArchiveMailList { + id, + name, + from_email, + create_time, + subject, + body, + message_id, + in_reply_to, + reference, + filename, +}); +/** +*struct:CommonField +*desc:所有表的公共字段 CRUD_SERVICE使用 +*author:zhaorunqi +*email:runqi@isrc.iscas.ac.cn +*/ +#[derive(Clone, Debug)] +pub struct CommonField { + pub id: Option, +} \ No newline at end of file diff --git a/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/lib.rs b/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/lib.rs new file mode 100644 index 00000000..6e63c02b --- /dev/null +++ b/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/lib.rs @@ -0,0 +1,34 @@ +#![allow(unused_variables)] //允许未使用的变量 +#![allow(dead_code)] //允许未使用的代码 +#![allow(unused_must_use)] + +#[macro_use] +extern crate lazy_static; +#[macro_use] +extern crate rbatis; + +pub mod config; +pub mod dao; + +pub mod controller; +pub mod dto; +pub mod entity; +pub mod request; +pub mod service; +use rbatis::rbatis::Rbatis; +use request::*; +use std::sync::Arc; +use thread_local::ThreadLocal; + +use crate::{config::config::ApplicationConfig, service::ServiceContext}; +//初始化静态上下文延迟加载 +lazy_static! { + pub static ref RB:Rbatis=async_std::task::block_on(async { crate::dao::init_rbatis().await }); + //环境配置 + pub static ref MAIL2LIST_CONFIG: ApplicationConfig = ApplicationConfig::default(); + //service服务类 + pub static ref CONTEXT: ServiceContext = ServiceContext::default(); + //登录信息透传 + pub static ref REQUEST_CONTEXT: Arc> = + Arc::new(ThreadLocal::default()); +} diff --git a/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/main.rs b/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/main.rs new file mode 100644 index 00000000..c369ac78 --- /dev/null +++ b/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/main.rs @@ -0,0 +1,24 @@ +use mail2list_archive_unsubscribe::{ + controller::subscribe_mail_list_controller, + controller::archive_mail_list_controller, + MAIL2LIST_CONFIG, +}; + + + + +/** + *method:main + *desc:退订以及归档 需要长时间自动访问 因此单独提取出来一个程序 + *author:zhaorunqi + *email:348040933QQ.com + */ +#[tokio::main] +async fn main() { + //此处直接开始监控删除并且一直监控 + //单独提出来一个程序 单独运行 并且如果找到的话记得删除邮件 + loop{ + //subscribe_mail_list_controller::delete(&MAIL2LIST_CONFIG.email.leave_smtp_server,&MAIL2LIST_CONFIG.email.leave_email,&MAIL2LIST_CONFIG.email.leave_smtp_server,&MAIL2LIST_CONFIG.email.leave_email_password, &MAIL2LIST_CONFIG.email.leave_name).await; + archive_mail_list_controller::save(&MAIL2LIST_CONFIG.email.leave_email,&MAIL2LIST_CONFIG.email.leave_smtp_server,&MAIL2LIST_CONFIG.email.leave_email_password, &MAIL2LIST_CONFIG.email.leave_name).await; + } +} diff --git a/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/request/mod.rs b/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/request/mod.rs new file mode 100644 index 00000000..92fd18e8 --- /dev/null +++ b/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/request/mod.rs @@ -0,0 +1,11 @@ +pub mod request_model; +pub use request_model::*; +use serde::{Deserialize, Serialize}; +#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)] +pub struct RequestModel { + pub uid: i64, + pub super_admin:i32, + pub username: String, + pub agency_code: String, + pub product_code: String, +} diff --git a/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/request/request_model.rs b/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/request/request_model.rs new file mode 100644 index 00000000..3ffb7b2d --- /dev/null +++ b/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/request/request_model.rs @@ -0,0 +1,11 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct SubscribeMailListQuery { + pub id: Option, +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct ArchiveMailListQuery { + pub ids: Option>, +} \ No newline at end of file diff --git a/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/service/archive_mail_list_service.rs b/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/service/archive_mail_list_service.rs new file mode 100644 index 00000000..de8aed1d --- /dev/null +++ b/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/service/archive_mail_list_service.rs @@ -0,0 +1,274 @@ +extern crate lettre; +extern crate lettre_email; +extern crate mime; + +use crate::dto::archive_mail_list_dto::ArchiveMailListDTO; +use crate::entity::sys_entitys::{ArchiveMailList, CommonField, SubscribeMailList}; +use crate::request::ArchiveMailListQuery; +use crate::service::crud_service::CrudService; +use crate::RB; +use rbatis::crud::CRUD; +use rbatis::wrapper::Wrapper; + +use imap; +use lettre::transport::smtp::authentication::Credentials; +use lettre::{ + message::{Body,Attachment, MultiPart}, + Message, SmtpTransport, Transport, +}; +use mailparse::*; +use std::io::Write; +use std::fs; + +/** +*struct:ArchiveMailListService +*desc:菜单基础服务 +*author:zhaorunqi +*email:runqi@isrc.iscas.ac.cn +*/ +pub struct ArchiveMailListService {} + +impl ArchiveMailListService { + /** + * 转发以及存储 + */ + pub async fn save_info( + &self, + smtp_server: &str, + mine_email: &str, + password: &str, + name: &str, + ) -> imap::error::Result> { + let client = imap::ClientBuilder::new(smtp_server, 993).native_tls()?; + + // the client we have here is unauthenticated. + // to do anything useful with the e-mails, we need to log in + let mut imap_session = client.login(mine_email, password).map_err(|e| e.0)?; + + // we want to fetch the first email in the INBOX mailbox + let inbox = imap_session.select("INBOX")?; + + // let mut entity: ArchiveMailList = dto.into(); + for i in 0 as u32..inbox.exists { + // fetch message number 1 in this mailbox, along with its RFC822 field. + // RFC 822 dictates the format of the body of e-mails + let messages = imap_session.fetch((inbox.exists - i).to_string(), "RFC822")?; + let message = if let Some(m) = messages.iter().next() { + m + } else { + return Ok(None); + }; + + // extract the message's body + let body = message.body().expect("message did not have a body!"); + let body1 = std::str::from_utf8(body) + .expect("message was not valid utf-8") + .to_string(); + //println!("{}",body1); + + //此处的话增加两次 以此增加发件人 主题 日期 一次增加内容 + let parsed = parse_mail(body).unwrap(); + + //message_id + let message_id = parsed.headers.get_first_value("Message-ID").unwrap(); + println!("{}", message_id); + + //主题 + let subject = parsed.headers.get_first_value("Subject").unwrap(); + println!("{}", subject); + let subject1 = subject.clone(); + let subject2 = subject1.clone(); + + //from + let mail = parsed.headers.get_first_value("From").unwrap(); + let pos = mail.rfind("<").unwrap(); + let (_, lst) = mail.split_at(pos + 1); + let mut from_email = lst.to_string(); + from_email.pop(); + println!("{}", from_email); + let email = from_email.clone(); + + //Date + let date = dateparse(parsed.headers.get_first_value("Date").unwrap().as_str()).unwrap(); + println!("{}", date); + + //body + let mut body = dump(&parsed); + if body.ends_with(">") == true { + let pos = body.find("<").unwrap(); + let (_, body1) = body.split_at(pos); + body = body1.to_string(); + } + let body1 = body.clone(); + let body2 = body.clone(); + + println!("{}", body); + + //filename + //可能存在多个附件 将其名字通过隔一个空格隔开 在前端进行分割拿取 + let mut filename = String::new(); + for subpart in parsed.subparts { + if subpart.get_content_disposition().disposition == DispositionType::Attachment { + // it's an attachment + let filename_temp = subpart + .get_content_disposition() + .params + .get("filename") + .unwrap() + .to_string(); + filename += &filename_temp; + filename += &" ".to_string(); + } + } + println!("{}", filename); + let mut filename1 = Some(filename.clone()); + let filename3 = filename1.clone(); + let temp = filename3.unwrap(); + if temp == "" { + filename1 = None; + } + + let filename2 = filename.clone(); + + let mut archive_mail_list = ArchiveMailList { + name: Some(name.to_string()), + from_email: Some(from_email), + create_time: Some(date.to_string()), + subject: Some(subject1), + message_id: Some(message_id), + in_reply_to: None, + reference: None, + body: Some(body1), + id: None, + filename: filename1, + }; + + self.save(&mut archive_mail_list).await; + + println!("{}",filename2); + + //转发 再进行转发的时候 其他都很好实现 但是对于存在多个附件 此时则需要判断一下 另外 其中要转发给的人也需要设置 + let wrapper = RB.new_wrapper().eq("name", name).ne("email", email); + let detail: Vec = RB.fetch_list_by_wrapper(wrapper).await.unwrap(); + for data in detail { + //找到了每个人邮箱 开始进行发送 + let email = data.email.unwrap(); + let mut multipart = MultiPart::alternative_plain_html( + String::from("Plaintext version of the body"), + String::from(&body2), + ); + if filename2 != "" { + let pos: Vec<&str> = filename2.split(" ").collect(); + for i in pos.into_iter() { + //此处拿到保存在本地的文件 + if i =="" { + break; + } + let mut content = "E:/openEuler/task/test/".to_string(); + content += &i; + let attachment = fs::read(content).unwrap(); + let attachment_body = Body::new(attachment); + let attachment = Attachment::new(i.to_string()) + .body(attachment_body, "application/octet-stream".parse().unwrap()); // build `Attachment` here + multipart = multipart.singlepart(attachment); + } + } + + let email = Message::builder() + .from(mine_email.parse().unwrap()) + .to(email.parse().unwrap()) + .subject(&subject2) + .multipart(multipart) + .unwrap(); + let creds = Credentials::new(mine_email.to_string(), password.to_string()); + + // Open a remote connection to gmail + let mailer = SmtpTransport::relay(smtp_server) + .unwrap() + .credentials(creds) + .build(); + + match mailer.send(&email) { + Ok(_) => println!("Email sent successfully!"), + Err(e) => panic!("Could not send email: {:?}", e), + } + } + + imap_session + .store(format!("{}", message.message), "+FLAGS (\\Deleted)") + .unwrap(); + imap_session.expunge().unwrap(); + } + // be nice to the server and log out + imap_session.logout()?; + + Ok(Some("logout".to_string())) + } +} + +pub fn dump(pm: &mailparse::ParsedMail) -> String { + //接下来是文章内容 文字部分将会存储在body中 进行展示 附件将会保存在云服务器端 + //这里的话我们直接将整个body追加存储在数据中 然后在数据库层面进行判断 + if pm.ctype.mimetype.starts_with("text/") { + let body = pm.get_body().unwrap(); + return body; + } else if pm.ctype.mimetype.starts_with("application/") { + // println!( + // " (Body is binary type {}, {} bytes in length)", + // pm.ctype.mimetype, + // pm.get_body().unwrap().len() + // ); + //此处将文件进行存储 + let filename = pm + .get_content_disposition() + .params + .get("filename") + .unwrap() + .to_string(); + let data = pm.get_body_raw().unwrap(); + let mut path = "E:/openEuler/task/test/".to_string(); + //此处增加一个随机数 避免文件重合 + path += &filename; + let mut file = std::fs::File::create(path).expect("create failed"); + //println!("文件创建成功:{:?}",file); + //写入了文件 + file.write_all(&data).expect("write failed"); + //println!("data written to file"); + //接下来就是存储文件到云端 此处需要服务器 然后通过云服务器地址 展示在前端 此处暂时保留 + } else { + // println!( + // " (Body is binary type {}, {} bytes in length)", + // pm.ctype.mimetype, + // pm.get_body().unwrap().len() + // ); + } + let mut c = 1; + let mut all_body = String::new(); + for s in &pm.subparts { + // println!(">> Subpart {} <<", c); + let body = dump(s); + all_body += &body; + c = c + 1; + } + return all_body; +} + +impl Default for ArchiveMailListService { + fn default() -> Self { + ArchiveMailListService {} + } +} +impl CrudService + for ArchiveMailListService +{ + fn get_wrapper(arg: &ArchiveMailListQuery) -> Wrapper { + let mut wrapper = RB.new_wrapper(); + if let Some(id_list) = &arg.ids { + wrapper = wrapper.r#in(ArchiveMailList::id(), id_list); + } + wrapper + } + fn set_save_common_fields(&self, common: CommonField, data: &mut ArchiveMailList) { + data.id = common.id; + } +} diff --git a/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/service/crud_service.rs b/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/service/crud_service.rs new file mode 100644 index 00000000..a7742e25 --- /dev/null +++ b/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/service/crud_service.rs @@ -0,0 +1,117 @@ +use crate::entity::sys_entitys::CommonField; +use crate::{RB, REQUEST_CONTEXT}; +use async_trait::async_trait; +use mail2list_common::error::Result; +use rbatis::crud::{CRUDTable, Skip, CRUD}; +use rbatis::wrapper::Wrapper; +use serde::de::DeserializeOwned; +use serde::Serialize; +use std::convert::From; +/** + *struct:CrudService + *desc:orm基础CRUD实现 + *author:zhaorunqi + *email:runqi@isrc.iscas.ac.cn + */ +#[async_trait] +pub trait CrudService: Sync + Send +where + Entity: CRUDTable + DeserializeOwned, + Dto: From + Send + Sync + Serialize, + Params: Send + Sync + Serialize, +{ + /** + * 获取查询条件Wrapper + * 子类实现 + */ + fn get_wrapper(arg: &Params) -> Wrapper; + /**设置公共的字段保存方法*/ + fn set_save_common_fields(&self, common: CommonField, data: &mut Entity); + + + /** + * 根据id查询条件查询单个值 + */ + async fn get(&self, id: String) -> Result { + let wrapper = RB.new_wrapper().eq("id", id); + let detail: Entity = RB.fetch_by_wrapper(wrapper).await?; + let vo = Dto::from(detail); + return Ok(vo); + } + + async fn get_email(&self, email: String, name: String) -> Result { + let wrapper = RB.new_wrapper().eq("name", name).eq("email",email); + let detail: Entity = RB.fetch_by_wrapper(wrapper).await?; + let vo = Dto::from(detail); + return Ok(vo); + } + + async fn get_email_archive(&self, email: String, name: String) -> Result { + let wrapper = RB.new_wrapper().eq("name", name).ne("email",email); + let detail: Entity = RB.fetch_by_wrapper(wrapper).await?; + let vo = Dto::from(detail); + return Ok(vo); + } + /** + * 保存实体 + */ + async fn save(&self, data: &mut Entity) -> Result { + /*设置创建人*/ + + let tls = REQUEST_CONTEXT.clone(); + let creator = if let Some(a) = tls.get() { a.uid } else { 0 }; + /*设置公共字段*/ + self.set_save_common_fields( + CommonField { + id: Some(0), + }, + data, + ); + let result = RB.save(data, &[Skip::Column("id")]).await?; + return Ok(0); + } + + // async fn save_archive(&self, subject: String, message_id:String, crate_time:String, from_email:String) -> Result { + // /*设置创建人*/ + + // // let tls = REQUEST_CONTEXT.clone(); + // // let creator = if let Some(a) = tls.get() { a.uid } else { 0 }; + // // /*设置公共字段*/ + // // self.set_save_common_fields( + // // CommonField { + // // id: Some(0), + // // }, + // // data, + // // ); + // let data = ArchiveMailList{ + // subject:Some(subject), + // }; + // let result = RB.save(data, &[Skip::Column("id")]).await?; + // return Ok(0); + // } + + /** + * 批量保存实体 + */ + async fn save_batch(&self, mut list: &Vec) { + RB.save_batch(&mut list, &[Skip::Column("id")]).await; + } + /** + * 删除实体 逻辑删除 + */ + async fn del(&self, id: &String) { + RB.remove_by_column::("id", id).await; + } + /** + * 根据字段实体 + */ + async fn del_by_column(&self, column: &str, column_value: &str) { + RB.remove_by_column::(column, column_value).await; + } + /** + * 批量删除实体 逻辑删除 + */ + async fn del_batch(&self, ids: &Vec) { + RB.remove_batch_by_column::("id", ids).await; + } +} diff --git a/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/service/mod.rs b/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/service/mod.rs new file mode 100644 index 00000000..47acecfb --- /dev/null +++ b/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/service/mod.rs @@ -0,0 +1,22 @@ +pub mod crud_service; +pub mod subscribe_mail_list_service; +pub mod archive_mail_list_service; + + +use self::subscribe_mail_list_service::SubscribeMailListService; +use self::archive_mail_list_service::ArchiveMailListService; + + +pub struct ServiceContext { + pub subscribe_mail_list_service: SubscribeMailListService, + pub archive_mail_list_service: ArchiveMailListService, +} + +impl ServiceContext { + pub fn default() -> Self { + Self { + subscribe_mail_list_service: Default::default(), + archive_mail_list_service: Default::default(), + } + } +} diff --git a/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/service/subscribe_mail_list_service.rs b/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/service/subscribe_mail_list_service.rs new file mode 100644 index 00000000..76260e7b --- /dev/null +++ b/mail2list/mail2list_backend/mail2list_archive_unsubscribe/src/service/subscribe_mail_list_service.rs @@ -0,0 +1,82 @@ +use crate::MAIL2LIST_CONFIG; + +use crate::dto::subscribe_mail_list_dto::SubscribeMailListDTO; +use crate::entity::sys_entitys::{CommonField, SubscribeMailList}; +use crate::request::SubscribeMailListQuery; +use crate::service::crud_service::CrudService; +use crate::RB; +use mail2list_common::utils::send_email::SendMail; +use rbatis::wrapper::Wrapper; +use imap; +use mailparse::*; + +pub struct SubscribeMailListService {} + +impl SubscribeMailListService { + + /** + *退订 + */ + pub async fn delete(&self, imap_server: &str, mine_email :&str,smtp_server: &str, password :&str, subject :&str, body :&str, name :&str) -> imap::error::Result> { + let client = imap::ClientBuilder::new(imap_server, 993).native_tls()?; + let mut imap_session = client + .login(mine_email, password) + .map_err(|e| e.0)?; + imap_session.select("INBOX")?; + let mut i = 1; + loop { + let i1 = i.to_string(); + let messages = imap_session.fetch(i1, "RFC822.HEADER")?; + let message = if let Some(m) = messages.iter().next() { + m + } else { + return Ok(None); + }; + let header = message.header().expect("message did not have a subject!"); + let parsed = parse_mail(header).unwrap(); + let mail = parsed.headers.get_first_value("From").unwrap(); + let pos = mail.rfind("<").unwrap(); + let (_, lst) = mail.split_at(pos + 1); + let mut email = lst.to_string(); + email.pop(); + let email1 = email.clone(); + let flag = self.get_email(email1,name.to_string()).await; + //找到此用户,则删除 + if flag.is_ok() { + let email2 = email.clone(); + SendMail::send_email(&email2,&MAIL2LIST_CONFIG.email.leave_email,&MAIL2LIST_CONFIG.email.leave_smtp_server,&MAIL2LIST_CONFIG.email.leave_email_password,subject,body); + self.del_by_column("email",&email2).await; + imap_session.store(format!("{}", message.message), "+FLAGS (\\Deleted)").unwrap(); + imap_session.expunge().unwrap(); + }else { + imap_session.store(format!("{}", message.message), "+FLAGS (\\Deleted)").unwrap(); + imap_session.expunge().unwrap(); + } + //找不到 则继续遍历 直到遍历完所有邮箱 + i = i + 1; + if email.is_empty() { + break; + } + } + // be nice to the server and log out + imap_session.logout()?; + Ok(Some(true)) + } +} + +impl Default for SubscribeMailListService { + fn default() -> Self { + SubscribeMailListService {} + } +} + +impl CrudService + for SubscribeMailListService +{ + fn get_wrapper(arg: &SubscribeMailListQuery) -> Wrapper { + RB.new_wrapper() + } + fn set_save_common_fields(&self, common: CommonField, data: &mut SubscribeMailList) { + data.id = common.id; + } +} \ No newline at end of file diff --git a/mail2list/mail2list_backend/mail2list_common/src/utils/mod.rs b/mail2list/mail2list_backend/mail2list_common/src/utils/mod.rs index 5b222872..9e9ffc5e 100644 --- a/mail2list/mail2list_backend/mail2list_common/src/utils/mod.rs +++ b/mail2list/mail2list_backend/mail2list_common/src/utils/mod.rs @@ -2,4 +2,3 @@ pub mod bencher; pub mod string; pub mod send_email; pub mod receive_mail; - diff --git a/mail2list/mail2list_backend/mail2list_common/src/utils/receive_mail.rs b/mail2list/mail2list_backend/mail2list_common/src/utils/receive_mail.rs index ce0c8a3b..92c9d674 100644 --- a/mail2list/mail2list_backend/mail2list_common/src/utils/receive_mail.rs +++ b/mail2list/mail2list_backend/mail2list_common/src/utils/receive_mail.rs @@ -6,7 +6,7 @@ use crate::utils::send_email::SendMail; pub struct ReceiveMail {} impl ReceiveMail { - pub fn receive_mail(imap_server: &str, email_receiver: &str, mine_email :&str,smtp_server: &str, password :&str, subject :&str, body :&str) -> imap::error::Result> { + pub fn receive_mail(imap_server: &str, email_receiver: &str, mine_email :&str,smtp_server: &str, password :&str, subject :&str, body :&str) -> imap::error::Result { let client = imap::ClientBuilder::new(imap_server, 993).native_tls()?; // the client we have here is unauthenticated. // to do anything useful with the e-mails, we need to log in @@ -25,7 +25,7 @@ impl ReceiveMail { let message = if let Some(m) = messages.iter().next() { m } else { - return Ok(None); + return Ok(false); }; // extract the message's body let header = message.header().expect("message did not have a subject!"); @@ -41,6 +41,6 @@ impl ReceiveMail { } // be nice to the server and log out imap_session.logout()?; - Ok(Some(flag)) + Ok(flag) } } \ No newline at end of file diff --git a/mail2list/mail2list_backend/mail2list_common/src/utils/send_email.rs b/mail2list/mail2list_backend/mail2list_common/src/utils/send_email.rs index b260eafb..947a4c80 100644 --- a/mail2list/mail2list_backend/mail2list_common/src/utils/send_email.rs +++ b/mail2list/mail2list_backend/mail2list_common/src/utils/send_email.rs @@ -33,7 +33,7 @@ impl SendMail { // Send the email let result = mailer.send(email.into()); if result.is_ok() { - println!("Email sent to "); + print!("Email sent to "); println!("{}", email_receiver.to_string()); } else { println!("Could not send email: {:?}", result); diff --git a/mail2list/mail2list_backend/mail2list_web/Cargo.toml b/mail2list/mail2list_backend/mail2list_web/Cargo.toml index ab469763..ec20363c 100644 --- a/mail2list/mail2list_backend/mail2list_web/Cargo.toml +++ b/mail2list/mail2list_backend/mail2list_web/Cargo.toml @@ -26,11 +26,7 @@ async-std = "1.10" #static lazy_static = "1.4.0" -#redis -redis = { version = "0.21.5", features = ["async-std-comp"] } -#captcha -captcha = "0.0.8" #http client,use rust-tls replace to native tls reqwest = { version = "0.11", default-features = false, features = ["json", "rustls-tls","blocking"] } @@ -56,4 +52,8 @@ jsonwebtoken = "7" log = "0.4" fast_log = { version="1.4", features = ["lz4","zip", "gzip"]} -percent-encoding = "2.1.0" \ No newline at end of file +percent-encoding = "2.1.0" + +rand = "0.7.3" +imap = "3.0.0-alpha.6" +mailparse = "0.13.8" \ No newline at end of file diff --git a/mail2list/mail2list_backend/mail2list_web/application.yml b/mail2list/mail2list_backend/mail2list_web/application.yml index a77c5af1..0dd3912d 100644 --- a/mail2list/mail2list_backend/mail2list_web/application.yml +++ b/mail2list/mail2list_backend/mail2list_web/application.yml @@ -7,7 +7,7 @@ server: host: "0.0.0.0" port: "9999" #数据库地址 -database_url: "postgresql://****:****@**.**.**.**:**/***" +database_url: "********************************" #逻辑删除字段 logic_column: "del" logic_un_deleted: 0 @@ -21,4 +21,19 @@ log_pack_compress: "zip" #日志滚动保留-保留全部: All,按时间保留(秒): KeepTime(i64),按版本保留: KeepNum(i64) log_rolling_type: "KeepNum(20)" #日志等级-info,error,warn,debug,trace -log_level: "info" \ No newline at end of file +log_level: "info" + +email: + mine_email: "********************************" + smtp_server: "********************************" + password: "********************************" + imap_server: "********************************" + #一个退订邮箱管理一个社区 + leave_email : "********************************" + leave_email_password : "********************************" + leave_name: "********************************" + leave_smtp_server: "********************************" + leave_imap_server: "********************************" + + + diff --git a/mail2list/mail2list_backend/mail2list_web/src/config/config.rs b/mail2list/mail2list_backend/mail2list_web/src/config/config.rs index 361f1bad..ac69e524 100644 --- a/mail2list/mail2list_backend/mail2list_web/src/config/config.rs +++ b/mail2list/mail2list_backend/mail2list_web/src/config/config.rs @@ -5,6 +5,19 @@ pub struct ServerConfig{ pub port: String, } +#[derive(Debug, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct MailConfig{ + pub mine_email : String, + pub password : String, + pub smtp_server : String, + pub imap_server : String, + pub leave_email : String, + pub leave_email_password : String, + pub leave_smtp_server : String, + pub leave_imap_server : String, + pub leave_name : String, +} + ///服务启动配置 #[derive(Debug, PartialEq, serde::Serialize, serde::Deserialize)] pub struct ApplicationConfig { @@ -27,6 +40,8 @@ pub struct ApplicationConfig { pub log_level: String, //server 配置 pub server:ServerConfig, + //邮箱收发配置 + pub email:MailConfig, } ///默认配置 diff --git a/mail2list/mail2list_backend/mail2list_web/src/controller/archive_mail_list_controller.rs b/mail2list/mail2list_backend/mail2list_web/src/controller/archive_mail_list_controller.rs new file mode 100644 index 00000000..aacd3beb --- /dev/null +++ b/mail2list/mail2list_backend/mail2list_web/src/controller/archive_mail_list_controller.rs @@ -0,0 +1,35 @@ +use crate::REQUEST_CONTEXT; +use crate::{request::ArchiveMailListQuery,service::crud_service::CrudService, CONTEXT}; +use axum::extract::{Path, Query}; +use axum::response::IntoResponse; + +use mail2list_common::RespVO; +// use validator::Validate; + + +/** + *method:/user/list + *desc:用户查询 + *author:zhaorunqi + */ +pub async fn list(arg: Option>) -> impl IntoResponse { + let arg = arg.unwrap(); + let vo = CONTEXT.archive_mail_list_service.list_archive(&arg).await; + RespVO::from_result(&vo).resp_json() +} +/** + *method:/menu/getList/:id + *desc:查询 + *author:zhaorunqi + */ +pub async fn get_by_id(Path(id): Path) -> impl IntoResponse { + let vo = CONTEXT.archive_mail_list_service.get(id).await; + RespVO::from_result(&vo).resp_json() +} + +pub async fn info() -> impl IntoResponse { + let tls = REQUEST_CONTEXT.clone(); + let uid = if let Some(a) = tls.get() { a.uid } else { 0 }; + let vo = CONTEXT.archive_mail_list_service.get(uid.to_string()).await.unwrap(); + RespVO::from(&vo).resp_json() +} \ No newline at end of file diff --git a/mail2list/mail2list_backend/mail2list_web/src/controller/mod.rs b/mail2list/mail2list_backend/mail2list_web/src/controller/mod.rs index 98d8512d..ccab3b86 100644 --- a/mail2list/mail2list_backend/mail2list_web/src/controller/mod.rs +++ b/mail2list/mail2list_backend/mail2list_web/src/controller/mod.rs @@ -1 +1,3 @@ -pub mod mail_list_controller; \ No newline at end of file +pub mod mail_list_controller; +pub mod subscribe_mail_list_controller; +pub mod archive_mail_list_controller; \ No newline at end of file diff --git a/mail2list/mail2list_backend/mail2list_web/src/controller/subscribe_mail_list_controller.rs b/mail2list/mail2list_backend/mail2list_web/src/controller/subscribe_mail_list_controller.rs new file mode 100644 index 00000000..d46dd7bd --- /dev/null +++ b/mail2list/mail2list_backend/mail2list_web/src/controller/subscribe_mail_list_controller.rs @@ -0,0 +1,44 @@ +use crate::{ + dto::subscribe_mail_list_dto::SubscribeMailListDTO, request::SubscribeMailListQuery, + service::crud_service::CrudService, CONTEXT, +}; +use axum::extract::{Query}; +use axum::response::IntoResponse; +use axum::Json; +use mail2list_common::RespVO; + +/** + *method:/user/list + *desc:保存 + *author:zhaorunqi + */ +// pub async fn save(Json(arg): Json) -> impl IntoResponse { +// let context = CONTAINER.get::(); +// let user = arg; +// if let Err(e) = user.validate() { +// return RespVO::<()>::from_error(&Error::E(e.to_string())).resp_json(); +// } + +// context.sys_user_service.save_info(user).await; + +// return RespVO::from(&"保存成功".to_string()).resp_json(); +// } + +pub async fn list(arg: Option>) -> impl IntoResponse { + let arg = arg.unwrap(); + let vo = CONTEXT.subscribe_mail_list_service.list(&arg).await; + RespVO::from_result(&vo).resp_json() +} + +pub async fn save(Json(arg): Json) -> impl IntoResponse { + let flag = CONTEXT.subscribe_mail_list_service.save_info(arg).await; + if flag == true { + RespVO::from(&"邮件已发送,请查看邮箱并回复".to_string()).resp_json() + } else { + RespVO::from(&"用户已订阅".to_string()).resp_json() + } +} + +pub async fn delete(imap_server: &str, mine_email :&str,smtp_server: &str, password :&str, name :&str) { + CONTEXT.subscribe_mail_list_service.delete(imap_server, mine_email, smtp_server,password, "退订成功","",name).await; +} \ No newline at end of file diff --git a/mail2list/mail2list_backend/mail2list_web/src/dto/archive_mail_list_dto.rs b/mail2list/mail2list_backend/mail2list_web/src/dto/archive_mail_list_dto.rs new file mode 100644 index 00000000..a2d3f756 --- /dev/null +++ b/mail2list/mail2list_backend/mail2list_web/src/dto/archive_mail_list_dto.rs @@ -0,0 +1,59 @@ +use serde::{Deserialize, Serialize}; +use crate::entity::sys_entitys::ArchiveMailList; +use validator_derive::Validate; + +#[derive(Clone, Debug, Serialize, Deserialize,Validate)] +pub struct ArchiveMailListDTO { + pub id: Option, + pub name: Option, + #[validate(email)] + pub from_email: Option, + pub create_time: Option, + pub subject: Option, + pub body: Option, + pub message_id: Option, + pub in_reply_to: Option, + pub reference: Option, +} +impl_field_name_method!(ArchiveMailListDTO { + id, + name, + from_email, + create_time, + subject, + body, + message_id, + in_reply_to, + reference, +}); +impl Into for ArchiveMailListDTO { + fn into(self) -> ArchiveMailList { + ArchiveMailList { + id: self.id, + name: self.name, + from_email: self.from_email, + create_time: self.create_time, + subject: self.subject, + body: self.body, + message_id: self.message_id, + in_reply_to: self.in_reply_to, + reference: self.reference, + } + } +} + +impl From for ArchiveMailListDTO { + fn from(arg: ArchiveMailList) -> Self { + Self { + id: arg.id, + name: arg.name, + from_email: arg.from_email, + create_time: arg.create_time, + subject: arg.subject, + body: arg.body, + message_id: arg.message_id, + in_reply_to: arg.in_reply_to, + reference: arg.reference, + } + } +} diff --git a/mail2list/mail2list_backend/mail2list_web/src/dto/mod.rs b/mail2list/mail2list_backend/mail2list_web/src/dto/mod.rs index fcb47119..30f97823 100644 --- a/mail2list/mail2list_backend/mail2list_web/src/dto/mod.rs +++ b/mail2list/mail2list_backend/mail2list_web/src/dto/mod.rs @@ -1,5 +1,7 @@ pub mod mail_list_dto; +pub mod subscribe_mail_list_dto; +pub mod archive_mail_list_dto; use serde::{Deserialize, Serialize}; diff --git a/mail2list/mail2list_backend/mail2list_web/src/dto/subscribe_mail_list_dto.rs b/mail2list/mail2list_backend/mail2list_web/src/dto/subscribe_mail_list_dto.rs new file mode 100644 index 00000000..e8849271 --- /dev/null +++ b/mail2list/mail2list_backend/mail2list_web/src/dto/subscribe_mail_list_dto.rs @@ -0,0 +1,39 @@ +use serde::{Deserialize, Serialize}; +use crate::entity::sys_entitys::SubscribeMailList; +use validator_derive::Validate; + +#[derive(Clone, Debug, Serialize, Deserialize,Validate)] +pub struct SubscribeMailListDTO { + pub id: Option, + pub name: Option, + #[validate(email)] + pub email: Option, + pub username: Option, +} +impl_field_name_method!(SubscribeMailListDTO { + id, + name, + email, + username +}); +impl Into for SubscribeMailListDTO { + fn into(self) -> SubscribeMailList { + SubscribeMailList { + id: self.id, + name: self.name, + email: self.email, + username: self.username + } + } +} + +impl From for SubscribeMailListDTO { + fn from(arg: SubscribeMailList) -> Self { + Self { + id: arg.id, + name: arg.name, + email: arg.email, + username: arg.username, + } + } +} diff --git a/mail2list/mail2list_backend/mail2list_web/src/entity/sys_entitys.rs b/mail2list/mail2list_backend/mail2list_web/src/entity/sys_entitys.rs index a0f6f60b..892f73e3 100644 --- a/mail2list/mail2list_backend/mail2list_web/src/entity/sys_entitys.rs +++ b/mail2list/mail2list_backend/mail2list_web/src/entity/sys_entitys.rs @@ -27,6 +27,47 @@ impl_field_name_method!(MailList { archive, description }); + + +#[crud_table(table_name:subscribe_mail_list)] +#[derive(Clone, Debug)] +pub struct SubscribeMailList { + pub id: Option, + pub name: Option, + pub email: Option, + pub username: Option, +} +impl_field_name_method!(SubscribeMailList { + id, + name, + email, + username, +}); + +#[crud_table(table_name:archive_mail_list)] +#[derive(Clone, Debug)] +pub struct ArchiveMailList { + pub id: Option, + pub name: Option, + pub from_email: Option, + pub create_time: Option, + pub subject: Option, + pub body: Option, + pub message_id: Option, + pub in_reply_to: Option, + pub reference:Option, +} +impl_field_name_method!(ArchiveMailList { + id, + name, + from_email, + create_time, + subject, + body, + message_id, + in_reply_to, + reference, +}); /** *struct:CommonField *desc:所有表的公共字段 CRUD_SERVICE使用 diff --git a/mail2list/mail2list_backend/mail2list_web/src/main.rs b/mail2list/mail2list_backend/mail2list_web/src/main.rs index 15593e7a..07e7637b 100644 --- a/mail2list/mail2list_backend/mail2list_web/src/main.rs +++ b/mail2list/mail2list_backend/mail2list_web/src/main.rs @@ -1,18 +1,14 @@ use std::time::Duration; -use axum::{ - Router, Server, -}; +use axum::{Router, Server}; +use log::info; use mail2list_web::{ config::log::init_log, routers::{maillist}, MAIL2LIST_CONFIG, }; -use log::info; use tower_http::cors::{Any, CorsLayer}; - - /** *method:main *desc:程序主入口方法 admin 管理端api api:小程序,h5,app使用 @@ -22,6 +18,8 @@ use tower_http::cors::{Any, CorsLayer}; #[tokio::main] async fn main() { init_log(); + //此处直接开始监控删除并且一直监控 + //单独提出来一个程序 单独运行 并且如果找到的话记得删除邮件 info!( " - Local: http://{}:{}", MAIL2LIST_CONFIG.server.host.replace("0.0.0.0", "127.0.0.1"), @@ -40,10 +38,7 @@ async fn main() { //绑定端口 初始化 路由 let app = Router::new() - .nest( - "/maillist", - maillist::routers(), - ) + .nest("/maillist", maillist::routers()) .layer(cors); Server::bind(&server.parse().unwrap()) diff --git a/mail2list/mail2list_backend/mail2list_web/src/request/request_model.rs b/mail2list/mail2list_backend/mail2list_web/src/request/request_model.rs index 45bab33a..61eaf4ed 100644 --- a/mail2list/mail2list_backend/mail2list_web/src/request/request_model.rs +++ b/mail2list/mail2list_backend/mail2list_web/src/request/request_model.rs @@ -11,4 +11,14 @@ pub struct MailListQuery { pub ids: Option>, pub page_no: Option, pub page_size: Option, +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct SubscribeMailListQuery { + pub id: Option, +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct ArchiveMailListQuery { + pub ids: Option>, } \ No newline at end of file diff --git a/mail2list/mail2list_backend/mail2list_web/src/routers/maillist.rs b/mail2list/mail2list_backend/mail2list_web/src/routers/maillist.rs index 858af770..a9bb4fac 100644 --- a/mail2list/mail2list_backend/mail2list_web/src/routers/maillist.rs +++ b/mail2list/mail2list_backend/mail2list_web/src/routers/maillist.rs @@ -1,5 +1,7 @@ use crate::controller::{ mail_list_controller, + subscribe_mail_list_controller, + archive_mail_list_controller, }; use axum::{ routing::{get}, @@ -12,4 +14,9 @@ pub fn routers() -> Router { "/menu/getListById/:id", get(mail_list_controller::get_by_id) ) + .route( + "/menu/subscribe", + get(subscribe_mail_list_controller::list).post(subscribe_mail_list_controller::save) + ) + .route("/archive/list", get(archive_mail_list_controller::list)) } diff --git a/mail2list/mail2list_backend/mail2list_web/src/service/archive_mail_list_service.rs b/mail2list/mail2list_backend/mail2list_web/src/service/archive_mail_list_service.rs new file mode 100644 index 00000000..e6a5a4aa --- /dev/null +++ b/mail2list/mail2list_backend/mail2list_web/src/service/archive_mail_list_service.rs @@ -0,0 +1,45 @@ +use std::collections::HashMap; + +use crate::dto::archive_mail_list_dto::ArchiveMailListDTO; +use crate::entity::sys_entitys::{CommonField, ArchiveMailList}; +use crate::request::ArchiveMailListQuery; +use crate::service::crud_service::CrudService; +use crate::{RB}; +use rbatis::wrapper::Wrapper; + +/** +*struct:ArchiveMailListService +*desc:菜单基础服务 +*author:zhaorunqi +*email:runqi@isrc.iscas.ac.cn +*/ +pub struct ArchiveMailListService {} + +impl ArchiveMailListService { + + fn build(&self, menus: Vec) -> Vec { + let mut result = HashMap::with_capacity(menus.capacity()); + let data = vec![]; + for x in menus { + result.insert(x.id.clone().unwrap_or_default(), x); + } + data + } +} +impl Default for ArchiveMailListService { + fn default() -> Self { + ArchiveMailListService {} + } +} +impl CrudService for ArchiveMailListService { + fn get_wrapper(arg: &ArchiveMailListQuery) -> Wrapper { + let mut wrapper = RB.new_wrapper(); + if let Some(id_list) = &arg.ids { + wrapper = wrapper.r#in(ArchiveMailList::id(), id_list); + } + wrapper + } + fn set_save_common_fields(&self, common: CommonField, data: &mut ArchiveMailList) { + data.id = common.id; + } +} diff --git a/mail2list/mail2list_backend/mail2list_web/src/service/crud_service.rs b/mail2list/mail2list_backend/mail2list_web/src/service/crud_service.rs index e23c1791..0cb0f03d 100644 --- a/mail2list/mail2list_backend/mail2list_web/src/service/crud_service.rs +++ b/mail2list/mail2list_backend/mail2list_web/src/service/crud_service.rs @@ -31,36 +31,24 @@ where fn set_save_common_fields(&self, common: CommonField, data: &mut Entity); /** - * 公共分页查询方法 + * 公共列表查询方法 */ - async fn page(&self, arg: &Params, page: PageData) -> Result> { + async fn list(&self, arg: &Params) -> Result> { //构建查询条件 let wrapper = Self::get_wrapper(arg); - //构建分页条件 - let page_request = - PageRequest::new(page.page_no.unwrap_or(1), page.page_size.unwrap_or(10)); - //执行分页查询 - let data_page: Page = RB.fetch_page_by_wrapper(wrapper, &page_request).await?; - //将Entity实体转换成 Vo对象返回 + //执行查询 + let list: Vec = RB.fetch_list_by_wrapper(wrapper).await?; let mut vos = vec![]; - for x in data_page.records { + //将Entity实体转换成 Vo对象 返回 + for x in list { vos.push(Dto::from(x)); } - Ok(Page:: { - records: vos, - total: data_page.total, - pages: data_page.pages, - page_no: data_page.page_no, - page_size: data_page.page_size, - search_count: data_page.search_count, - }) + Ok(vos) } - /** - * 公共列表查询方法 - */ - async fn list(&self, arg: &Params) -> Result> { + + async fn list_archive(&self, arg: &Params) -> Result> { //构建查询条件 - let wrapper = Self::get_wrapper(arg); + let wrapper = Self::get_wrapper(arg).is_null("in_reply_to").order_by(false,&["create_time"]); //执行查询 let list: Vec = RB.fetch_list_by_wrapper(wrapper).await?; let mut vos = vec![]; @@ -91,6 +79,13 @@ where let vo = Dto::from(detail); return Ok(vo); } + + async fn get_email(&self, email: String, name: String) -> Result { + let wrapper = RB.new_wrapper().eq("name", name).eq("email",email); + let detail: Entity = RB.fetch_by_wrapper(wrapper).await?; + let vo = Dto::from(detail); + return Ok(vo); + } /** * 保存实体 */ @@ -100,13 +95,13 @@ where let tls = REQUEST_CONTEXT.clone(); let creator = if let Some(a) = tls.get() { a.uid } else { 0 }; /*设置公共字段*/ - // self.set_save_common_fields( - // // CommonField { - // // id: Some(0), - // // }, - // data, - // ); - let result = RB.save(data, &[Skip::Column("create_date")]).await?; + self.set_save_common_fields( + CommonField { + id: Some(0), + }, + data, + ); + let result = RB.save(data, &[Skip::Column("id")]).await?; return Ok(0); } /** diff --git a/mail2list/mail2list_backend/mail2list_web/src/service/mod.rs b/mail2list/mail2list_backend/mail2list_web/src/service/mod.rs index 3e6aeb76..f4edc6b9 100644 --- a/mail2list/mail2list_backend/mail2list_web/src/service/mod.rs +++ b/mail2list/mail2list_backend/mail2list_web/src/service/mod.rs @@ -1,17 +1,25 @@ pub mod crud_service; pub mod mail_list_service; +pub mod subscribe_mail_list_service; +pub mod archive_mail_list_service; use self::mail_list_service::MailListService; +use self::subscribe_mail_list_service::SubscribeMailListService; +use self::archive_mail_list_service::ArchiveMailListService; pub struct ServiceContext { pub mail_list_service: MailListService, + pub subscribe_mail_list_service: SubscribeMailListService, + pub archive_mail_list_service: ArchiveMailListService, } impl ServiceContext { pub fn default() -> Self { Self { mail_list_service: Default::default(), + subscribe_mail_list_service: Default::default(), + archive_mail_list_service: Default::default(), } } } diff --git a/mail2list/mail2list_backend/mail2list_web/src/service/subscribe_mail_list_service.rs b/mail2list/mail2list_backend/mail2list_web/src/service/subscribe_mail_list_service.rs new file mode 100644 index 00000000..f314e863 --- /dev/null +++ b/mail2list/mail2list_backend/mail2list_web/src/service/subscribe_mail_list_service.rs @@ -0,0 +1,163 @@ +use crate::MAIL2LIST_CONFIG; + +use crate::dto::subscribe_mail_list_dto::SubscribeMailListDTO; +use crate::entity::sys_entitys::{CommonField, SubscribeMailList}; +use crate::request::SubscribeMailListQuery; +use crate::service::crud_service::CrudService; +use crate::RB; +use mail2list_common::utils::receive_mail::ReceiveMail; +use mail2list_common::utils::send_email::SendMail; +use rand::distributions::Alphanumeric; +use rand::{thread_rng, Rng}; +use rbatis::wrapper::Wrapper; +use imap; +use mailparse::*; + +/** +*struct:SubscribeMailListService +*desc:菜单基础服务 +*author:zhaorunqi +*email:runqi@isrc.iscas.ac.cn +*/ +pub struct SubscribeMailListService {} + +impl SubscribeMailListService { + /** + * 保存用户 + */ + pub async fn save_info(&self, dto: SubscribeMailListDTO) -> bool { + let rand_string: String = thread_rng() + .sample_iter(&Alphanumeric) + .take(30) + .map(char::from) + .collect(); + let email = dto.email.clone(); + let email1 = email.unwrap(); + let email2 = email1.clone(); + let username = dto.username.clone(); + let username1 = "".to_string(); + match username { + Some(username1) => println!("username is true"), + None => println!("username is empty"), + }; + // let username1 = username.unwrap(); + let name = dto.name.clone(); + let name1 = name.unwrap(); + let mut subject = "confirm+".to_string(); + subject += &rand_string; + let mut body = "邮箱订阅确认\n您好,这是openeuler.org的邮件列表服务。\n我们收到来自以下邮箱的注册请求:\n\r".to_string(); + body += &email2; + body += &"\n在开始使用本站的邮件列表服务之前,请确认这是您的邮箱。你可以通过回复此消息来进行确认,请保持回信的主题不变。\n若您不想注册该邮箱,请忽略此消息。若您怀疑自己被恶意订阅了该列表或有其他任何疑问,请联系:\n\r".to_string(); + body += &MAIL2LIST_CONFIG.email.mine_email; + + let mut success_body = "欢迎使用".to_string(); + success_body += &name1; + success_body += &"邮件列表!\n要通过此列表交流,请发送电子邮件至:\n\r".to_string(); + success_body += &MAIL2LIST_CONFIG.email.mine_email; + success_body += &"\n\n取消订阅或调整选项请发送电子邮件至:\n\r".to_string(); + success_body += &MAIL2LIST_CONFIG.email.leave_email; + + let flag = self.get_email(email2, name1).await; + if flag.is_err() { + let mut entity: SubscribeMailList = dto.into(); + //发送邮箱 + SendMail::send_email( + email1.as_str(), + MAIL2LIST_CONFIG.email.mine_email.as_str(), + MAIL2LIST_CONFIG.email.smtp_server.as_str(), + MAIL2LIST_CONFIG.email.password.as_str(), + subject.as_str(), + body.as_str(), + ); + //接收邮件 此处要判断是否接收到 要是没有收到则一直等待 直到收到 此时我们可以启动其他服务 + loop { + let flag = ReceiveMail::receive_mail( + MAIL2LIST_CONFIG.email.imap_server.as_str(), + email1.as_str(), + MAIL2LIST_CONFIG.email.mine_email.as_str(), + MAIL2LIST_CONFIG.email.smtp_server.as_str(), + MAIL2LIST_CONFIG.email.password.as_str(), + subject.as_str(), + success_body.as_str(), + ); + match flag { + Ok(flag) => { + if flag == true { + break; + } + } + Err(err) => panic!("Problem receive the mail: {:?}", err), + }; + } + let id = self.save(&mut entity).await; + // id.unwrap() + return true; + } + + return false; + } + + pub async fn delete(&self, imap_server: &str, mine_email :&str,smtp_server: &str, password :&str, subject :&str, body :&str, name :&str) -> imap::error::Result> { + let client = imap::ClientBuilder::new(imap_server, 993).native_tls()?; + // the client we have here is unauthenticated. + // to do anything useful with the e-mails, we need to log in + let mut imap_session = client + .login(mine_email, password) + .map_err(|e| e.0)?; + // we want to fetch the first email in the INBOX mailbox + imap_session.select("INBOX")?; + // fetch message number 1 in this mailbox, along with its RFC822 field. + // RFC 822 dictates the format of the body of e-mails + let mut i = 1; + loop { + let i1 = i.to_string(); + let messages = imap_session.fetch(i1, "RFC822.HEADER")?; + let message = if let Some(m) = messages.iter().next() { + m + } else { + return Ok(None); + }; + // extract the message's body + let header = message.header().expect("message did not have a subject!"); + let parsed = parse_mail(header).unwrap(); + let mail = parsed.headers.get_first_value("From").unwrap(); + let pos = mail.rfind("<").unwrap(); + let (_, lst) = mail.split_at(pos + 1); + let mut email = lst.to_string(); + email.pop(); + let email1 = email.clone(); + let flag = self.get_email(email1,name.to_string()).await; + //找到此用户,则删除 + if flag.is_ok() { + let email2 = email.clone(); + SendMail::send_email(&email2,&MAIL2LIST_CONFIG.email.leave_email,&MAIL2LIST_CONFIG.email.leave_smtp_server,&MAIL2LIST_CONFIG.email.leave_email_password,subject,body); + self.del_by_column("email",&email2).await; + } + //找不到 则继续遍历 直到遍历完所有邮箱 + i = i + 1; + if email.is_empty() { + break; + } + } + // be nice to the server and log out + imap_session.logout()?; + Ok(Some(true)) + } +} + +impl Default for SubscribeMailListService { + fn default() -> Self { + SubscribeMailListService {} + } +} + +impl CrudService + for SubscribeMailListService +{ + fn get_wrapper(arg: &SubscribeMailListQuery) -> Wrapper { + RB.new_wrapper() + } + fn set_save_common_fields(&self, common: CommonField, data: &mut SubscribeMailList) { + data.id = common.id; + } +} \ No newline at end of file diff --git a/mail2list/vue-mail2list-web/src/assets/language/en-US.js b/mail2list/vue-mail2list-web/src/assets/language/en-US.js index b530d3fa..3209ed36 100644 --- a/mail2list/vue-mail2list-web/src/assets/language/en-US.js +++ b/mail2list/vue-mail2list-web/src/assets/language/en-US.js @@ -50,6 +50,7 @@ module.exports = { placeholder: "please enter the content...", template_title: "Template name", ok_text: "Submit", + new_thread:"new thread", upload_text: "upload", confirm: "Confirm", cancel_text: "Cancel", diff --git a/mail2list/vue-mail2list-web/src/assets/language/zh-CN.js b/mail2list/vue-mail2list-web/src/assets/language/zh-CN.js index 7b1dd650..568f9a5c 100644 --- a/mail2list/vue-mail2list-web/src/assets/language/zh-CN.js +++ b/mail2list/vue-mail2list-web/src/assets/language/zh-CN.js @@ -47,6 +47,7 @@ module.exports = { modal: { placeholder: "请输入内容...", template_title: "保存模板名称", + new_thread:"新建会话", ok_text: "提交", upload_text: "提交", confirm: "确定", diff --git a/mail2list/vue-mail2list-web/src/components/compon/Footer.vue b/mail2list/vue-mail2list-web/src/components/compon/Footer.vue index ad61670a..14d325d8 100644 --- a/mail2list/vue-mail2list-web/src/components/compon/Footer.vue +++ b/mail2list/vue-mail2list-web/src/components/compon/Footer.vue @@ -93,7 +93,7 @@ export default { case 'flow': window.sessionStorage.setItem("menuName", 'flow'); this.$router.push({ - name: 'flow', + name: 'maillist', path: "/maillist", // query: { // src: "/drawingBoard/page/flow/mxGraph/index.html?load=" + row.id, diff --git a/mail2list/vue-mail2list-web/src/components/pages/archive/index.scss b/mail2list/vue-mail2list-web/src/components/pages/archive/index.scss new file mode 100644 index 00000000..dc9e881d --- /dev/null +++ b/mail2list/vue-mail2list-web/src/components/pages/archive/index.scss @@ -0,0 +1,98 @@ +section { + .navbar { + display: flex; + justify-content: space-between; + align-items: center; + padding: 6px 20px; + border: 1px solid #d5d5d5; + background: #f7f7f7; + + background: linear-gradient(to bottom, #ffffff, #f7f7f7); + //filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#f7f7f7', GradientType=0); + + .left { + font-weight: bold; + } + } + + /deep/ .ivu-input-wrapper { + margin: 6px 0; + + .ivu-input { + padding: 8px 10px; + } + } + + .input { + margin-bottom: 5px; + margin-top: 10px; + } + + .input-height { + margin-bottom: 5px; + margin-top: 10px; + height: 30px; + } + + .button-warp { + text-align: center; + color: #1a7444; + font-weight: bold; + display: inline-block; + margin: 2px 5px; + padding: 2px 10px; + border: 1px solid #1a7444; + background-color: #fafafa; + cursor: pointer; + background-image: linear-gradient(to bottom, #fefefe, #f2f2f2); + background-repeat: repeat-x; + box-shadow: inset 0 1px 1px rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + } + + .button-warp-archive { + text-align: center; + color: #1a7444; + font-weight: bold; + display: inline-block; + //margin: 2px 5px; + padding: none; + border: none;; + background-color: #fafafa; + cursor: pointer; + background-image: linear-gradient(to bottom, #fefefe, #f2f2f2); + background-repeat: repeat-x; + box-shadow: inset 0 1px 1px rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + } + + .page { + padding: 12px; + border: 1px solid #e6e6e6; + display: flex; + justify-content: center; + margin-top: 10px; + background-color: #fff; + } + +} + +.modal-warp { + .item { + margin: 12px 0px; + + label { + display: inline-block; + width: 120px; + text-align: right; + } + } +} + +.Schedule { + /deep/ .ivu-form-item-label { + width: 133px !important; + } + + /deep/ .form-content>.ivu-form-item-content { + margin-left: 120px !important; + } +} \ No newline at end of file diff --git a/mail2list/vue-mail2list-web/src/components/pages/archive/index.vue b/mail2list/vue-mail2list-web/src/components/pages/archive/index.vue new file mode 100644 index 00000000..b1d899d0 --- /dev/null +++ b/mail2list/vue-mail2list-web/src/components/pages/archive/index.vue @@ -0,0 +1,167 @@ + + + + \ No newline at end of file diff --git a/mail2list/vue-mail2list-web/src/components/pages/mailList/index.scss b/mail2list/vue-mail2list-web/src/components/pages/mailList/index.scss index 33bcb393..ccec18b9 100644 --- a/mail2list/vue-mail2list-web/src/components/pages/mailList/index.scss +++ b/mail2list/vue-mail2list-web/src/components/pages/mailList/index.scss @@ -8,26 +8,32 @@ section { background: #f7f7f7; background: linear-gradient(to bottom, #ffffff, #f7f7f7); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#f7f7f7', GradientType=0); + //filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#f7f7f7', GradientType=0); + .left { font-weight: bold; } } + /deep/ .ivu-input-wrapper { margin: 6px 0; + .ivu-input { padding: 8px 10px; } } + .input { margin-bottom: 5px; margin-top: 10px; } + .input-height { margin-bottom: 5px; margin-top: 10px; - height: 60; + height: 30px; } + .button-warp { text-align: center; color: #1a7444; @@ -42,6 +48,7 @@ section { background-repeat: repeat-x; box-shadow: inset 0 1px 1px rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); } + .page { padding: 12px; border: 1px solid #e6e6e6; @@ -50,23 +57,27 @@ section { margin-top: 10px; background-color: #fff; } - + } - .modal-warp{ - .item{ - margin: 12px 0px; - label{ - display: inline-block; - width: 120px; - text-align: right; - } + +.modal-warp { + .item { + margin: 12px 0px; + + label { + display: inline-block; + width: 120px; + text-align: right; } } - .Schedule{ - /deep/ .ivu-form-item-label{ - width: 133px!important; - } - /deep/ .form-content>.ivu-form-item-content{ - margin-left: 120px!important; - } - } \ No newline at end of file +} + +.Schedule { + /deep/ .ivu-form-item-label { + width: 133px !important; + } + + /deep/ .form-content>.ivu-form-item-content { + margin-left: 120px !important; + } +} \ No newline at end of file diff --git a/mail2list/vue-mail2list-web/src/components/pages/mailList/index.vue b/mail2list/vue-mail2list-web/src/components/pages/mailList/index.vue index cec1e78e..24bc6906 100644 --- a/mail2list/vue-mail2list-web/src/components/pages/mailList/index.vue +++ b/mail2list/vue-mail2list-web/src/components/pages/mailList/index.vue @@ -3,27 +3,135 @@ -
- +
+ + + + + @@ -46,33 +154,39 @@ export default { description: "", email: "", archive: "", + username: "", formValidate: { - mail: '' + mail: "", }, ruleValidate: { mail: [ - { required: true, message: 'Mailbox cannot be empty', trigger: 'blur' }, - { type: 'email', message: 'Incorrect email format', trigger: 'blur' } - ] + { + required: true, + message: "Mailbox cannot be empty", + trigger: "blur", + }, + { type: "email", message: "Incorrect email format", trigger: "blur" }, + ], }, promptContent: [ { - content: 'Subscribe', - icon: 'ios-redo' - },{ - content: 'Archive', - icon: 'ios-archive' - } - ] + content: "Subscribe", + icon: "ios-redo", + }, + { + content: "Archive", + icon: "ios-archive", + }, + ], }; }, watch: { isOpen(state) { if (!state) { this.handleReset(); - }else if (state && this.id === ''){ + } else if (state && this.id === "") { this.getGlobalList(false); } }, @@ -119,22 +233,76 @@ export default { this.name = ""; this.description = ""; this.email = ""; - this.archive = "" + this.archive = ""; + this.username = ""; }, handleButtonSelect(row, key) { switch (key) { case 2: - window.open('https://mailweb.openeuler.org/hyperkitty/list/' + row.name.toLowerCase() + '@openeuler.org/'); + this.$event.emit("crumb", [ + { name: "maillist", path: "/maillist" }, + { name: "archive", path: "/archive" }, + ]); + this.$router.push({ + path: "/archive", + // query: {id:row.id, email:row.email}, + query: { + // src: "/archive/page/flow/mxGraph/index.html?load=" + row.id, + name: row.name, + email: row.email, + }, + }); break; case 1: - window.open('https://mailweb.openeuler.org/postorius/lists/' + row.name.toLowerCase() + '@openeuler.org/'); + this.getRowData(row); break; default: break; } }, + // add / update + handleSaveUpdateData() { + let data = { + username: this.formValidate.username, + email: this.formValidate.mail, + name: this.name, + }; + //update + // data.id = this.id; + this.$axios + .post("/maillist/menu/subscribe", JSON.stringify(data), { + headers: { + "content-type": "application/json", + }, + }) + .then((res) => { + if (res.data.code === 0) { + this.$Modal.success({ + title: this.$t("tip.title"), + // content `${this.data.data}`, + content: `${res.data.data} `, + }); + this.isOpen = false; + this.handleReset(); + this.getTableData(); + } else { + this.$Message.error({ + content: `${this.name} ` + this.$t("tip.update_fail_content"), + duration: 3, + }); + } + }) + .catch((error) => { + console.log(error); + this.$Message.error({ + content: this.$t("tip.fault_content"), + duration: 3, + }); + }); + }, + getRowData(row) { this.$event.emit("loading", true); this.$axios @@ -161,7 +329,6 @@ export default { }); }, - getTableData() { this.$axios .get("/maillist/menu/list") @@ -169,7 +336,7 @@ export default { if (res.data.code != 0) { this.$Message.error({ content: this.$t("tip.request_fail_content"), - duration: 3 + duration: 3, }); } else { this.tableData = res.data.data; @@ -179,7 +346,7 @@ export default { console.log(error); this.$Message.error({ content: this.$t("tip.fault_content"), - duration: 3 + duration: 3, }); }); }, @@ -188,7 +355,7 @@ export default { \ No newline at end of file diff --git a/mail2list/vue-mail2list-web/src/main.js b/mail2list/vue-mail2list-web/src/main.js index 1c7712d3..59c0b785 100644 --- a/mail2list/vue-mail2list-web/src/main.js +++ b/mail2list/vue-mail2list-web/src/main.js @@ -55,7 +55,7 @@ import './assets/style/my-vxe-table.scss'; Vue.use(VXETable) //实例化 store -import store from './store'; // this.$store.commit("setUser", user); +import store from './store'; // this.$store.commit("setUser", user); //引入axios import axios from 'axios'; diff --git a/mail2list/vue-mail2list-web/src/router/index.js b/mail2list/vue-mail2list-web/src/router/index.js index c396b99b..9272ac2e 100644 --- a/mail2list/vue-mail2list-web/src/router/index.js +++ b/mail2list/vue-mail2list-web/src/router/index.js @@ -34,6 +34,11 @@ export default new Router({ path: '/maillist', name: 'mailList', component: () => import('../components/pages/mailList') + }, + { + path: '/archive', + name: 'archive', + component: () => import('../components/pages/archive') } ] }, -- Gitee