Skip to content

Commit 50bb621

Browse files
committedJan 21, 2025
V1
1 parent 5900fff commit 50bb621

File tree

10 files changed

+637
-1
lines changed

10 files changed

+637
-1
lines changed
 

‎.dockerignore

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Git
2+
.git
3+
.gitignore
4+
5+
# Rust
6+
target/
7+
**/*.rs.bk
8+
Cargo.lock
9+
10+
# Docker
11+
Dockerfile
12+
.dockerignore
13+
14+
# IDE
15+
.idea/
16+
.vscode/
17+
18+
# Data
19+
data/
20+
*.eml

‎.github/workflows/build.yaml

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
name: ci
2+
3+
on:
4+
push:
5+
branches:
6+
- "main"
7+
8+
jobs:
9+
docker:
10+
runs-on: ubuntu-latest
11+
steps:
12+
- name: Checkout
13+
uses: actions/checkout@v4
14+
15+
# 添加 QEMU 支持,用于多架构构建
16+
- name: Set up QEMU
17+
uses: docker/setup-qemu-action@v3
18+
19+
- name: Log in to Docker Hub
20+
uses: docker/login-action@v3
21+
with:
22+
username: ${{ secrets.DOCKERHUB_USERNAME }}
23+
password: ${{ secrets.DOCKERHUB_TOKEN }}
24+
25+
- name: Set up Docker Buildx
26+
uses: docker/setup-buildx-action@v3
27+
with:
28+
version: "lab:latest"
29+
driver: cloud
30+
endpoint: "relaxcloud/dev"
31+
32+
- name: Build and push
33+
uses: docker/build-push-action@v5
34+
with:
35+
context: .
36+
platforms: linux/amd64,linux/arm64/v8 # 指定构建平台
37+
tags: "relaxcloud/rsendmail:${{ github.ref_name }}"
38+
outputs: ${{ github.event_name == 'pull_request' && 'type=cacheonly' || 'type=registry,push=true' }}

‎.gitignore

+3-1
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,6 @@ Cargo.lock
1818
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
1919
# and can be added to the global gitignore or merged into this file. For a more nuclear
2020
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
21-
#.idea/
21+
#.idea/
22+
23+
test_data/*

‎Dockerfile

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# 第一阶段:依赖缓存层
2+
FROM rust:1.75-slim as cacher
3+
WORKDIR /usr/src/app/rsendmail
4+
COPY rsendmail/Cargo.* ./
5+
RUN rm -f Cargo.lock && \
6+
mkdir src && \
7+
echo "fn main() {}" > src/main.rs && \
8+
cargo fetch
9+
10+
# 第二阶段:构建层
11+
FROM rust:1.75-slim as builder
12+
WORKDIR /usr/src/app/rsendmail
13+
COPY --from=cacher /usr/local/cargo /usr/local/cargo
14+
COPY rsendmail .
15+
RUN rm -f Cargo.lock && \
16+
cargo build --release --offline
17+
18+
# 第三阶段:运行层
19+
FROM debian:bookworm-slim
20+
RUN apt-get update && \
21+
apt-get install -y --no-install-recommends \
22+
libssl3 \
23+
ca-certificates \
24+
&& rm -rf /var/lib/apt/lists/* && \
25+
useradd -r -s /bin/false rsendmail && \
26+
mkdir /data && \
27+
chown rsendmail:rsendmail /data
28+
29+
# 只复制必要的二进制文件
30+
COPY --from=builder /usr/src/app/rsendmail/target/release/rsendmail /usr/local/bin/
31+
32+
USER rsendmail
33+
WORKDIR /data
34+
ENTRYPOINT ["rsendmail"]

‎README.md

+81
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,83 @@
11
# RSendMail
22
Used for batch sending emails for testing
3+
4+
## Features
5+
6+
- Batch process and send multiple emails
7+
- Multi-threaded processing for improved performance
8+
- Support for custom SMTP server configuration
9+
- Detailed logging and statistics
10+
- Docker support for easy deployment
11+
12+
## Building
13+
14+
### Local Build
15+
```bash
16+
cd rsendmail
17+
cargo build --release
18+
```
19+
20+
### Docker Build
21+
```bash
22+
docker build -t rsendmail .
23+
```
24+
25+
## Usage
26+
27+
### Local Usage
28+
```bash
29+
rsendmail -s <smtp_server> -P <port> -f <from_addr> -t <to_addr> -d <email_dir> -p <parallel>
30+
```
31+
32+
### Docker Usage
33+
```bash
34+
docker run --rm -v /path/to/emails:/data rsendmail -s <smtp_server> -P <port> -f <from_addr> -t <to_addr> -d /data -p <parallel>
35+
```
36+
37+
### Parameters
38+
39+
- `-s, --smtp`: SMTP server address
40+
- `-P, --port`: SMTP server port
41+
- `-f, --from`: From email address
42+
- `-t, --to`: To email address
43+
- `-d, --dir`: Directory containing email files (.eml)
44+
- `-p, --parallel`: Number of parallel processes (default: 10)
45+
46+
## Example
47+
48+
```bash
49+
# Local example
50+
rsendmail -s 192.168.1.100 -P 25 -f sender@example.com -t recipient@example.com -d ./emails -p 10
51+
52+
# Docker example
53+
docker run --rm -v $(pwd)/emails:/data rsendmail -s 192.168.1.100 -P 25 -f sender@example.com -t recipient@example.com -d /data -p 10
54+
```
55+
56+
## Docker Container
57+
58+
The Docker container is designed with security and efficiency in mind:
59+
60+
- Based on debian:bookworm-slim for minimal size
61+
- Runs as non-root user
62+
- Includes only necessary runtime dependencies
63+
- Uses volume mounting for email files
64+
- Stateless operation
65+
66+
### Container Structure
67+
68+
- `/usr/local/bin/rsendmail`: Application binary
69+
- `/data`: Mount point for email files (volume)
70+
71+
## Performance
72+
73+
- Multi-threaded processing
74+
- Efficient memory usage
75+
- Fast email parsing and sending
76+
- Detailed performance statistics output
77+
78+
## Security
79+
80+
- Non-root user execution
81+
- Minimal container footprint
82+
- Isolated runtime environment
83+
- No persistent storage

‎rsendmail/Cargo.toml

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
[package]
2+
name = "rsendmail"
3+
version = "0.1.0"
4+
edition = "2021"
5+
authors = ["RSendMail Contributors"]
6+
description = "A high-performance bulk email sending CLI tool"
7+
8+
[dependencies]
9+
mail-send = "0.4.0"
10+
mail-parser = "0.9.2"
11+
mail-builder = "0.3.2"
12+
tokio = { version = "1.35.1", features = ["full"] }
13+
clap = { version = "4.4.12", features = ["derive"] }
14+
anyhow = "1.0.79"
15+
walkdir = "2.4.0"
16+
log = "0.4.20"
17+
env_logger = "0.10.1"
18+
chrono = "0.4.31"
19+
num_cpus = "1.16.0"
20+
futures = "0.3.30"

‎rsendmail/src/config.rs

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
use clap::Parser;
2+
3+
#[derive(Debug, Clone)]
4+
pub enum ProcessMode {
5+
Auto,
6+
Fixed(usize),
7+
}
8+
9+
#[derive(Parser, Debug, Clone)]
10+
#[command(author, version, about, long_about = None)]
11+
pub struct Config {
12+
/// SMTP服务器地址
13+
#[arg(short = 's', long)]
14+
pub smtp_server: String,
15+
16+
/// SMTP服务器端口
17+
#[arg(short = 'P', long, default_value_t = 25)]
18+
pub port: u16,
19+
20+
/// 发件人邮箱
21+
#[arg(short = 'f', long)]
22+
pub from: String,
23+
24+
/// 收件人邮箱
25+
#[arg(short = 't', long)]
26+
pub to: String,
27+
28+
/// 邮件文件目录
29+
#[arg(short = 'd', long)]
30+
pub dir: String,
31+
32+
/// 邮件文件扩展名
33+
#[arg(short = 'e', long, default_value = "eml")]
34+
pub extension: String,
35+
36+
/// 并发进程数,使用 "auto" 自动设置
37+
#[arg(short = 'p', long, default_value = "auto")]
38+
processes_str: String,
39+
}
40+
41+
impl Config {
42+
pub fn processes(&self) -> ProcessMode {
43+
if self.processes_str == "auto" {
44+
ProcessMode::Auto
45+
} else {
46+
match self.processes_str.parse() {
47+
Ok(n) => ProcessMode::Fixed(n),
48+
Err(_) => ProcessMode::Auto,
49+
}
50+
}
51+
}
52+
}

0 commit comments

Comments
 (0)
Failed to load comments.