chore: 重写初始提交(清空历史,整理后全量提交)
This commit is contained in:
@@ -0,0 +1,307 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | YwxApp [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2026-2036 http://ywxapp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: ywxapp<admin@ywxapp.cn>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace addon\mqttbroker\controller\backend;
|
||||
|
||||
use think\facade\Db;
|
||||
use think\facade\View;
|
||||
use ywxapp\controller\BackendBase;
|
||||
use ywxapp\service\AddonService;
|
||||
use addon\mqttbroker\model\Connection;
|
||||
use addon\mqttbroker\model\Message;
|
||||
use addon\mqttbroker\service\Auth;
|
||||
use addon\mqttbroker\service\Store;
|
||||
|
||||
/**
|
||||
* MqttBroker 类
|
||||
*
|
||||
* @author ywxapp <admin@ywxapp.cn>
|
||||
*/
|
||||
class MqttBroker extends BackendBase
|
||||
{
|
||||
// 与 blog 后台一致:开发期放宽登录校验(生产请改回需登录)
|
||||
|
||||
protected $noNeedVerify = ['*'];
|
||||
|
||||
/**
|
||||
* 概览
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$stats = [
|
||||
'online' => Connection::where('status', 1)->count(),
|
||||
'total' => Connection::count(),
|
||||
'messages' => Message::count(),
|
||||
'topics' => Db::name('mqttbroker_topic')->count(),
|
||||
];
|
||||
$recent = Message::order('id', 'desc')->limit(10)->select();
|
||||
View::assign('stats', $stats);
|
||||
View::assign('recent', $recent);
|
||||
return View::fetch('admin/index');
|
||||
}
|
||||
|
||||
/**
|
||||
* 客户端连接列表
|
||||
*/
|
||||
public function connections()
|
||||
{
|
||||
$list = Connection::order('id', 'desc')->paginate(20);
|
||||
View::assign('list', $list);
|
||||
return View::fetch('admin/connections');
|
||||
}
|
||||
|
||||
/**
|
||||
* 消息日志
|
||||
*/
|
||||
public function messages()
|
||||
{
|
||||
$topic = input('topic', '');
|
||||
$query = Message::order('id', 'desc');
|
||||
if ($topic) {
|
||||
$query->where('topic', 'like', "%{$topic}%");
|
||||
}
|
||||
$list = $query->paginate(20);
|
||||
View::assign('list', $list);
|
||||
View::assign('topic', $topic);
|
||||
return View::fetch('admin/messages');
|
||||
}
|
||||
|
||||
/**
|
||||
* 发布消息表单
|
||||
*/
|
||||
public function publish()
|
||||
{
|
||||
return View::fetch('admin/publish');
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行发布(零外部依赖:写入出站队列,由常驻 Broker 消费后经内部路由投递)
|
||||
*/
|
||||
public function doPublish()
|
||||
{
|
||||
$topic = input('post.topic', '');
|
||||
$payload = input('post.payload', '');
|
||||
$qos = (int) input('post.qos', 0);
|
||||
if (!$topic) {
|
||||
$this->result->error('主题不能为空');
|
||||
}
|
||||
$ok = Store::enqueueManual($topic, $payload, $qos, 0, 'backend');
|
||||
if ($ok) {
|
||||
$this->result->success('已加入发布队列,Broker 将投递给在线订阅者');
|
||||
}
|
||||
$this->result->error('发布失败:无法写入出站队列');
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务设置
|
||||
*/
|
||||
public function setting()
|
||||
{
|
||||
$def = include ADDON_PATH . 'mqttbroker' . DIRECTORY_SEPARATOR . 'config.php';
|
||||
$saved = AddonService::config('mqttbroker');
|
||||
View::assign('def', $def);
|
||||
View::assign('saved', $saved ?: []);
|
||||
return View::fetch('admin/setting');
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存设置
|
||||
*/
|
||||
public function saveSetting()
|
||||
{
|
||||
$data = input('post.');
|
||||
AddonService::config('mqttbroker', $data);
|
||||
$this->result->success('保存成功');
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 认证账号管理
|
||||
* ========================================================== */
|
||||
|
||||
public function auth()
|
||||
{
|
||||
$list = Db::name('mqttbroker_auth')->order('id', 'desc')->paginate(20);
|
||||
View::assign('list', $list);
|
||||
return View::fetch('admin/auth');
|
||||
}
|
||||
|
||||
|
||||
public function saveAuth()
|
||||
{
|
||||
$id = (int) input('post.id', 0);
|
||||
$username = trim((string) input('post.username', ''));
|
||||
$password = (string) input('post.password', '');
|
||||
$isSuper = (int) input('post.is_superuser', 0);
|
||||
$status = (int) input('post.status', 1);
|
||||
$remark = (string) input('post.remark', '');
|
||||
if ($username === '') {
|
||||
$this->result->error('用户名不能为空');
|
||||
}
|
||||
$data = [
|
||||
'username' => $username,
|
||||
'is_superuser' => $isSuper ? 1 : 0,
|
||||
'status' => $status ? 1 : 0,
|
||||
'remark' => $remark,
|
||||
'update_at' => time(),
|
||||
];
|
||||
// 仅当填写了密码才更新(编辑时留空表示不改)
|
||||
if ($password !== '') {
|
||||
$data['password'] = Auth::hashPassword($password);
|
||||
}
|
||||
try {
|
||||
if ($id > 0) {
|
||||
Db::name('mqttbroker_auth')->where('id', $id)->update($data);
|
||||
} else {
|
||||
if ($password === '') {
|
||||
$this->result->error('新增账号必须设置密码');
|
||||
}
|
||||
$data['create_at'] = time();
|
||||
Db::name('mqttbroker_auth')->insert($data);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
$this->result->error('保存失败:' . $e->getMessage());
|
||||
}
|
||||
$this->result->success('保存成功');
|
||||
}
|
||||
|
||||
|
||||
public function deleteAuth()
|
||||
{
|
||||
$id = (int) input('post.id', 0);
|
||||
Db::name('mqttbroker_auth')->where('id', $id)->delete();
|
||||
$this->result->success('已删除');
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* ACL 规则管理
|
||||
* ========================================================== */
|
||||
|
||||
public function acl()
|
||||
{
|
||||
$list = Db::name('mqttbroker_acl')->order('sort', 'asc')->order('id', 'asc')->paginate(50);
|
||||
View::assign('list', $list);
|
||||
return View::fetch('admin/acl');
|
||||
}
|
||||
|
||||
|
||||
public function saveAcl()
|
||||
{
|
||||
$id = (int) input('post.id', 0);
|
||||
$data = [
|
||||
'target_type' => (string) input('post.target_type', 'all'),
|
||||
'target' => (string) input('post.target', ''),
|
||||
'topic' => (string) input('post.topic', ''),
|
||||
'access' => (int) input('post.access', 3),
|
||||
'allow' => (int) input('post.allow', 1),
|
||||
'sort' => (int) input('post.sort', 0),
|
||||
'remark' => (string) input('post.remark', ''),
|
||||
];
|
||||
if ($data['topic'] === '') {
|
||||
$this->result->error('主题过滤器不能为空');
|
||||
}
|
||||
try {
|
||||
if ($id > 0) {
|
||||
Db::name('mqttbroker_acl')->where('id', $id)->update($data);
|
||||
} else {
|
||||
$data['create_at'] = time();
|
||||
Db::name('mqttbroker_acl')->insert($data);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
$this->result->error('保存失败:' . $e->getMessage());
|
||||
}
|
||||
$this->result->success('保存成功(最长 30s 后在 Broker 生效)');
|
||||
}
|
||||
|
||||
|
||||
public function deleteAcl()
|
||||
{
|
||||
$id = (int) input('post.id', 0);
|
||||
Db::name('mqttbroker_acl')->where('id', $id)->delete();
|
||||
$this->result->success('已删除');
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 实时监控仪表盘(轮询 stats 接口)
|
||||
* ========================================================== */
|
||||
|
||||
public function stats()
|
||||
{
|
||||
$row = Db::name('mqttbroker_stats')->where('id', 1)->find();
|
||||
if (!$row) {
|
||||
$row = [
|
||||
'uptime' => 0, 'clients_online' => 0, 'clients_total' => 0,
|
||||
'subscriptions' => 0, 'messages_received' => 0, 'messages_sent' => 0,
|
||||
'retained' => 0, 'timestamp' => time(),
|
||||
];
|
||||
}
|
||||
// 以连接表的实时在线数补全(更贴近 DB 视角的在线状态)
|
||||
try {
|
||||
$row['db_online'] = Connection::where('status', 1)->count();
|
||||
} catch (\Throwable $e) {
|
||||
$row['db_online'] = $row['clients_online'] ?? 0;
|
||||
}
|
||||
$this->result->success($row);
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 转发规则(规则引擎 / 桥接到 EMQX)
|
||||
* ========================================================== */
|
||||
|
||||
public function rule()
|
||||
{
|
||||
$list = Db::name('mqttbroker_rule')->order('id', 'desc')->paginate(20);
|
||||
View::assign('list', $list);
|
||||
return View::fetch('admin/rule');
|
||||
}
|
||||
|
||||
|
||||
public function saveRule()
|
||||
{
|
||||
$id = (int) input('post.id', 0);
|
||||
$data = [
|
||||
'name' => (string) input('post.name', ''),
|
||||
'source_filter' => (string) input('post.source_filter', ''),
|
||||
'target_type' => (string) input('post.target_type', 'mqtt'),
|
||||
'target_broker' => (string) input('post.target_broker', ''),
|
||||
'target_clientid' => (string) input('post.target_clientid', ''),
|
||||
'target_username' => (string) input('post.target_username', ''),
|
||||
'target_password' => (string) input('post.target_password', ''),
|
||||
'target_topic' => (string) input('post.target_topic', ''),
|
||||
'target_qos' => (int) input('post.target_qos', 0),
|
||||
'enabled' => (int) input('post.enabled', 1),
|
||||
'remark' => (string) input('post.remark', ''),
|
||||
];
|
||||
if ($data['name'] === '' || $data['source_filter'] === '') {
|
||||
$this->result->error('规则名称与源主题过滤器必填');
|
||||
}
|
||||
if ($data['target_broker'] === '') {
|
||||
$this->result->error('目标 Broker 地址必填');
|
||||
}
|
||||
try {
|
||||
if ($id > 0) {
|
||||
Db::name('mqttbroker_rule')->where('id', $id)->update($data);
|
||||
} else {
|
||||
$data['create_at'] = time();
|
||||
Db::name('mqttbroker_rule')->insert($data);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
$this->result->error('保存失败:' . $e->getMessage());
|
||||
}
|
||||
$this->result->success('保存成功(最多 30s 后在 Broker 生效)');
|
||||
}
|
||||
|
||||
|
||||
public function deleteRule()
|
||||
{
|
||||
$id = (int) input('post.id', 0);
|
||||
Db::name('mqttbroker_rule')->where('id', $id)->delete();
|
||||
$this->result->success('已删除');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user