diff --git a/.workbuddy/memory/2026-06-01.md b/.workbuddy/memory/2026-06-01.md index 0000e0f..73be414 100644 --- a/.workbuddy/memory/2026-06-01.md +++ b/.workbuddy/memory/2026-06-01.md @@ -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/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..f65bcd6 --- /dev/null +++ b/README.md @@ -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 diff --git a/install.sh b/install.sh new file mode 100644 index 0000000..ca3b9fb --- /dev/null +++ b/install.sh @@ -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}" </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