Files
2026-06-28 19:42:35 +08:00

8.8 KiB
Raw Permalink Blame History

kiri-mail-server 部署使用指南

一、安装

composer require game-worker/kiri-mail-server

二、创建配置文件

将默认配置复制到你的项目:

cp vendor/game-worker/kiri-mail-server/config/mail.php config/mail.php

编辑 config/mail.php

<?php
return [
    'smtp' => [
        'host'     => '0.0.0.0',
        'port'     => 25,
        'hostname' => 'mail.yourdomain.com',  // ← 改成你的域名
    ],
    'imap' => [
        'host' => '0.0.0.0',
        'port' => 143,
    ],
    'redis' => [
        'host' => '127.0.0.1',
        'port' => 6379,
    ],
    'storage' => [
        'path' => '/data/mail',  // ← 邮件存储目录,确保可写
    ],
    'domains' => [
        'local' => ['yourdomain.com'],  // ← 你的域名
    ],
    'database' => [
        'driver'   => 'mysql',
        'host'     => '127.0.0.1',
        'port'     => 3306,
        'database' => 'mail',
        'username' => 'root',
        'password' => 'your_mysql_password',  // ← 改成你的密码
    ],
    'auth' => [
        'type' => 'simple',  // 快速测试用 simple,生产用 database(见第五节)
        'users' => [          // 仅 simple 模式有效
            'admin@yourdomain.com' => 'password123',
        ],
        'require_auth_for_send' => true,
    ],
];

三、选择认证模式

模式 A:简单配置认证(测试用)

'auth' => [
    'type' => 'simple',
    'users' => [
        'user1@yourdomain.com' => 'mypassword',
        'user2@yourdomain.com' => 'anotherpassword',
    ],
],

模式 B:数据库认证(生产用)

config/databases.php 中添加 mail 连接:

// config/databases.php
return [
    'connections' => [
        // 原有连接...
        'db' => [ /* ... */ ],
        
        // 新增邮件数据库连接
        'mail' => [
            'id'          => 'mail',
            'cds'         => 'mysql:host=127.0.0.1;port=3306;dbname=mail',
            'username'    => 'root',
            'password'    => 'yourpassword',
            'database'    => 'mail',
            'tablePrefix' => '',
            'driver'      => 'mysql',
            'charset'     => 'utf8mb4',
            'pool'        => ['min' => 1, 'max' => 10],
        ],
    ],
];

执行 SQL 迁移:

mysql -u root -p mail < vendor/game-worker/kiri-mail-server/migrations/001_init.sql

config/mail.php 中设置:

'auth' => [
    'type' => 'database',
],

四、注册进程

编辑 config/servers.php

return [
    'process' => [
        \Kiri\MailServer\SmtpServerProcess::class,        // SMTP 收信 :25
        \Kiri\MailServer\ImapServerProcess::class,         // IMAP 读信 :143
        \Kiri\MailServer\OutboundDeliveryProcess::class,    // 外发投递
    ],
];

启动服务:

php kiri.php sw:server start

五、注册 Webmail 路由

创建 app/Controller/MailWebController.php

<?php
namespace App\Controller;

use Kiri\Router\Annotate\Get;
use Kiri\Router\Annotate\Post;
use Kiri\Router\Base\Controller;
use Kiri\MailServer\WebmailViews;
use Kiri\MailServer\Controller\MailApiController;
use Kiri\MailServer\Auth\DatabaseAuth;
use Kiri\MailServer\Storage\MaildirStorage;
use Kiri\MailServer\MailQueue;

class MailWebController extends Controller
{
    private MailApiController $api;

    public function init(): void
    {
        $redis = new \Redis();
        $redis->connect('127.0.0.1', 6379);
        
        $storage = new MaildirStorage('/data/mail');
        $mailQueue = new MailQueue($redis);
        $auth = new DatabaseAuth();
        
        $this->api = new MailApiController($auth, $storage, $mailQueue);
    }

    #[Get('/webmail')]
    public function index(): string
    {
        return WebmailViews::loginPage();
    }

    #[Post('/webmail/login')]
    public function login(): string
    {
        $email = $this->request->post('email');
        $password = $this->request->post('password');

        if ($this->api->verifyCredentials($email, $password)) {
            // 生产环境应使用 Session 或 JWT
            $url = '/webmail/inbox?email=' . urlencode($email);
            return $this->response->withHeader('Location', $url)->withStatus(302);
        }

        return WebmailViews::loginPage();
    }

    #[Get('/webmail/inbox')]
    public function inbox(): string
    {
        $email = $_GET['email'] ?? '';
        $data = $this->api->list($email);
        return WebmailViews::inboxPage($email, $data['messages'] ?? []);
    }

