commit 2a5621d318f5e772715c43001e73b902741804bd Author: 沐念云 Date: Wed Jul 1 18:30:11 2026 +0800 feat: add downstream domain fix plugin diff --git a/DownstreamDomainFixPlugin.php b/DownstreamDomainFixPlugin.php new file mode 100644 index 0000000..c95e608 --- /dev/null +++ b/DownstreamDomainFixPlugin.php @@ -0,0 +1,32 @@ + 'DownstreamDomainFix', + 'title' => '下游推送域名修复', + 'description' => '批量修改指定客户名下服务器的下游推送域名', + 'status' => 1, + 'author' => '沐念云', + 'version' => '1.0', + 'module' => 'addons', + 'lang' => [ + 'chinese' => '下游推送域名修复', + 'chinese_tw' => '下游推送域名修復', + 'english' => 'Downstream Domain Fix', + ], + ]; + + public function install() + { + return true; + } + + public function uninstall() + { + return true; + } +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..71b31de --- /dev/null +++ b/README.md @@ -0,0 +1,149 @@ +# 下游推送域名修复插件 + +用于你站点名下代理用户的地址变更后,可通过本插件直接批量更换推送代理新地址。 + +## 功能说明 + +本插件用于批量修改指定客户名下服务器的下游推送地址。 + +当代理用户更换域名后,系统数据库中已保存的推送地址仍然可能是旧域名,导致上游向下游推送时请求失败,例如出现 `HTTP 404`。通过本插件可以按客户 ID 批量修复这些服务器的推送域名。 + +插件会更新数据库中: + +```text +shd_host.stream_info +``` + +字段内 JSON 的: + +```text +downstream_url +``` + +## 适用场景 + +- 代理站更换域名 +- 上游推送下游失败 +- 数据库中保存的下游推送地址仍是旧地址 +- 需要按客户 ID 批量修复该客户名下所有服务器的推送地址 + +## 安装方式 + +将插件目录上传到系统插件目录: + +```text +public/plugins/addons/downstream_domain_fix +``` + +然后进入后台插件管理页面,找到并安装: + +```text +下游推送域名修复 +``` + +## 使用方法 + +进入插件后台菜单: + +```text +修复推送域名 +``` + +填写以下信息: + +| 字段 | 说明 | 示例 | +| --- | --- | --- | +| 客户ID | 需要批量修复的客户 ID,对应 `shd_host.uid` | `8888` | +| 指定推送域名 | 新的代理站地址 | `https://www.baidu.com` | + +点击: + +```text +开始修复 +``` + +插件会自动处理该客户名下符合条件的服务器。 + +## 处理规则 + +插件只会处理满足以下条件的服务器: + +1. `shd_host.uid` 等于输入的客户 ID +2. `stream_info` 不为空 +3. `stream_info` 中存在完整的下游推送信息: + - `downstream_url` + - `downstream_token` + - `downstream_id` + +插件会跳过以下服务器: + +- 没有下游推送信息的服务器 +- 下游推送信息不完整的服务器 +- 当前推送域名已经等于目标域名的服务器 + +## 修改内容 + +插件实际修改的是: + +```text +shd_host.stream_info.downstream_url +``` + +不会修改: + +- 产品信息 +- 客户信息 +- 订单信息 +- 服务器状态 +- `downstream_token` +- `downstream_id` + +## 域名填写说明 + +填写站点根地址即可,例如: + +```text +https://www.baidu.com +``` + +如果系统安装在子目录,则填写到系统根目录,例如: + +```text +https://www.baidu.com/finance +``` + +## 处理结果 + +执行后页面会显示: + +- 匹配服务器数量 +- 成功更新数量 +- 跳过数量 +- 每台服务器的处理结果 + +结果表格包含: + +- Host ID +- 主机名 +- 主 IP +- 状态 +- 原推送域名 +- 新推送域名 +- 处理结果 + +## 注意事项 + +- 执行前建议先备份数据库。 +- 请确认客户 ID 正确,避免修改错误客户名下的服务器。 +- 本插件只修改本地数据库中的推送域名,不主动调用上游接口。 +- 修改完成后,上游后续推送会读取新的 `downstream_url`。 + +## 插件信息 + +| 项目 | 内容 | +| --- | --- | +| 插件名称 | 下游推送域名修复 | +| 插件标识 | `DownstreamDomainFix` | +| 插件目录 | `downstream_domain_fix` | +| 作者 | 沐念云 | +| 版本 | `1.0` | diff --git a/controller/AdminIndexController.php b/controller/AdminIndexController.php new file mode 100644 index 0000000..1c87716 --- /dev/null +++ b/controller/AdminIndexController.php @@ -0,0 +1,90 @@ +request->isPost()) { + $result = $this->fixDomain($uid, $domain); + } + + $this->assign('Title', '下游推送域名修复'); + $this->assign('uid', $uid ?: ''); + $this->assign('domain', $domain); + $this->assign('result', $result); + return $this->fetch('/index'); + } + + private function fixDomain($uid, $domain) + { + $domain = rtrim($domain, '/'); + if ($uid <= 0) { + return ['status' => 400, 'msg' => '客户ID不能为空', 'total' => 0, 'updated' => 0, 'skipped' => 0, 'list' => []]; + } + if (!$this->isValidDomain($domain)) { + return ['status' => 400, 'msg' => '推送域名必须以 http:// 或 https:// 开头', 'total' => 0, 'updated' => 0, 'skipped' => 0, 'list' => []]; + } + + $hosts = \think\Db::name('host') + ->field('id,uid,domain,dedicatedip,domainstatus,stream_info') + ->where('uid', $uid) + ->where('stream_info', '<>', '') + ->select() + ->toArray(); + + $result = ['status' => 200, 'msg' => '处理完成', 'total' => count($hosts), 'updated' => 0, 'skipped' => 0, 'list' => []]; + + foreach ($hosts as $host) { + $streamInfo = json_decode($host['stream_info'], true) ?: []; + if (empty($streamInfo['downstream_url']) || empty($streamInfo['downstream_token']) || empty($streamInfo['downstream_id'])) { + $result['skipped']++; + $result['list'][] = $this->buildRow($host, '', $domain, '跳过:没有完整下游推送信息'); + continue; + } + + $oldDomain = rtrim($streamInfo['downstream_url'], '/'); + if ($oldDomain === $domain) { + $result['skipped']++; + $result['list'][] = $this->buildRow($host, $oldDomain, $domain, '跳过:域名未变化'); + continue; + } + + $streamInfo['downstream_url'] = $domain; + \think\Db::name('host')->where('id', $host['id'])->update([ + 'stream_info' => json_encode($streamInfo, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE), + ]); + + $result['updated']++; + $result['list'][] = $this->buildRow($host, $oldDomain, $domain, '已更新'); + } + + if (function_exists('active_log')) { + active_log(sprintf('插件批量修复下游推送域名,客户ID:%d,目标域名:%s,更新:%d,跳过:%d', $uid, $domain, $result['updated'], $result['skipped'])); + } + + return $result; + } + + private function isValidDomain($domain) + { + return strpos($domain, 'https://') === 0 || strpos($domain, 'http://') === 0; + } + + private function buildRow($host, $oldDomain, $newDomain, $status) + { + return [ + 'id' => $host['id'], + 'domain' => $host['domain'], + 'dedicatedip' => $host['dedicatedip'], + 'domainstatus' => $host['domainstatus'], + 'old_domain' => $oldDomain, + 'new_domain' => $newDomain, + 'status' => $status, + ]; + } +} diff --git a/lang/en-us.php b/lang/en-us.php new file mode 100644 index 0000000..881ab67 --- /dev/null +++ b/lang/en-us.php @@ -0,0 +1,2 @@ + '修复推送域名', + 'url' => 'DownstreamDomainFix://AdminIndex/index', + 'custom' => 0, + 'lang' => [ + 'chinese' => '修复推送域名', + 'chinese_tw' => '修復推送域名', + 'english' => 'Fix Domain', + ], + ], +]; diff --git a/menuclientarea.php b/menuclientarea.php new file mode 100644 index 0000000..881ab67 --- /dev/null +++ b/menuclientarea.php @@ -0,0 +1,2 @@ + + .ddf-page { + background: linear-gradient(135deg, #f7fbff 0%, #eef5ff 100%); + border-radius: 14px; + padding: 22px; + } + .ddf-hero { + position: relative; + overflow: hidden; + border-radius: 16px; + padding: 28px 32px; + color: #fff; + background: linear-gradient(135deg, #1d4ed8 0%, #0f766e 100%); + box-shadow: 0 14px 34px rgba(20, 82, 160, .18); + margin-bottom: 22px; + } + .ddf-hero:after { + content: ""; + position: absolute; + width: 220px; + height: 220px; + right: -70px; + top: -70px; + border-radius: 50%; + background: rgba(255, 255, 255, .14); + } + .ddf-hero h3 { + margin: 0 0 10px; + font-size: 24px; + font-weight: 700; + letter-spacing: .5px; + } + .ddf-hero p { + margin: 0; + max-width: 760px; + line-height: 1.8; + color: rgba(255, 255, 255, .9); + } + .ddf-form-card, + .ddf-result-card { + border: 1px solid #e6eef8; + border-radius: 16px; + background: #fff; + box-shadow: 0 10px 28px rgba(15, 52, 96, .08); + } + .ddf-form-card { + padding: 26px 28px; + } + .ddf-field label { + color: #26384d; + font-weight: 600; + margin-bottom: 8px; + } + .ddf-field .form-control { + height: 42px; + border-color: #d9e4f2; + border-radius: 10px; + box-shadow: none; + } + .ddf-field .form-control:focus { + border-color: #2563eb; + box-shadow: 0 0 0 3px rgba(37, 99, 235, .12); + } + .ddf-submit { + min-width: 150px; + height: 42px; + border: 0; + border-radius: 10px; + background: linear-gradient(135deg, #2563eb 0%, #0f766e 100%); + box-shadow: 0 10px 20px rgba(37, 99, 235, .2); + font-weight: 600; + } + .ddf-result-card { + margin-top: 22px; + padding: 18px; + } + .ddf-summary { + display: flex; + flex-wrap: wrap; + gap: 12px; + margin-bottom: 16px; + } + .ddf-summary-item { + min-width: 150px; + border-radius: 12px; + padding: 14px 16px; + background: #f5f8fc; + } + .ddf-summary-item strong { + display: block; + font-size: 20px; + color: #12304f; + } + .ddf-summary-item span { + color: #6b7a90; + } + .ddf-table { + margin-bottom: 0; + border-radius: 12px; + overflow: hidden; + } + .ddf-table thead th { + border-bottom: 0; + background: #f1f6fd; + color: #34506c; + font-weight: 700; + white-space: nowrap; + } + .ddf-table tbody td { + vertical-align: middle; + color: #34495e; + } + .ddf-badge { + display: inline-block; + border-radius: 999px; + padding: 4px 10px; + background: #e8f5ee; + color: #17834f; + font-size: 12px; + font-weight: 600; + } + + +
+
+
+
+
+
+
{$Title}
+
+ {foreach $PluginsAdminMenu as $v} + {if $v['custom']} + {$v.name} + {else/} + {$v.name} + {/if} + {/foreach} +
+
+ +
+
+

代理推送地址批量修复

+

用于你站点名下代理用户的地址变更后,可通过本插件直接批量更换推送代理新地址。

+
+ +
+
+
+
+
+ + +
+
+
+
+ + +
+
+
+ +
+
+
+
+ + {if $result} +
+
+ {$result.msg};匹配 {$result.total} 台,更新 {$result.updated} 台,跳过 {$result.skipped} 台。 +
+ +
+
{$result.total}匹配服务器
+
{$result.updated}成功更新
+
{$result.skipped}跳过数量
+
+ + {if $result.list} +
+ + + + + + + + + + + + + + {foreach $result.list as $row} + + + + + + + + + + {/foreach} + +
Host ID主机名主IP状态原推送域名新推送域名处理结果
{$row.id}{$row.domain}{$row.dedicatedip}{$row.domainstatus}{$row.old_domain}{$row.new_domain}{$row.status}
+
+ {/if} +
+ {/if} +
+
+
+
+
+