up
This commit is contained in:
@@ -6,3 +6,14 @@
|
||||
- **新增 AdminViewMail handler + admin/mail_view.html 模板**:管理员可查看任意用户邮件详情
|
||||
- **新增 AdminDownloadAttachment handler + 路由**:管理员可下载任意用户附件(绕过归属校验)
|
||||
- **AdminHandler 新增 storage 依赖**:`NewAdminHandler(stores, attStorage)` 签名变更
|
||||
|
||||
## MailGo README 编写
|
||||
- 创建 `README.md`,包含:功能特性、快速开始、完整配置参考、5 个常见配置场景(MySQL 切换、Unix Socket、TLS、OAuth2、LDAP)、端口速查、目录结构、Linux systemd 部署、DNS 配置、技术栈、默认账户
|
||||
|
||||
## MailGo 安装脚本
|
||||
- 创建 `install.sh`,参考 portal_page 脚本适配 MailGo
|
||||
- 服务名 mail_go,Git 仓库 https://git.lmve.net/kevin/mailgo.git
|
||||
- CGO_ENABLED=1(mattn/go-sqlite3 依赖)
|
||||
- 部署模板目录 internal/web/templates/ + admin/ 子目录
|
||||
- systemd 增加 AmbientCapabilities=CAP_NET_BIND_SERVICE(绑定 25/465 等特权端口)
|
||||
- 附件目录 /srv/mail_go/attachments/
|
||||
|
||||
@@ -0,0 +1,389 @@
|
||||
# MailGo
|
||||
|
||||
Go 语言编写的轻量级邮件系统,集成 SMTP / IMAP / POP3 协议服务和 Web 管理界面。
|
||||
|
||||
## 功能特性
|
||||
|
||||
- **邮件协议**:SMTP(发送)、IMAP(同步)、POP3(收取),均支持 TLS 加密
|
||||
- **Web 邮箱**:收件箱、已发送、草稿箱、富文本编辑(Quill.js)、附件上传/下载
|
||||
- **管理后台**:域名管理、用户管理、DKIM 密钥自动生成、DNS 配置提示、全量邮件查看、IP 封禁管理、仪表盘统计
|
||||
- **外部认证**:OAuth2(Google / GitHub)、LDAP(可选,默认关闭)
|
||||
- **安全机制**:BCrypt 密码哈希、登录失败自动封禁 IP、管理员可解封
|
||||
- **多数据库**:默认 SQLite,可切换 MySQL
|
||||
- **跨平台**:Linux 生产部署 + Windows 本地调试
|
||||
|
||||
## 快速开始
|
||||
|
||||
### 编译
|
||||
|
||||
```bash
|
||||
go build -o mailgo .
|
||||
```
|
||||
|
||||
### 启动
|
||||
|
||||
```bash
|
||||
./mailgo
|
||||
```
|
||||
|
||||
首次启动会自动创建配置文件和数据库,默认管理员账户:
|
||||
|
||||
| 项目 | 值 |
|
||||
|------|-----|
|
||||
| 邮箱 | `admin@example.com` |
|
||||
| 密码 | `admin` |
|
||||
|
||||
> ⚠️ 请在生产环境中立即修改默认密码。
|
||||
|
||||
### 访问
|
||||
|
||||
| 页面 | 地址 |
|
||||
|------|------|
|
||||
| 用户邮箱 | `http://localhost:8080/` |
|
||||
| 管理后台 | `http://localhost:8080/admin` |
|
||||
|
||||
---
|
||||
|
||||
## 配置文件
|
||||
|
||||
配置文件路径(TOML 格式):
|
||||
|
||||
| 系统 | 路径 |
|
||||
|------|------|
|
||||
| Linux | `/etc/mail_go/mail_go.toml` |
|
||||
| Windows | `./win/etc/mail_go/mail_go.toml` |
|
||||
|
||||
首次启动自动生成,缺失字段自动补全。修改后需重启服务生效。
|
||||
|
||||
### 完整配置参考
|
||||
|
||||
```toml
|
||||
[database]
|
||||
driver = "sqlite" # sqlite | mysql
|
||||
dsn = "/srv/mail_go/mail.db" # SQLite: 文件路径; MySQL: DSN 连接串
|
||||
|
||||
[storage]
|
||||
base_dir = "/srv/mail_go" # 数据根目录
|
||||
attach_dir = "/srv/mail_go/attachments" # 附件存储目录
|
||||
|
||||
[web]
|
||||
addr = ":8080" # 监听地址,支持 TCP 端口或 Unix socket
|
||||
|
||||
[smtp]
|
||||
addr = ":25" # SMTP 明文端口
|
||||
tls_addr = ":465" # SMTPS 端口(需配置 TLS 证书)
|
||||
domain = "example.com" # 邮件域名
|
||||
tls_cert = "" # TLS 证书路径(留空则不启用 TLS)
|
||||
tls_key = "" # TLS 私钥路径
|
||||
max_message_bytes = 67108864 # 单封邮件最大 64MB
|
||||
|
||||
[imap]
|
||||
addr = ":143" # IMAP 明文端口
|
||||
tls_addr = ":993" # IMAPS 端口
|
||||
tls_cert = ""
|
||||
tls_key = ""
|
||||
|
||||
[pop3]
|
||||
addr = ":110" # POP3 明文端口
|
||||
tls_addr = ":995" # POP3S 端口
|
||||
tls_cert = ""
|
||||
tls_key = ""
|
||||
|
||||
[auth]
|
||||
oauth2_enabled = false # 是否启用 OAuth2 登录
|
||||
oauth2_provider = "" # google | github
|
||||
oauth2_client_id = ""
|
||||
oauth2_client_secret = ""
|
||||
oauth2_redirect_url = ""
|
||||
ldap_enabled = false # 是否启用 LDAP 登录
|
||||
ldap_server = "" # 例: ldap://localhost:389
|
||||
ldap_bind_dn = "" # 例: cn=admin,dc=example,dc=com
|
||||
ldap_bind_password = ""
|
||||
ldap_search_base = "" # 例: ou=users,dc=example,dc=com
|
||||
ldap_search_filter = "" # 例: (uid=%s)
|
||||
ldap_use_tls = false
|
||||
|
||||
[ban]
|
||||
max_fail_attempts = 5 # 登录失败次数阈值
|
||||
ban_duration_min = 30 # 封禁时长(分钟)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 常见配置场景
|
||||
|
||||
### 1. 切换到 MySQL
|
||||
|
||||
```toml
|
||||
[database]
|
||||
driver = "mysql"
|
||||
dsn = "mailgo:YourPassword@tcp(127.0.0.1:3306)/mailgo?charset=utf8mb4&parseTime=True&loc=Local"
|
||||
```
|
||||
|
||||
MySQL DSN 格式:`用户名:密码@tcp(主机:端口)/数据库名?参数`
|
||||
|
||||
> 数据库和用户需提前在 MySQL 中创建:
|
||||
> ```sql
|
||||
> CREATE DATABASE mailgo CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
> CREATE USER 'mailgo'@'localhost' IDENTIFIED BY 'YourPassword';
|
||||
> GRANT ALL PRIVILEGES ON mailgo.* TO 'mailgo'@'localhost';
|
||||
> FLUSH PRIVILEGES;
|
||||
> ```
|
||||
|
||||
### 2. Web 使用 Unix Socket
|
||||
|
||||
```toml
|
||||
[web]
|
||||
addr = "/run/mail_go/web.sock"
|
||||
```
|
||||
|
||||
当 `addr` 以 `/` 开头时,Gin 自动以 Unix socket 方式监听。
|
||||
|
||||
Nginx 反向代理配置:
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
server_name mail.example.com;
|
||||
|
||||
location / {
|
||||
proxy_pass http://unix:/run/mail_go/web.sock;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> 确保 socket 目录存在并有写权限:
|
||||
> ```bash
|
||||
> mkdir -p /run/mail_go
|
||||
> chown mailgo:mailgo /run/mail_go
|
||||
> ```
|
||||
|
||||
### 3. 启用 TLS 加密
|
||||
|
||||
为 SMTP/IMAP/POP3 启用 TLS(三个协议共享同一证书,或分别配置):
|
||||
|
||||
```toml
|
||||
[smtp]
|
||||
tls_cert = "/etc/mail_go/certs/server.crt"
|
||||
tls_key = "/etc/mail_go/certs/server.key"
|
||||
|
||||
[imap]
|
||||
tls_cert = "/etc/mail_go/certs/server.crt"
|
||||
tls_key = "/etc/mail_go/certs/server.key"
|
||||
|
||||
[pop3]
|
||||
tls_cert = "/etc/mail_go/certs/server.crt"
|
||||
tls_key = "/etc/mail_go/certs/server.key"
|
||||
```
|
||||
|
||||
配置 TLS 后,系统会自动启动对应的加密端口(465/993/995)。管理后台添加域名时勾选 TLS 会自动切换端口号。
|
||||
|
||||
> 可用 Let's Encrypt 获取免费证书:
|
||||
> ```bash
|
||||
> certbot certonly --standalone -d mail.example.com
|
||||
> # 证书路径: /etc/letsencrypt/live/mail.example.com/fullchain.pem
|
||||
> # 私钥路径: /etc/letsencrypt/live/mail.example.com/privkey.pem
|
||||
> ```
|
||||
|
||||
### 4. 启用 OAuth2 登录(Google 示例)
|
||||
|
||||
```toml
|
||||
[auth]
|
||||
oauth2_enabled = true
|
||||
oauth2_provider = "google"
|
||||
oauth2_client_id = "your-client-id.apps.googleusercontent.com"
|
||||
oauth2_client_secret = "your-client-secret"
|
||||
oauth2_redirect_url = "https://mail.example.com/auth/oauth2/callback"
|
||||
```
|
||||
|
||||
> 需要在 [Google Cloud Console](https://console.cloud.google.com/) 创建 OAuth 2.0 客户端,授权回调地址填 `https://你的域名/auth/oauth2/callback`。
|
||||
|
||||
### 5. 启用 LDAP 认证
|
||||
|
||||
```toml
|
||||
[auth]
|
||||
ldap_enabled = true
|
||||
ldap_server = "ldap://ldap.example.com:389"
|
||||
ldap_bind_dn = "cn=admin,dc=example,dc=com"
|
||||
ldap_bind_password = "ldap_admin_password"
|
||||
ldap_search_base = "ou=users,dc=example,dc=com"
|
||||
ldap_search_filter = "(uid=%s)"
|
||||
ldap_use_tls = true
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 端口速查
|
||||
|
||||
| 协议 | 明文端口 | TLS 端口 | 说明 |
|
||||
|------|---------|---------|------|
|
||||
| SMTP | 25 | 465 | 邮件发送 |
|
||||
| IMAP | 143 | 993 | 邮箱同步 |
|
||||
| POP3 | 110 | 995 | 邮件收取 |
|
||||
| Web | 8080 | — | Web 界面(可配 Unix socket) |
|
||||
|
||||
---
|
||||
|
||||
## 目录结构
|
||||
|
||||
```
|
||||
mailgo/
|
||||
├── main.go # 程序入口
|
||||
├── config/
|
||||
│ ├── config.go # 配置加载与合并
|
||||
│ └── defaults.go # 默认常量
|
||||
├── internal/
|
||||
│ ├── db/
|
||||
│ │ ├── db.go # 数据库初始化(SQLite/MySQL)
|
||||
│ │ └── models.go # GORM 模型定义
|
||||
│ ├── store/
|
||||
│ │ ├── stores.go # Store 聚合器
|
||||
│ │ ├── user_store.go # 用户数据操作
|
||||
│ │ ├── mail_store.go # 邮件数据操作
|
||||
│ │ ├── domain_store.go # 域名数据操作
|
||||
│ │ ├── attachment_store.go # 附件数据操作
|
||||
│ │ └── ban_store.go # 封禁数据操作
|
||||
│ ├── smtp_server/server.go # SMTP 服务
|
||||
│ ├── imap_server/
|
||||
│ │ ├── server.go # IMAP 服务
|
||||
│ │ └── backend.go # IMAP 后端
|
||||
│ ├── pop3_server/server.go # POP3 服务
|
||||
│ ├── storage/attachment.go # 附件文件存储
|
||||
│ ├── dkim/keys.go # DKIM 密钥生成
|
||||
│ ├── auth/
|
||||
│ │ ├── provider.go # 认证接口
|
||||
│ │ ├── oauth2.go # OAuth2 认证
|
||||
│ │ └── ldap.go # LDAP 认证
|
||||
│ └── web/
|
||||
│ ├── server.go # Web 路由与模板加载
|
||||
│ ├── handlers/
|
||||
│ │ ├── auth.go # 登录/登出/OAuth2/LDAP
|
||||
│ │ ├── mail.go # 邮箱操作
|
||||
│ │ └── admin.go # 管理后台
|
||||
│ ├── middleware/
|
||||
│ │ ├── auth.go # 会话认证
|
||||
│ │ ├── admin.go # 管理员权限
|
||||
│ │ └── ban.go # IP 封禁检查
|
||||
│ └── templates/ # HTML 模板
|
||||
│ ├── *.html # 用户页面
|
||||
│ └── admin/*.html # 管理页面
|
||||
└── .gitignore
|
||||
```
|
||||
|
||||
### 运行时数据目录(Linux)
|
||||
|
||||
| 路径 | 说明 |
|
||||
|------|------|
|
||||
| `/etc/mail_go/` | 配置文件 |
|
||||
| `/srv/mail_go/` | 数据库 + 附件存储 |
|
||||
|
||||
### 运行时数据目录(Windows 调试)
|
||||
|
||||
| 路径 | 说明 |
|
||||
|------|------|
|
||||
| `./win/etc/mail_go/` | 配置文件 |
|
||||
| `./win/srv/mail_go/` | 数据库 + 附件存储 |
|
||||
|
||||
---
|
||||
|
||||
## Linux 服务部署
|
||||
|
||||
### systemd 服务文件
|
||||
|
||||
创建 `/etc/systemd/system/mailgo.service`:
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=MailGo Mail Server
|
||||
After=network.target mysql.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=mailgo
|
||||
Group=mailgo
|
||||
WorkingDirectory=/opt/mailgo
|
||||
ExecStart=/opt/mailgo/mailgo
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
LimitNOFILE=65536
|
||||
|
||||
# 安全加固
|
||||
NoNewPrivileges=true
|
||||
ProtectSystem=strict
|
||||
ProtectHome=true
|
||||
ReadWritePaths=/srv/mail_go /run/mail_go /etc/mail_go
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
### 启动服务
|
||||
|
||||
```bash
|
||||
# 创建系统用户
|
||||
sudo useradd -r -s /sbin/nologin -d /srv/mail_go mailgo
|
||||
|
||||
# 设置目录权限
|
||||
sudo chown -R mailgo:mailgo /srv/mail_go /etc/mail_go
|
||||
|
||||
# 启用并启动
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable mailgo
|
||||
sudo systemctl start mailgo
|
||||
|
||||
# 查看日志
|
||||
sudo journalctl -u mailgo -f
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## DNS 配置
|
||||
|
||||
邮件服务需要以下 DNS 记录(管理后台 → DNS 提示 页面可查看完整配置):
|
||||
|
||||
| 记录类型 | 名称 | 值 | 说明 |
|
||||
|---------|------|-----|------|
|
||||
| A | mail | 服务器 IP | 邮件服务器地址 |
|
||||
| MX | @ | mail.example.com | 邮件路由 |
|
||||
| TXT | @ | `v=spf1 mx ~all` | SPF 反垃圾 |
|
||||
| TXT | default._domainkey | DKIM 公钥(后台自动生成) | DKIM 签名验证 |
|
||||
| TXT | _dmarc | `v=DMARC1; p=none; rua=mailto:admin@example.com` | DMARC 策略 |
|
||||
|
||||
---
|
||||
|
||||
## 技术栈
|
||||
|
||||
| 组件 | 技术 |
|
||||
|------|------|
|
||||
| 语言 | Go 1.25+ |
|
||||
| Web 框架 | Gin |
|
||||
| 模板引擎 | html/template |
|
||||
| ORM | GORM |
|
||||
| 数据库 | SQLite(默认)/ MySQL |
|
||||
| 配置格式 | TOML |
|
||||
| SMTP | github.com/emersion/go-smtp |
|
||||
| IMAP | github.com/emersion/go-imap v1 |
|
||||
| POP3 | 手工实现 TCP 协议 |
|
||||
| 密码哈希 | golang.org/x/crypto/bcrypt |
|
||||
| 富文本 | Quill.js (CDN) |
|
||||
| OAuth2 | golang.org/x/oauth2 |
|
||||
| LDAP | github.com/go-ldap/ldap/v3 |
|
||||
| DKIM | RSA 2048 自动生成 |
|
||||
|
||||
---
|
||||
|
||||
## 默认账户
|
||||
|
||||
| 角色 | 邮箱 | 密码 | 默认配额 |
|
||||
|------|------|------|---------|
|
||||
| 管理员 | admin@example.com | admin | 5 GB |
|
||||
|
||||
> ⚠️ 部署到生产环境后请立即修改管理员密码。
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
+372
@@ -0,0 +1,372 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# mail_go 服务安装/管理脚本
|
||||
# 用法:
|
||||
# sudo ./install.sh install — 安装/更新服务
|
||||
# sudo ./install.sh uninstall — 卸载服务
|
||||
# sudo ./install.sh start — 启动服务
|
||||
# sudo ./install.sh stop — 停止服务
|
||||
# sudo ./install.sh restart — 重启服务
|
||||
# sudo ./install.sh status — 查看服务状态
|
||||
#
|
||||
|
||||
source /etc/profile
|
||||
source ~/.bashrc
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# ======================== 配置区 ========================
|
||||
SERVICE_NAME="mail_go"
|
||||
SERVICE_USER="mail_go"
|
||||
SERVICE_DESC="MailGo - Go 邮件系统(SMTP/IMAP/POP3 + Web)"
|
||||
|
||||
# 目录
|
||||
INSTALL_DIR="/opt/mail_go" # 程序安装目录
|
||||
DATA_DIR="/srv/mail_go" # 数据目录(数据库+附件)
|
||||
CONFIG_DIR="/etc/mail_go" # 配置文件目录
|
||||
LOG_DIR="/var/log/mail_go" # 日志目录
|
||||
PID_DIR="/run/mail_go" # PID/Socket 目录
|
||||
|
||||
# Git 仓库
|
||||
GIT_REPO="https://git.lmve.net/kevin/mailgo.git"
|
||||
GIT_BRANCH="main"
|
||||
|
||||
# 编译临时目录
|
||||
BUILD_DIR="/tmp/mail_go_build"
|
||||
|
||||
# systemd 服务文件路径
|
||||
SERVICE_FILE="/etc/systemd/system/${SERVICE_NAME}.service"
|
||||
|
||||
# ======================== 颜色输出 ========================
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
info() { echo -e "${BLUE}[INFO]${NC} $*"; }
|
||||
ok() { echo -e "${GREEN}[OK]${NC} $*"; }
|
||||
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
|
||||
error() { echo -e "${RED}[ERROR]${NC} $*"; exit 1; }
|
||||
|
||||
# ======================== 前置检查 ========================
|
||||
check_root() {
|
||||
if [[ $EUID -ne 0 ]]; then
|
||||
error "此脚本需要 root 权限运行,请使用 sudo"
|
||||
fi
|
||||
}
|
||||
|
||||
# ======================== 用户管理 ========================
|
||||
ensure_user() {
|
||||
if id "${SERVICE_USER}" &>/dev/null; then
|
||||
ok "用户 ${SERVICE_USER} 已存在"
|
||||
else
|
||||
info "创建系统用户 ${SERVICE_USER} ..."
|
||||
useradd -r -s /usr/sbin/nologin -d "${INSTALL_DIR}" -c "${SERVICE_DESC}" "${SERVICE_USER}"
|
||||
ok "用户 ${SERVICE_USER} 创建成功"
|
||||
fi
|
||||
}
|
||||
|
||||
# ======================== 目录管理 ========================
|
||||
ensure_dirs() {
|
||||
info "检查并创建关键目录 ..."
|
||||
|
||||
# 安装目录(含模板子目录结构)
|
||||
mkdir -p "${INSTALL_DIR}/internal/web/templates/admin"
|
||||
# 配置目录
|
||||
mkdir -p "${CONFIG_DIR}"
|
||||
# 数据目录及子目录
|
||||
mkdir -p "${DATA_DIR}/attachments"
|
||||
# 日志目录
|
||||
mkdir -p "${LOG_DIR}"
|
||||
# PID/Socket 目录
|
||||
mkdir -p "${PID_DIR}"
|
||||
|
||||
# 设置所有权(每次安装都强制修正,避免旧版遗留权限问题)
|
||||
chown -R "${SERVICE_USER}:${SERVICE_USER}" "${INSTALL_DIR}"
|
||||
chown -R "${SERVICE_USER}:${SERVICE_USER}" "${DATA_DIR}"
|
||||
chown -R "${SERVICE_USER}:${SERVICE_USER}" "${LOG_DIR}"
|
||||
chown -R "${SERVICE_USER}:${SERVICE_USER}" "${PID_DIR}"
|
||||
chown -R "${SERVICE_USER}:${SERVICE_USER}" "${CONFIG_DIR}"
|
||||
|
||||
# 确保目录可写
|
||||
chmod 755 "${CONFIG_DIR}" "${DATA_DIR}" "${LOG_DIR}" "${PID_DIR}" "${INSTALL_DIR}"
|
||||
|
||||
ok "目录结构就绪"
|
||||
}
|
||||
|
||||
# ======================== 编译 ========================
|
||||
build_binary() {
|
||||
info "拉取最新代码 ..."
|
||||
if [[ -d "${BUILD_DIR}/.git" ]]; then
|
||||
cd "${BUILD_DIR}"
|
||||
git fetch --all
|
||||
git reset --hard "origin/${GIT_BRANCH}"
|
||||
info "代码已更新到最新"
|
||||
else
|
||||
rm -rf "${BUILD_DIR}"
|
||||
git clone -b "${GIT_BRANCH}" "${GIT_REPO}" "${BUILD_DIR}"
|
||||
cd "${BUILD_DIR}"
|
||||
info "代码克隆完成"
|
||||
fi
|
||||
|
||||
info "编译 Go 项目(CGO 已启用,SQLite 依赖) ..."
|
||||
CGO_ENABLED=1 go build -ldflags="-s -w" -o mail_go .
|
||||
ok "编译完成: ${BUILD_DIR}/mail_go"
|
||||
}
|
||||
|
||||
# ======================== 部署文件 ========================
|
||||
deploy_files() {
|
||||
info "部署文件到 ${INSTALL_DIR} ..."
|
||||
|
||||
# 停止服务(如果正在运行),避免替换正在使用的二进制
|
||||
if systemctl is-active --quiet "${SERVICE_NAME}" 2>/dev/null; then
|
||||
info "停止运行中的服务 ..."
|
||||
systemctl stop "${SERVICE_NAME}"
|
||||
fi
|
||||
|
||||
# 复制二进制
|
||||
cp -f "${BUILD_DIR}/mail_go" "${INSTALL_DIR}/mail_go"
|
||||
chmod 755 "${INSTALL_DIR}/mail_go"
|
||||
|
||||
# 复制模板文件(目录结构: internal/web/templates/*.html + admin/*.html)
|
||||
cp -f "${BUILD_DIR}/internal/web/templates/"*.html "${INSTALL_DIR}/internal/web/templates/"
|
||||
cp -f "${BUILD_DIR}/internal/web/templates/admin/"*.html "${INSTALL_DIR}/internal/web/templates/admin/"
|
||||
|
||||
# 配置文件由程序首次启动时自动生成 + 缺失项自动补全,脚本不干预
|
||||
if [[ -f "${CONFIG_DIR}/mail_go.toml" ]]; then
|
||||
info "保留现有配置文件: ${CONFIG_DIR}/mail_go.toml"
|
||||
else
|
||||
info "配置文件不存在,程序首次启动将自动生成"
|
||||
fi
|
||||
|
||||
# 设置安装目录所有权
|
||||
chown -R "${SERVICE_USER}:${SERVICE_USER}" "${INSTALL_DIR}"
|
||||
|
||||
ok "文件部署完成"
|
||||
}
|
||||
|
||||
# ======================== systemd 服务 ========================
|
||||
install_service() {
|
||||
info "创建 systemd 服务文件 ..."
|
||||
|
||||
cat > "${SERVICE_FILE}" <<EOF
|
||||
[Unit]
|
||||
Description=${SERVICE_DESC}
|
||||
After=network.target
|
||||
Wants=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=${SERVICE_USER}
|
||||
Group=${SERVICE_USER}
|
||||
WorkingDirectory=${INSTALL_DIR}
|
||||
ExecStart=${INSTALL_DIR}/mail_go
|
||||
ExecReload=/bin/kill -HUP \$MAINPID
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
LimitNOFILE=65536
|
||||
|
||||
# 日志输出到 journald
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
SyslogIdentifier=${SERVICE_NAME}
|
||||
|
||||
# 安全加固:专用低权用户运行,无需额外文件系统沙箱
|
||||
# ProtectSystem=strict/full 会阻止写入 /etc 和 /opt 等目录,
|
||||
# 导致配置文件和 unix socket 无法创建,得不偿失
|
||||
ProtectHome=true
|
||||
NoNewPrivileges=true
|
||||
|
||||
# 环境变量
|
||||
Environment=GIN_MODE=release
|
||||
|
||||
# MailGo 需要绑定 25/465/143/993/110/995 等特权端口
|
||||
# 取消限制,允许非 root 绑定低端口号(需内核支持)
|
||||
# 或使用: sysctl net.ipv4.ip_unprivileged_port_start=0
|
||||
AmbientCapabilities=CAP_NET_BIND_SERVICE
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
chmod 644 "${SERVICE_FILE}"
|
||||
systemctl daemon-reload
|
||||
ok "systemd 服务文件已创建"
|
||||
}
|
||||
|
||||
# ======================== 安装主流程 ========================
|
||||
do_install() {
|
||||
info "========== 安装 ${SERVICE_NAME} =========="
|
||||
|
||||
# 1. 检查依赖
|
||||
check_root
|
||||
command -v git &>/dev/null || error "需要 git,请先安装"
|
||||
command -v go &>/dev/null || error "需要 go,请先安装"
|
||||
command -v gcc &>/dev/null || error "需要 gcc(CGO/SQLite 编译依赖),请先安装"
|
||||
|
||||
# 2. 创建用户
|
||||
ensure_user
|
||||
|
||||
# 3. 创建目录
|
||||
ensure_dirs
|
||||
|
||||
# 4. 拉取代码并编译
|
||||
build_binary
|
||||
|
||||
# 5. 部署文件
|
||||
deploy_files
|
||||
|
||||
# 6. 安装服务
|
||||
install_service
|
||||
|
||||
# 7. 启动并设置开机自启
|
||||
info "启动服务 ..."
|
||||
systemctl start "${SERVICE_NAME}"
|
||||
systemctl enable "${SERVICE_NAME}"
|
||||
|
||||
sleep 1
|
||||
if systemctl is-active --quiet "${SERVICE_NAME}"; then
|
||||
ok "服务启动成功!"
|
||||
else
|
||||
error "服务启动失败,请检查日志: journalctl -u ${SERVICE_NAME} -n 50"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
info "========== 安装完成 =========="
|
||||
echo " 程序目录: ${INSTALL_DIR}"
|
||||
echo " 配置文件: ${CONFIG_DIR}/mail_go.toml"
|
||||
echo " 数据目录: ${DATA_DIR}"
|
||||
echo " 附件目录: ${DATA_DIR}/attachments"
|
||||
echo " 日志目录: ${LOG_DIR}"
|
||||
echo " 服务管理: systemctl {start|stop|restart|status} ${SERVICE_NAME}"
|
||||
echo " 查看日志: journalctl -u ${SERVICE_NAME} -f"
|
||||
echo ""
|
||||
echo " 默认监听端口:"
|
||||
echo " SMTP 25 / 465(TLS) IMAP 143 / 993(TLS)"
|
||||
echo " POP3 110 / 995(TLS) Web :8080"
|
||||
echo ""
|
||||
echo " 默认管理员: admin@example.com / admin"
|
||||
warn "⚠ 请登录后立即修改默认密码!"
|
||||
}
|
||||
|
||||
# ======================== 卸载 ========================
|
||||
do_uninstall() {
|
||||
info "========== 卸载 ${SERVICE_NAME} =========="
|
||||
check_root
|
||||
|
||||
# 停止服务
|
||||
if systemctl is-active --quiet "${SERVICE_NAME}" 2>/dev/null; then
|
||||
systemctl stop "${SERVICE_NAME}"
|
||||
info "服务已停止"
|
||||
fi
|
||||
|
||||
# 禁用开机自启
|
||||
if systemctl is-enabled --quiet "${SERVICE_NAME}" 2>/dev/null; then
|
||||
systemctl disable "${SERVICE_NAME}"
|
||||
info "已禁用开机自启"
|
||||
fi
|
||||
|
||||
# 删除服务文件
|
||||
if [[ -f "${SERVICE_FILE}" ]]; then
|
||||
rm -f "${SERVICE_FILE}"
|
||||
systemctl daemon-reload
|
||||
info "服务文件已删除"
|
||||
fi
|
||||
|
||||
# 询问是否删除数据
|
||||
echo ""
|
||||
warn "以下目录包含运行数据,是否删除?"
|
||||
echo " [1] ${INSTALL_DIR} (程序文件+模板)"
|
||||
echo " [2] ${DATA_DIR} (数据库+附件)"
|
||||
echo " [3] ${CONFIG_DIR} (配置文件)"
|
||||
echo " [4] ${LOG_DIR} (日志)"
|
||||
echo " [0] 全部保留(默认)"
|
||||
echo " [a] 全部删除"
|
||||
echo ""
|
||||
read -rp "请选择 [0/a/组合如 14]: " choice
|
||||
|
||||
case "${choice:-0}" in
|
||||
0)
|
||||
info "保留所有数据目录"
|
||||
;;
|
||||
a|A)
|
||||
rm -rf "${INSTALL_DIR}" "${DATA_DIR}" "${CONFIG_DIR}" "${LOG_DIR}"
|
||||
info "所有数据目录已删除"
|
||||
;;
|
||||
*)
|
||||
[[ "${choice}" == *1* ]] && rm -rf "${INSTALL_DIR}" && info "已删除 ${INSTALL_DIR}"
|
||||
[[ "${choice}" == *2* ]] && rm -rf "${DATA_DIR}" && info "已删除 ${DATA_DIR}"
|
||||
[[ "${choice}" == *3* ]] && rm -rf "${CONFIG_DIR}" && info "已删除 ${CONFIG_DIR}"
|
||||
[[ "${choice}" == *4* ]] && rm -rf "${LOG_DIR}" && info "已删除 ${LOG_DIR}"
|
||||
;;
|
||||
esac
|
||||
|
||||
# 删除用户(如果没有其他依赖)
|
||||
if id "${SERVICE_USER}" &>/dev/null; then
|
||||
read -rp "是否删除系统用户 ${SERVICE_USER}?[y/N]: " del_user
|
||||
if [[ "${del_user}" =~ ^[Yy]$ ]]; then
|
||||
userdel "${SERVICE_USER}"
|
||||
info "用户 ${SERVICE_USER} 已删除"
|
||||
fi
|
||||
fi
|
||||
|
||||
# 清理编译目录
|
||||
rm -rf "${BUILD_DIR}"
|
||||
|
||||
ok "卸载完成"
|
||||
}
|
||||
|
||||
# ======================== 服务控制 ========================
|
||||
do_start() {
|
||||
check_root
|
||||
systemctl start "${SERVICE_NAME}"
|
||||
sleep 1
|
||||
if systemctl is-active --quiet "${SERVICE_NAME}"; then
|
||||
ok "服务已启动"
|
||||
else
|
||||
error "服务启动失败,查看日志: journalctl -u ${SERVICE_NAME} -n 50"
|
||||
fi
|
||||
}
|
||||
|
||||
do_stop() {
|
||||
check_root
|
||||
systemctl stop "${SERVICE_NAME}"
|
||||
ok "服务已停止"
|
||||
}
|
||||
|
||||
do_restart() {
|
||||
check_root
|
||||
systemctl restart "${SERVICE_NAME}"
|
||||
sleep 1
|
||||
if systemctl is-active --quiet "${SERVICE_NAME}"; then
|
||||
ok "服务已重启"
|
||||
else
|
||||
error "服务重启失败,查看日志: journalctl -u ${SERVICE_NAME} -n 50"
|
||||
fi
|
||||
}
|
||||
|
||||
do_status() {
|
||||
systemctl status "${SERVICE_NAME}" --no-pager || true
|
||||
}
|
||||
|
||||
# ======================== 入口 ========================
|
||||
case "${1:-}" in
|
||||
install) do_install ;;
|
||||
uninstall) do_uninstall ;;
|
||||
start) do_start ;;
|
||||
stop) do_stop ;;
|
||||
restart) do_restart ;;
|
||||
status) do_status ;;
|
||||
*)
|
||||
echo "用法: sudo $0 {install|uninstall|start|stop|restart|status}"
|
||||
echo ""
|
||||
echo " install — 完整安装/更新(拉代码+编译+部署+启动+开机自启)"
|
||||
echo " uninstall — 卸载服务(可选保留数据)"
|
||||
echo " start — 启动服务"
|
||||
echo " stop — 停止服务"
|
||||
echo " restart — 重启服务"
|
||||
echo " status — 查看服务状态"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
Reference in New Issue
Block a user