    #[Get('/webmail/read')]
    public function read(): string
    {
        $email = $_GET['email'] ?? '';
        $id = $_GET['id'] ?? '';
        $data = $this->api->read($email, $id);
        return WebmailViews::readPage($email, $data);
    }
}

六、创建管理面板路由

创建 app/Controller/MailAdminController.php

<?php
namespace App\Controller;

use Kiri\Router\Annotate\Get;
use Kiri\Router\Annotate\Post;
use Kiri\Router\Base\Controller;
use Kiri\MailServer\WebmailViews;
use Kiri\MailServer\Controller\AdminApiController;
use Kiri\MailServer\MailQueue;
use Kiri\MailServer\Model\Database;

class MailAdminController extends Controller
{
    private AdminApiController $api;

    public function init(): void
    {
        Database::init(config('mail.database'));
        
        $redis = new \Redis();
        $redis->connect('127.0.0.1', 6379);
        $mailQueue = new MailQueue($redis);
        
        $this->api = new AdminApiController($mailQueue);
    }

    #[Get('/admin')]
    public function dashboard(): string
    {
        $domains = $this->api->listDomains()['domains'] ?? [];
        $queueStats = $this->api->queueStats();
        return WebmailViews::adminDashboard($domains, $queueStats);
    }

    #[Get('/admin/users')]
    public function users(): string
    {
        $domainId = (int)($_GET['domain_id'] ?? 0);
        $users = $this->api->listUsers($domainId)['users'] ?? [];
        return WebmailViews::adminUsers($users, "Domain #{$domainId}");
    }

    #[Post('/api/admin/domains/create')]
    public function createDomain(): string
    {
        $result = $this->api->createDomain(
            $this->request->post('domain'),
            $this->request->post('description', ''),
            (int)$this->request->post('max_users', 100),
            (int)$this->request->post('max_quota', 0),
        );
        return json_encode($result);
    }

    #[Post('/api/admin/users/create')]
    public function createUser(): string
    {
        $result = $this->api->createUser(
            $this->request->post('email'),
            (int)$this->request->post('domain_id'),
            $this->request->post('password'),
        );
        return json_encode($result);
    }
}

七、测试验证

测试 SMTP 收信

# 连接 SMTP 服务器
telnet localhost 25

# 发送测试邮件
EHLO test
MAIL FROM:<sender@external.com>
RCPT TO:<admin@yourdomain.com>
DATA
From: sender@external.com
To: admin@yourdomain.com
Subject: Test Email

Hello, this is a test email.
.
QUIT

发送外部邮件

# 使用认证 (AUTH PLAIN)
telnet localhost 25
EHLO test
AUTH PLAIN AG1haWwAMTIzNA==    # base64(\0user\0pass)
MAIL FROM:<admin@yourdomain.com>
RCPT TO:<friend@gmail.com>
DATA
From: admin@yourdomain.com
To: friend@gmail.com
Subject: Outbound Test

Hello from my mail server!
.
QUIT

检查队列状态:

redis-cli
> ZCARD mail:queue:outbound       # 待发送数
> ZCARD mail:queue:outbound:dead  # 死信数

测试 IMAP 读信

telnet localhost 143

A001 LOGIN admin@yourdomain.com password123
A002 SELECT INBOX
A003 FETCH 1:* (FLAGS BODY[])
A004 LOGOUT

测试 Webmail

# 浏览器访问
open http://localhost:9501/webmail

八、DNS 记录配置(生产环境必须)

在你的 DNS 管理后台添加:

类型 名称 说明
A mail 你的服务器IP 邮件服务器主机
MX @ mail.yourdomain.com 邮件路由,优先级 10
TXT @ v=spf1 mx -all SPF 记录
TXT mail._domainkey v=DKIM1; k=rsa; p=你的公钥 DKIM 签名(需先生成密钥)

生成 DKIM 密钥:

openssl genrsa -out dkim_private.pem 2048
openssl rsa -in dkim_private.pem -pubout -out dkim_public.pem
# 将公钥内容放入 DNS TXT 记录: mail._domainkey.yourdomain.com

九、检查清单

  • config/mail.phphostname 改为真实域名
  • domains.local 添加了真实域名
  • storage.path 目录存在且可写
  • Redis 服务已启动 (redis-cli ping)
  • MySQL 数据库已创建并迁移
  • DNS MX 记录已配置(指向 mail.yourdomain.com
  • DNS SPF 记录已配置
  • 防火墙开放端口 25、143、587
  • config/servers.php 注册了三个进程