| name | btpanel_mail |
| description | 宝塔邮局管理技能,提供邮局服务管理、域名配置、邮箱账户管理、DNS 解析配置等功能 |
| author | Baota Team |
| category | mail |
| tags | ["mail","postfix","dovecot","email","邮局"] |
Skill: btpanel_mail
宝塔邮局管理技能
宝塔邮局(BT Mail)是基于宝塔面板的企业级邮件系统管理工具,提供完整的邮件服务器搭建、域名配置、邮箱账户管理、DNS 解析配置等功能。

功能概述
本技能提供以下核心功能:
| 功能模块 | 描述 |
|---|
| 邮局初始化 | 一键安装和配置 Postfix、Dovecot、Rspamd 等邮件服务 |
| 域名管理 | 添加、删除、查询邮件域名 |
| 邮箱账户 | 创建、删除、批量管理邮箱账户 |
| DNS 解析 | 自动配置 MX、SPF、DKIM、DMARC 等 DNS 记录 |
| 服务监控 | 检查邮件服务运行状态 |
| 证书管理 | SSL 证书配置和自动续签 |
核心服务组件
宝塔邮局依赖以下核心服务:
| 服务 | 端口 | 用途 |
|---|
| Postfix | 25 (SMTP) | 邮件发送和接收 |
| Dovecot | 143 (IMAP), 993 (IMAPS) | 邮件收取 |
| Rspamd | 11333 | 垃圾邮件过滤 |
| OpenDKIM | - | 邮件签名验证 |
AI 使用约束(重要)
使用本技能时,AI 必须遵循以下原则:
🚫 绝对禁止的操作
- 禁止任何手动操作:无论什么情况下,都不得使用手动命令行操作(如
os.system、public.ExecShell 等直接执行系统命令)
- 禁止绕过面板接口:所有操作必须通过宝塔面板已封装的接口进行
- 禁止修改配置文件:不得直接编辑
/etc/postfix/、/etc/dovecot/ 等配置文件
✅ 必须遵守的规则
- 接口选择:根据授权状态自动选择可用接口
- 内部判断授权状态,不向用户展示判断过程
- 只展示操作步骤和结果
- 检查安装状态:在执行任何操作前,必须先检查邮局是否已安装
- 初始化检查:检查邮局是否已初始化,未初始化时调用
setup_mail_sys
- 服务状态检查:可以使用检测类接口检查服务状态
- 密码强度:创建邮箱时使用符合强度要求的密码(大小写字母 + 数字,长度≥8)
- 批量操作提示:批量创建邮箱时先告知用户操作内容
- 数据安全:不主动泄露邮箱密码等敏感信息
接口使用说明
| 接口类型 | 可用功能 | 说明 |
|---|
| 完整功能 | 所有接口 | 包括安装、初始化、自动 DNS 配置等 |
| 基础功能 | 核心接口 | 包括初始化、域名管理、邮箱管理等 |
执行流程示例
AI: 我将为您执行以下操作:
1. 检查邮局安装状态
2. [如未安装] 安装邮局服务
3. 检查邮局初始化状态
4. [如未初始化] 初始化邮局系统
5. 检查邮局服务状态(Postfix、Dovecot、Rspamd)
6. 添加域名 example.com
7. 配置 DNS 解析记录
正在获取数据,请稍候...
[调用接口]
[展示结果]
注意:内部会自动判断可用接口,不向用户展示判断过程。
Python 接口调用方法
完整调用流程(内部实现)
import sys
import os
import time
import json
sys.path.insert(0, '/www/server/panel')
sys.path.insert(0, '/www/server/panel/class')
os.chdir('/www/server/panel')
from mod.base import public_aap as public
def _check_auth():
auth_file = '/www/server/panel/data/auth_list.json'
if os.path.exists(auth_file):
try:
auth_data = json.loads(public.readFile(auth_file))
ltd = auth_data.get('ltd', 0)
current_time = int(time.time())
return ltd > current_time
except:
return False
return False
def _get_mail_interface():
if _check_auth():
from mailModel.mainModel import main as MailMain
from mailModel.manageModel import main as MailManage
return MailMain(), MailManage(), True
else:
MAIL_SYS_PLUGIN = '/www/server/panel/plugin/mail_sys/mail_sys_main.py'
if not os.path.exists(MAIL_SYS_PLUGIN):
return None, None, False
sys.path.insert(0, '/www/server/panel/plugin/mail_sys')
from mail_sys_main import mail_sys_main as MailMain
return MailMain(), None, False
mail, manage, has_full_access = _get_mail_interface()
if not mail:
print("邮局服务不可用")
sys.exit(1)
class Args:
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
print("="*60)
print("宝塔邮局部署")
print("="*60)
print("\n[步骤 1] 检查邮局安装状态...")
if has_full_access:
install_check = manage.install_status(None)
if not install_check.get('status', False):
print("正在安装邮局服务...")
manage.install_service(None)
print("安装任务已提交,请等待完成后重试")
sys.exit(0)
else:
if not os.path.exists('/www/server/panel/plugin/mail_sys/mail_sys_main.py'):
print("邮局插件未安装")
sys.exit(1)
print("✓ 邮局已安装")
print("\n[步骤 2] 检查邮局初始化状态...")
init_check = mail.check_mail_sys(None)
if not init_check.get('status', False):
print("正在初始化邮局系统...")
args = Args(
domain='example.com',
a_record='mail.example.com',
ips='1.2.3.4'
)
result = mail.setup_mail_sys(args)
print(f"初始化完成:{result.get('msg', '')}")
else:
print("✓ 邮局已初始化")
print("\n[步骤 3] 检查服务状态...")
status = mail.get_service_status(None)
print(f"Postfix: {'✓' if status['data']['postfix'] else '✗'}")
print(f"Dovecot: {'✓' if status['data']['dovecot'] else '✗'}")
print(f"Rspamd: {'✓' if status['data']['rspamd'] else '✗'}")
核心接口方法
1. 检查邮局初始化状态
result = mail.check_mail_sys(None)
{
"status": True,
"msg": "邮局系统已经存在,重装之前请先卸载!"
}
{
"status": False,
"msg": "之前没有安装过邮局系统,请放心安装!"
}
if result['status']:
print("邮局已初始化")
else:
print("需要初始化邮局系统")
2. 初始化邮局系统
class Args:
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
args = Args(
domain='example.com',
a_record='mail.example.com',
ips='1.2.3.4'
)
result = mail.setup_mail_sys(args)
3. 检查服务状态
result = mail.get_service_status(None)
{
"status": True,
"data": {
"postfix": True,
"dovecot": True,
"rspamd": True,
"opendkim": False,
"change_rspamd": False
}
}
4. 添加域名
args = Args(
domain='example.com',
a_record='mail.example.com',
ips='1.2.3.4'
)
result = mail.add_domain(args)
args = Args(
domain='example.com',
a_record='mail.example.com',
ips='1.2.3.4',
mailboxes=1000,
mailbox_quota='1 GB',
quota='10 GB',
rate_limit=12
)
result = mail.add_domain_new(args)
5. 创建邮箱账户
args = Args(
username='user@example.com',
full_name='User Name',
password='Password123',
quota='1 GB',
is_admin=0,
active=1
)
result = mail.add_mailbox(args)
args = Args(
content=json.dumps([
"user1|user1@example.com|Password123|1|GB",
"user2|user2@example.com|Password123|1|GB"
])
)
result = mail.add_mailbox_multiple(args)
6. 自动配置 DNS 解析(完整功能可用)
args = Args(
domain='example.com',
a_record='mail.example.com'
)
result = mail.auto_create_dns_record(args)
7. 查询域名列表
result = mail.get_domain_name(None)
{
"status": True,
"data": ["example.com", "test.com"]
}
8. 查询邮箱列表
args = Args(p=1, size=50)
result = mail.get_mailboxs(args)
{
"status": True,
"data": [
{
"username": "user@example.com",
"full_name": "User Name",
"quota": 1073741824,
"active": 1,
"domain": "example.com"
}
],
"page": "分页 HTML"
}
9. 删除域名
args = Args(domain='example.com')
result = mail.delete_domain(args)
{
"status": True,
"msg": "删除域成功! (example.com)"
}
10. 删除邮箱
args = Args(username='user@example.com')
result = mail.delete_mailbox(args)
{
"status": True,
"msg": "删除邮箱用户成功! (user@example.com)"
}
10. 发送邮件
args = Args(
username='user@example.com',
mail_from='user@example.com',
mail_to=['user1@example.com'],
subject='测试邮件',
content='这是一封测试邮件。',
subtype='html',
smtp_server='localhost'
)
result = mail.send_mail(args)
{
"status": True,
"msg": "发送邮件成功"
}
接口可用性对照表
| 接口名称 | 完整功能 | 基础功能 | 说明 |
|---|
install_service | ✅ | ❌ | 安装邮局服务 |
install_status | ✅ | ❌ | 检查安装状态 |
check_mail_sys | ✅ | ✅ | 检查初始化状态 |
setup_mail_sys | ✅ | ✅ | 初始化邮局系统 |
get_service_status | ✅ | ✅ | 检查服务状态 |
add_domain | ✅ | ✅ | 添加域名 |
add_domain_new | ✅ | ✅ | 添加域名(跳过 DNS 验证) |
add_mailbox | ✅ | ✅ | 创建邮箱 |
add_mailbox_multiple | ✅ | ✅ | 批量创建邮箱 |
get_domain_name | ✅ | ✅ | 查询域名列表 |
get_mailboxs | ✅ | ✅ | 查询邮箱列表 |
auto_create_dns_record | ✅ | ❌ | 自动配置 DNS |
delete_domain | ✅ | ✅ | 删除域名 |
delete_mailbox | ✅ | ✅ | 删除邮箱 |
send_mail | ✅ | ✅ | 发送邮件 |
重要提示:
- ✅ 表示接口可用
- ❌ 表示接口不可用
- 完整功能:包含所有邮局管理功能
- 基础功能:包含核心邮局管理功能
常用场景示例
场景一:完整部署流程
import sys
import os
import json
import time
sys.path.insert(0, '/www/server/panel')
sys.path.insert(0, '/www/server/panel/class')
os.chdir('/www/server/panel')
from mod.base import public_aap as public
def _check_auth():
auth_file = '/www/server/panel/data/auth_list.json'
if os.path.exists(auth_file):
try:
auth_data = json.loads(public.readFile(auth_file))
ltd = auth_data.get('ltd', 0)
current_time = int(time.time())
return ltd > current_time
except:
return False
return False
def _get_mail_interface():
if _check_auth():
from mailModel.mainModel import main as MailMain
from mailModel.manageModel import main as MailManage
return MailMain(), MailManage(), True
else:
MAIL_SYS_PLUGIN = '/www/server/panel/plugin/mail_sys/mail_sys_main.py'
if not os.path.exists(MAIL_SYS_PLUGIN):
return None, None, False
sys.path.insert(0, '/www/server/panel/plugin/mail_sys')
from mail_sys_main import mail_sys_main as MailMain
return MailMain(), None, False
print("="*60)
print("宝塔邮局部署")
print("="*60)
mail, manage, has_full_access = _get_mail_interface()
if not mail:
print("\n✗ 邮局服务不可用")
sys.exit(1)
class Args:
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
print("\n[步骤 1] 检查邮局安装状态...")
if has_full_access:
install_check = manage.install_status(None)
if not install_check.get('status', False):
print("正在安装邮局服务...")
install_result = manage.install_service(None)
print(f"安装结果:{install_result.get('msg', '')}")
print("请等待安装完成后重新运行")
sys.exit(0)
else:
if not os.path.exists('/www/server/panel/plugin/mail_sys/mail_sys_main.py'):
print("✗ 邮局插件未安装")
sys.exit(1)
print("✓ 邮局已安装")
print("\n[步骤 2] 检查邮局初始化状态...")
init_check = mail.check_mail_sys(None)
if not init_check.get('status', False):
print("正在初始化邮局系统...")
args = Args(
domain='example.com',
a_record='mail.example.com',
ips='1.2.3.4'
)
result = mail.setup_mail_sys(args)
print(f"初始化完成:{result.get('msg', '')}")
else:
print("✓ 邮局已初始化")
print("\n[步骤 3] 检查服务状态...")
status = mail.get_service_status(None)
print(f"Postfix: {'✓ 运行中' if status['data']['postfix'] else '✗ 未运行'}")
print(f"Dovecot: {'✓ 运行中' if status['data']['dovecot'] else '✗ 未运行'}")
print(f"Rspamd: {'✓ 运行中' if status['data']['rspamd'] else '✗ 未运行'}")
print("\n[步骤 4] 添加域名...")
args = Args(
domain='example.com',
a_record='mail.example.com',
ips='1.2.3.4',
mailboxes=1000,
mailbox_quota='1 GB',
quota='10 GB',
rate_limit=12
)
result = mail.add_domain_new(args)
print(f"域名添加:{result.get('msg', '')}")
if has_full_access:
print("\n[步骤 5] 自动配置 DNS...")
args = Args(domain='example.com', a_record='mail.example.com')
result = mail.auto_create_dns_record(args)
print(f"DNS 配置:{result.get('msg', '')}")
else:
print("\n[步骤 5] 跳过 DNS 自动配置")
print(" 请手动配置 DNS 记录")
print("\n[步骤 6] 创建管理员邮箱...")
args = Args(
username='admin@example.com',
full_name='Admin',
password='AdminPass123',
quota='2 GB',
is_admin=1,
active=1
)
result = mail.add_mailbox(args)
print(f"邮箱创建:{result.get('msg', '')}")
print("\n" + "="*60)
print("邮局配置完成!")
print("="*60)
场景二:批量创建测试邮箱
import sys
import os
import json
import time
sys.path.insert(0, '/www/server/panel')
sys.path.insert(0, '/www/server/panel/class')
os.chdir('/www/server/panel')
from mod.base import public_aap as public
def _check_auth():
auth_file = '/www/server/panel/data/auth_list.json'
if os.path.exists(auth_file):
try:
auth_data = json.loads(public.readFile(auth_file))
ltd = auth_data.get('ltd', 0)
current_time = int(time.time())
return ltd > current_time
except:
return False
return False
def _get_mail_interface():
if _check_auth():
from mailModel.mainModel import main as MailMain
from mailModel.manageModel import main as MailManage
return MailMain(), MailManage(), True
else:
MAIL_SYS_PLUGIN = '/www/server/panel/plugin/mail_sys/mail_sys_main.py'
if not os.path.exists(MAIL_SYS_PLUGIN):
return None, None, False
sys.path.insert(0, '/www/server/panel/plugin/mail_sys')
from mail_sys_main import mail_sys_main as MailMain
return MailMain(), None, False
mail, manage, has_full_access = _get_mail_interface()
if not mail:
print("邮局服务不可用")
sys.exit(1)
class Args:
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
if has_full_access:
install_check = manage.install_status(None)
if not install_check.get('status', False):
print("邮局未安装")
sys.exit(1)
check_result = mail.check_mail_sys(None)
if not check_result.get('status', False):
print("邮局未初始化")
sys.exit(1)
domain = 'example.com'
count = 50
password = 'TestPass123'
print(f"开始创建 {count} 个测试邮箱...")
success = 0
fail = 0
for i in range(1, count + 1):
args = Args(
username=f'test{i}@{domain}',
full_name=f'Test User {i}',
password=password,
quota='1 GB',
is_admin=0,
active=1
)
result = mail.add_mailbox(args)
if result.get('status', False):
success += 1
else:
fail += 1
print(f"创建完成:成功 {success} 个,失败 {fail} 个")
数据库结构
宝塔邮局使用 SQLite 数据库存储配置,数据库文件位于:
- 主数据库:
/www/vmail/postfixadmin.db
- 日志数据库:
/www/vmail/postfixmaillog.db
主要数据表
domain 表(域名)
| 字段 | 类型 | 说明 |
|---|
| domain | TEXT | 域名 |
| a_record | TEXT | A 记录 |
| mailboxes | INTEGER | 邮箱数量限制 |
| mailbox_quota | INTEGER | 单个邮箱配额(字节) |
| quota | INTEGER | 域名总配额(字节) |
| rate_limit | INTEGER | 发送频率限制 |
| created | DATETIME | 创建时间 |
mailbox 表(邮箱)
| 字段 | 类型 | 说明 |
|---|
| username | TEXT | 邮箱地址 |
| full_name | TEXT | 显示名称 |
| password | TEXT | 加密密码 |
| password_encode | TEXT | 可解密密码 |
| quota | INTEGER | 配额(字节) |
| domain | TEXT | 所属域名 |
| active | INTEGER | 是否启用 |
| is_admin | INTEGER | 是否管理员 |
| created | DATETIME | 创建时间 |
| modified | DATETIME | 修改时间 |
配置文件位置
| 文件 | 路径 | 说明 |
|---|
| Postfix 主配置 | /etc/postfix/main.cf | Postfix 配置 |
| Dovecot 主配置 | /etc/dovecot/dovecot.conf | Dovecot 配置 |
| Rspamd 配置 | /etc/rspamd/ | Rspamd 配置目录 |
| 虚拟邮箱目录 | /www/vmail/ | 邮箱数据存储 |
| 邮局插件目录 | /www/server/panel/plugin/mail_sys/ | 插件文件 |
| 邮局模型目录 | /www/server/panel/class/mailModel/ | 核心代码 |
| 授权文件 | /www/server/panel/data/auth_list.json | 授权信息 |
| 安装检测文件 | /etc/postfix/sqlite_virtual_domains_maps.cf | 邮局安装标志 |
接口实现说明
setup_mail_sys 接口
setup_mail_sys 接口在两个版本中各有实现:
| 版本 | 文件位置 | 类名 |
|---|
| 完整功能 | /www/server/panel/class/mailModel/mainModel.py | main (mailModel.mainModel) |
| 基础功能 | /www/server/panel/plugin/mail_sys/mail_sys_main.py | mail_sys_main |
使用方式:
result = mail.setup_mail_sys(args)
接口选择逻辑(内部实现)
def _get_mail_interface():
"""
根据授权状态选择可用接口
此函数为内部实现,不向用户展示
"""
auth_file = '/www/server/panel/data/auth_list.json'
has_full_access = False
if os.path.exists(auth_file):
try:
auth_data = json.loads(public.readFile(auth_file))
ltd = auth_data.get('ltd', 0)
current_time = int(time.time())
has_full_access = ltd > current_time
except:
pass
if has_full_access:
from mailModel.mainModel import main as MailMain
from mailModel.manageModel import main as MailManage
return MailMain(), MailManage(), True
else:
MAIL_SYS_PLUGIN = '/www/server/panel/plugin/mail_sys/mail_sys_main.py'
if not os.path.exists(MAIL_SYS_PLUGIN):
return None, None, False
sys.path.insert(0, '/www/server/panel/plugin/mail_sys')
from mail_sys_main import mail_sys_main as MailMain
return MailMain(), None, False
故障排查
1. 邮局未安装
from mailModel.manageModel import main as MailManage
manage = MailManage()
result = manage.install_status(None)
print(f"安装状态:{result.get('status', False)}")
if not os.path.exists('/www/server/panel/plugin/mail_sys/mail_sys_main.py'):
print("邮局插件未安装")
2. 邮局未初始化
result = mail.check_mail_sys(None)
if not result.get('status', False):
print("邮局未初始化,需要执行 setup_mail_sys")
args = Args(domain='example.com', ...)
mail.setup_mail_sys(args)
3. 服务无法启动
systemctl status postfix
systemctl status dovecot
systemctl status rspamd
journalctl -u postfix -f
journalctl -u dovecot -f
4. 接口导入错误
try:
from mailModel.mainModel import main as MailMain
from mailModel.manageModel import main as MailManage
print("✓ 接口导入成功")
except Exception as e:
print(f"✗ 导入失败:{e}")
MAIL_SYS_PLUGIN = '/www/server/panel/plugin/mail_sys/mail_sys_main.py'
if os.path.exists(MAIL_SYS_PLUGIN):
try:
sys.path.insert(0, '/www/server/panel/plugin/mail_sys')
from mail_sys_main import mail_sys_main as MailMain
print("✓ 接口导入成功")
except Exception as e:
print(f"✗ 导入失败:{e}")
5. 域名 DNS 验证失败
检查 DNS 记录是否正确配置:
dig MX example.com
dig mail.example.com
dig TXT example.com
6. 邮箱无法登录
- 检查密码强度是否符合要求
- 确认邮箱状态是否为 active=1
- 检查 Dovecot 服务是否运行
- 验证 IMAP 端口(143/993)是否开放
安全建议
- 启用 SSL/TLS: 为邮件服务配置 SSL 证书
- 强密码策略: 密码必须包含大小写字母和数字,长度≥8
- 定期更新: 保持 Postfix、Dovecot 为最新版本
- 监控日志: 定期检查邮件日志发现异常
- 限制发送频率: 设置合理的 rate_limit 防止滥用
- 配置防火墙: 仅开放必要的端口(25、143、993、995)
- 使用面板接口:所有操作必须通过面板接口,禁止手动操作
重要提示
🚫 禁止的操作
- 禁止手动操作:不得使用
os.system、public.ExecShell 等直接执行系统命令
- 禁止修改配置文件:不得直接编辑
/etc/postfix/、/etc/dovecot/ 等配置文件
- 禁止绕过面板接口:所有操作必须通过宝塔面板已封装的接口进行
- 禁止混用接口:mailModel 和插件版本的接口不要混用
✅ 推荐的做法
- 接口选择:内部自动判断可用接口,不向用户展示判断过程
- 检查安装状态:操作前检查邮局是否已安装
- 检查初始化状态:操作前使用
check_mail_sys 检查
- 初始化操作:未初始化时使用
setup_mail_sys 进行初始化
- 检查服务状态:可以使用检测类接口检查服务状态
- 用户可见输出:只展示操作步骤和结果,不展示接口判断逻辑