feat(admin-frontend): 新增节点自动上线托管能力

为节点新增 auto_online 字段与后台同步任务,
仅对开启托管的节点按在线状态自动同步前台显示。

管理端补齐单节点与批量开关、列表标识与统计,
并在自动上线启用时禁用手动显隐切换。

后端新增定时命令、保存校验、批量更新支持、
数据库迁移与单元测试,保证托管逻辑可落地。
This commit is contained in:
yinjianm
2026-04-28 00:08:12 +08:00
parent 9af9dd0df7
commit 73b1696b0a
26 changed files with 361 additions and 33 deletions
@@ -0,0 +1,29 @@
<?php
namespace App\Console\Commands;
use App\Services\ServerAutoOnlineService;
use Illuminate\Console\Command;
class SyncServerAutoOnline extends Command
{
protected $signature = 'sync:server-auto-online';
protected $description = 'Sync visible status for nodes with auto online enabled';
public function handle(ServerAutoOnlineService $service): int
{
$result = $service->sync();
$this->info(sprintf(
'Server auto online synced: total=%d updated=%d shown=%d hidden=%d unchanged=%d',
$result['total'],
$result['updated'],
$result['shown'],
$result['hidden'],
$result['unchanged']
));
return self::SUCCESS;
}
}
+1
View File
@@ -44,6 +44,7 @@ class Kernel extends ConsoleKernel
$schedule->command('horizon:snapshot')->everyFiveMinutes()->onOneServer();
// cleanup stale online_count (GC for Redis TTL expiration)
$schedule->command('cleanup:online-status')->everyFiveMinutes()->onOneServer();
$schedule->command('sync:server-auto-online')->everyFiveMinutes()->onOneServer()->withoutOverlapping(5);
// backup Timing
// if (env('ENABLE_AUTO_BACKUP_AND_UPDATE', false)) {
// $schedule->command('backup:database', ['true'])->daily()->onOneServer();
@@ -81,6 +81,7 @@ class ManageController extends Controller
$params = $request->validate([
'id' => 'required|integer',
'show' => 'nullable|integer',
'auto_online' => 'nullable|boolean',
'machine_id' => 'nullable|integer',
'enabled' => 'nullable|boolean',
]);
@@ -93,6 +94,9 @@ class ManageController extends Controller
if (array_key_exists('show', $params)) {
$server->show = (int) $params['show'];
}
if (array_key_exists('auto_online', $params)) {
$server->auto_online = (bool) $params['auto_online'];
}
if (array_key_exists('machine_id', $params)) {
$server->machine_id = $params['machine_id'] ?: null;
}
@@ -226,6 +230,7 @@ class ManageController extends Controller
'ids' => 'required|array',
'ids.*' => 'integer',
'show' => 'nullable|integer|in:0,1',
'auto_online' => 'nullable|boolean',
'enabled' => 'nullable|boolean',
'machine_id' => 'nullable|integer',
'host' => 'sometimes|required|string',
@@ -243,6 +248,9 @@ class ManageController extends Controller
if (array_key_exists('show', $params) && $params['show'] !== null) {
$update['show'] = (int) $params['show'];
}
if (array_key_exists('auto_online', $params) && $params['auto_online'] !== null) {
$update['auto_online'] = (bool) $params['auto_online'];
}
if (array_key_exists('enabled', $params) && $params['enabled'] !== null) {
$update['enabled'] = (bool) $params['enabled'];
}
+1
View File
@@ -116,6 +116,7 @@ class ServerSave extends FormRequest
'spectific_key' => 'nullable|string',
'code' => 'nullable|string',
'show' => '',
'auto_online' => 'nullable|boolean',
'name' => 'required|string',
'group_ids' => 'nullable|array',
'route_ids' => 'nullable|array',
+3 -1
View File
@@ -24,6 +24,7 @@ use Illuminate\Database\Eloquent\Casts\Attribute;
* @property array|null $route_ids 路由IDs
* @property array|null $tags 标签
* @property boolean $show 是否显示
* @property boolean $auto_online 是否根据在线状态自动同步显示
* @property string|null $allow_insecure 是否允许不安全
* @property string|null $network 网络类型
* @property int|null $parent_id 父节点ID
@@ -36,7 +37,7 @@ use Illuminate\Database\Eloquent\Casts\Attribute;
* @property int $updated_at
*
* @property-read Server|null $parent 父节点
* @property-read \Illuminate\Database\Eloquent\Collection<int, StatServer> $stats 节点统计
* @property-read \Illuminate\Database\Eloquent\Collection<int, StatServer> $stats 节点统计
* @property-read \Illuminate\Database\Eloquent\Collection<int, ServerGfwCheck> $gfwChecks 墙状态检测记录
*
* @property-read int|null $last_check_at 最后检查时间(Unix时间戳)
@@ -125,6 +126,7 @@ class Server extends Model
'last_check_at' => 'integer',
'last_push_at' => 'integer',
'show' => 'boolean',
'auto_online' => 'boolean',
'enabled' => 'boolean',
'created_at' => 'timestamp',
'updated_at' => 'timestamp',
+40
View File
@@ -0,0 +1,40 @@
<?php
namespace App\Services;
use App\Models\Server;
class ServerAutoOnlineService
{
public function sync(): array
{
$servers = Server::query()
->where('auto_online', true)
->get();
$result = [
'total' => $servers->count(),
'updated' => 0,
'shown' => 0,
'hidden' => 0,
'unchanged' => 0,
];
foreach ($servers as $server) {
$shouldShow = (int) $server->available_status !== Server::STATUS_OFFLINE;
if ((bool) $server->show === $shouldShow) {
$result['unchanged']++;
continue;
}
$server->show = $shouldShow;
$server->save();
$result['updated']++;
$shouldShow ? $result['shown']++ : $result['hidden']++;
}
return $result;
}
}