diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php
index f62d4ff..dfe61d2 100644
--- a/app/Console/Kernel.php
+++ b/app/Console/Kernel.php
@@ -2,10 +2,10 @@
namespace App\Console;
-use App\Jobs\Host\DeleteSuspendedHostJob;
+use App\Jobs\Host\DeleteHostJob;
use App\Jobs\Host\DispatchHostCostQueueJob;
use App\Jobs\Host\ScanAllHostsJob;
-use App\Jobs\Module\FetchModuleJob;
+use App\Jobs\Module\DispatchFetchModuleJob;
use App\Jobs\Module\SendModuleEarningsJob;
use App\Jobs\User\CheckAndChargeBalanceJob;
use App\Jobs\User\ClearTasksJob;
@@ -33,8 +33,8 @@ protected function schedule(Schedule $schedule): void
// 扣费
$schedule->job(new DispatchHostCostQueueJob(now()->minute))->everyMinute()->withoutOverlapping()->onOneServer();
- // 获取模块暴露的信息(服务器等)
- $schedule->job(new FetchModuleJob())->withoutOverlapping()->everyMinute();
+ // 获取模块暴露的信息(服务器等,检查模块状态)
+ $schedule->job(new DispatchFetchModuleJob())->withoutOverlapping()->everyMinute();
// 推送工单
$schedule->job(new PushWorkOrderJob())->everyMinute()->onOneServer();
@@ -45,7 +45,7 @@ protected function schedule(Schedule $schedule): void
$schedule->job(new ClearTasksJob())->weekly();
// 删除暂停或部署时间超过 3 天以上的主机
- $schedule->job(new DeleteSuspendedHostJob())->hourly();
+ $schedule->job(new DeleteHostJob())->hourly();
// 检查主机是否存在于模块
$schedule->job(new ScanAllHostsJob())->everyThirtyMinutes()->withoutOverlapping()->onOneServer();
diff --git a/app/Http/Controllers/Admin/HostController.php b/app/Http/Controllers/Admin/HostController.php
index c8528d3..274ac9b 100644
--- a/app/Http/Controllers/Admin/HostController.php
+++ b/app/Http/Controllers/Admin/HostController.php
@@ -58,7 +58,7 @@ public function update(Request $request, Host $host): RedirectResponse
{
$request->validate([
'name' => 'sometimes|string|max:255',
- 'status' => 'sometimes|in:running,stopped,suspended,pending',
+ 'status' => 'sometimes|in:running,stopped,suspended,pending,locked,unavailable',
'price' => 'sometimes|numeric',
'managed_price' => 'nullable|numeric',
]);
diff --git a/app/Http/Controllers/Api/HostController.php b/app/Http/Controllers/Api/HostController.php
index 9b081f7..3f38d59 100644
--- a/app/Http/Controllers/Api/HostController.php
+++ b/app/Http/Controllers/Api/HostController.php
@@ -30,6 +30,10 @@ public function update(HostRequest $request, Host $host): JsonResponse
'status' => 'required|in:running,stopped',
]);
+ if ($host->status === 'locked' || $host->status === 'unavailable') {
+ return $this->error('当前主机状态不允许操作');
+ }
+
$user = $request->user();
if ($user->balance < 0.5) {
diff --git a/app/Http/Controllers/Api/ModuleController.php b/app/Http/Controllers/Api/ModuleController.php
index 7bb037e..3ab62a3 100644
--- a/app/Http/Controllers/Api/ModuleController.php
+++ b/app/Http/Controllers/Api/ModuleController.php
@@ -7,6 +7,7 @@
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
+use Illuminate\Support\Facades\Cache;
class ModuleController extends Controller
{
@@ -17,6 +18,13 @@ public function index(): JsonResponse
return $this->success($modules);
}
+ public function servers(Module $module): JsonResponse
+ {
+ $servers = Cache::get('module:' . $module->id . ':servers', []);
+
+ return $this->success($servers);
+ }
+
public function call(Request $request, Module $module): JsonResponse|Response
{
return (new \App\Http\Controllers\Module\ModuleController())->call($request, $module);
diff --git a/app/Jobs/Host/DeleteSuspendedHostJob.php b/app/Jobs/Host/DeleteHostJob.php
similarity index 61%
rename from app/Jobs/Host/DeleteSuspendedHostJob.php
rename to app/Jobs/Host/DeleteHostJob.php
index 234946f..778a9fb 100644
--- a/app/Jobs/Host/DeleteSuspendedHostJob.php
+++ b/app/Jobs/Host/DeleteHostJob.php
@@ -10,7 +10,7 @@
// use Illuminate\Contracts\Queue\ShouldBeUnique;
-class DeleteSuspendedHostJob implements ShouldQueue
+class DeleteHostJob implements ShouldQueue
{
use InteractsWithQueue, Queueable, SerializesModels;
@@ -38,11 +38,25 @@ public function handle(): void
}
});
- // 查找部署时间超过3天以上的 host
+ // 查找部署时间超过 3 天以上的 host
(new Host)->where('status', 'pending')->where('created_at', '<', now()->subDays(3))->chunk(100, function ($hosts) {
foreach ($hosts as $host) {
dispatch(new HostJob($host, 'delete'));
}
});
+
+ // 查找不可用时间超过 3 天以上的 host
+ (new Host)->where('status', 'unavailable')->where('unavailable_at', '<', now()->subDays(3))->chunk(100, function ($hosts) {
+ foreach ($hosts as $host) {
+ dispatch(new HostJob($host, 'delete'));
+ }
+ });
+
+ // 查找锁定时间超过 3 天以上的 host
+ (new Host)->where('status', 'locked')->where('locked_at', '<', now()->subDays(3))->chunk(100, function ($hosts) {
+ foreach ($hosts as $host) {
+ dispatch(new HostJob($host, 'delete'));
+ }
+ });
}
}
diff --git a/app/Jobs/Host/HostJob.php b/app/Jobs/Host/HostJob.php
index 1c9801e..c98bd0b 100644
--- a/app/Jobs/Host/HostJob.php
+++ b/app/Jobs/Host/HostJob.php
@@ -38,6 +38,13 @@ public function __construct(HostModel $host, $type = 'post')
public function handle(): void
{
$host = $this->host;
+
+ // 忽略 unavailable 状态的 host
+ if ($host->status === 'unavailable') {
+ return;
+ }
+
+
$host->load(['module']);
switch ($this->type) {
diff --git a/app/Jobs/Host/UpdateOrSuspendedHostJob.php b/app/Jobs/Host/UpdateOrDeleteHostJob.php
similarity index 82%
rename from app/Jobs/Host/UpdateOrSuspendedHostJob.php
rename to app/Jobs/Host/UpdateOrDeleteHostJob.php
index 3f8356c..289e155 100644
--- a/app/Jobs/Host/UpdateOrSuspendedHostJob.php
+++ b/app/Jobs/Host/UpdateOrDeleteHostJob.php
@@ -11,7 +11,7 @@
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Log;
-class UpdateOrSuspendedHostJob implements ShouldQueue
+class UpdateOrDeleteHostJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
@@ -41,8 +41,10 @@ public function handle(): void
if ($response['status'] === 200) {
$host->update(Arr::except($response['json'], ['id', 'user_id', 'module_id', 'created_at', 'updated_at']));
} else if ($response['status'] === 404) {
- Log::warning($host->module->name . ' ' . $host->name . ' ' . $host->id . ' 不存在,标记为暂停。');
- dispatch(new HostJob($host, 'delete'));
+ Log::warning($host->module->name . ' ' . $host->name . ' ' . $host->id . ' 不存在,标记为不可用。');
+ // dispatch(new HostJob($host, 'delete'));
+ $host->status = 'unavailable';
+ $host->save();
}
}
}
diff --git a/app/Jobs/Module/DispatchFetchModuleJob.php b/app/Jobs/Module/DispatchFetchModuleJob.php
new file mode 100644
index 0000000..7559e42
--- /dev/null
+++ b/app/Jobs/Module/DispatchFetchModuleJob.php
@@ -0,0 +1,41 @@
+whereNotNull('url')->chunk(100, function ($modules) {
+ foreach ($modules as $module) {
+ dispatch(new FetchModuleJob($module));
+ }
+ });
+ }
+}
diff --git a/app/Jobs/Module/FetchModuleJob.php b/app/Jobs/Module/FetchModuleJob.php
index 2207ffa..54a3c55 100644
--- a/app/Jobs/Module/FetchModuleJob.php
+++ b/app/Jobs/Module/FetchModuleJob.php
@@ -2,10 +2,12 @@
namespace App\Jobs\Module;
+use App\Jobs\Host\UpdateOrDeleteHostJob;
use App\Models\Module;
use Exception;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
+use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Cache;
@@ -13,16 +15,19 @@
class FetchModuleJob implements ShouldQueue
{
- use InteractsWithQueue, Queueable, SerializesModels;
+ use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
+
+
+ protected Module $module;
/**
* Create a new job instance.
*
* @return void
*/
- public function __construct()
+ public function __construct(Module $module)
{
- //
+ $this->module = $module;
}
/**
@@ -32,78 +37,62 @@ public function __construct()
*/
public function handle(): void
{
- // 获取运行完成的时间
+ $module = $this->module;
- // $last_run = Cache::get('servers_updated_at', false);
- // if ($last_run !== false) {
- // // 如果和上次运行时间间隔小于一分钟,则不运行
- // if (now()->diffInMinutes($last_run) < 1) {
- // return;
- // }
- // }
+ $servers = Cache::get('module:' . $module->id . ':servers', []);
- //
- (new Module)->whereNotNull('url')->chunk(100, function ($modules) {
- $servers = [];
+ try {
+ $response = $module->http()->get('remote');
+ } catch (Exception $e) {
+ Log::error($e->getMessage());
+ return;
+ }
- foreach ($modules as $module) {
- try {
- $response = $module->http()->get('remote');
- } catch (Exception $e) {
- Log::error($e->getMessage());
- continue;
- }
-
- if ($response->successful()) {
-
- // 如果模块状态不为 up,则更新为 up
- if ($module->status !== 'up') {
- $module->status = 'up';
- }
-
- $json = $response->json();
-
- if (isset($json['servers']) && is_array($json['servers'])) {
- // 只保留 name, status, meta
- $servers = array_merge($servers, array_map(function ($server) use ($module) {
- return [
- 'name' => $server['name'],
- 'status' => $server['status'],
- 'meta' => $server['meta'] ?? [],
- 'created_at' => $server['created_at'] ?? now(),
- 'updated_at' => $server['updated_at'] ?? now(),
- 'module' => [
- 'id' => $module->id,
- 'name' => $module->name,
- ]
- ];
- }, $json['servers']));
-
- // broadcast(new Servers($servers));
- }
-
- } else {
-
- // if module return maintenance, then set module status to maintenance
- if ($response->status() == 503) {
- $module->status = 'maintenance';
- } else {
- $module->status = 'down';
- }
- }
-
- $module->save();
+ if ($response->successful()) {
+ // 如果模块状态不为 up,则更新为 up
+ if ($module->status !== 'up') {
+ $module->status = 'up';
}
- // if local
- if (config('app.env') === 'local') {
- Cache::forever('servers', $servers);
+ $json = $response->json();
+
+ if (isset($json['servers']) && is_array($json['servers'])) {
+ // 只保留 name, status, meta
+ $servers = array_merge($servers, array_map(function ($server) use ($module) {
+ return [
+ 'name' => $server['name'],
+ 'status' => $server['status'],
+ 'meta' => $server['meta'] ?? [],
+ 'created_at' => $server['created_at'] ?? now(),
+ 'updated_at' => $server['updated_at'] ?? now(),
+ 'module' => [
+ 'id' => $module->id,
+ 'name' => $module->name,
+ ]
+ ];
+ }, $json['servers']));
+
+ // broadcast(new Servers($servers));
+ }
+
+ } else {
+ // if module return maintenance, then set module status to maintenance
+ $status = $response->status();
+ if ($status == 503 || $status == 429 || $status == 502) {
+ $module->status = 'maintenance';
} else {
- Cache::put('servers', $servers, now()->addMinutes(10));
+ $module->status = 'down';
}
+ }
+
+ $module->save();
+
+ // if local
+ if (config('app.env') === 'local') {
+ Cache::forever('module:' . $module->id . ':servers', $servers);
+ } else {
+ Cache::put('module:' . $module->id . ':servers', $servers, now()->addMinutes(10));
+ }
- // 缓存运行完成的时间
- // Cache::put('servers_updated_at', now(), now()->addMinutes(10));
- });
}
}
diff --git a/app/Models/Host.php b/app/Models/Host.php
index 10c6dd1..9477c08 100644
--- a/app/Models/Host.php
+++ b/app/Models/Host.php
@@ -4,7 +4,7 @@
use App\Events\Users;
use App\Jobs\Host\HostJob;
-use App\Jobs\Host\UpdateOrSuspendedHostJob;
+use App\Jobs\Host\UpdateOrDeleteHostJob;
use App\Notifications\WebNotification;
use GeneaLabs\LaravelModelCaching\Traits\Cachable;
use Illuminate\Database\Eloquent\Collection;
@@ -54,23 +54,35 @@ protected static function boot()
});
static::created(function (self $model) {
-
$model->user->notify(new WebNotification($model, 'hosts.created'));
-
});
- static::updating(function ($model) {
+ static::updating(function (self $model) {
if ($model->isDirty('status')) {
if ($model->status == 'suspended') {
$model->suspended_at = now();
} else {
$model->suspended_at = null;
}
+
+ if ($model->status == 'locked') {
+ $model->locked_at = now();
+ } else {
+ $model->locked_at = null;
+ }
+
+ if ($model->status == 'unavailable') {
+ $model->unavailable_at = now();
+ } else {
+ $model->unavailable_at = null;
+ }
}
// 调度任务
- dispatch(new HostJob($model, 'patch'));
+ if ($model->status !== 'unavailable') {
+ dispatch(new HostJob($model, 'patch'));
+ }
broadcast(new Users($model->user_id, 'hosts.updating', $model));
});
@@ -307,7 +319,7 @@ public function addLog(string $amount = "0"): bool
public function updateOrDelete(): bool
{
- dispatch(new UpdateOrSuspendedHostJob($this));
+ dispatch(new UpdateOrDeleteHostJob($this));
return true;
}
diff --git a/app/Models/Module.php b/app/Models/Module.php
index 2e64917..cc80e6a 100644
--- a/app/Models/Module.php
+++ b/app/Models/Module.php
@@ -20,10 +20,9 @@ class Module extends Authenticatable
public $incrementing = false;
- // primary key
- public $timestamps = false;
protected $table = 'modules';
protected $keyType = 'string';
+
protected $fillable = [
'id',
'name',
diff --git a/database/migrations/2023_01_20_153636_add_unavailable_status_to_hosts_table.php b/database/migrations/2023_01_20_153636_add_unavailable_status_to_hosts_table.php
new file mode 100644
index 0000000..0962c78
--- /dev/null
+++ b/database/migrations/2023_01_20_153636_add_unavailable_status_to_hosts_table.php
@@ -0,0 +1,36 @@
+timestamp('unavailable_at')->nullable()->comment('不可用时间')->after('suspended_at');
+ $table->timestamp('locked_at')->nullable()->comment('锁定时间')->after('unavailable_at');
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down(): void
+ {
+ \Illuminate\Support\Facades\DB::statement("ALTER TABLE `hosts` CHANGE `status` `status` ENUM('running','stopped','error','suspended','pending') CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'pending';");
+
+ \Illuminate\Support\Facades\Schema::table('hosts', function (Blueprint $table) {
+ $table->dropColumn('unavailable_at');
+ $table->dropColumn('locked_at');
+ });
+ }
+};
diff --git a/resources/views/components/host-status.blade.php b/resources/views/components/host-status.blade.php
index 8a118bb..14e96e6 100644
--- a/resources/views/components/host-status.blade.php
+++ b/resources/views/components/host-status.blade.php
@@ -20,6 +20,14 @@
错误
@break
+ @case('unavailable')
+ 不可用
+ @break
+
+ @case('locked')
+ 锁定
+ @break
+
@default
{{ $status }}
@endswitch