2026.06.8

This commit is contained in:
brusnitsyn
2026-06-18 17:45:41 +09:00
parent 698422e0ba
commit f163b95663
15 changed files with 695 additions and 20 deletions

View File

@@ -0,0 +1,126 @@
<?php
namespace App\Http\Controllers\Web\Admin;
use App\Http\Controllers\Controller;
use App\Models\ReplicationLog;
use App\Models\ReplicationSchedule;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Inertia\Inertia;
class ReplicationController extends Controller
{
public function index()
{
abort_unless(auth()->user()->isAdmin(), 403);
$logs = ReplicationLog::query()
->latest('id')
->limit(50)
->get()
->map(fn (ReplicationLog $l) => [
'id' => $l->id,
'status' => $l->status,
'schedule_id' => $l->schedule_id,
'tables_success' => $l->tables_success,
'tables_failed' => $l->tables_failed,
'errors' => $l->errors,
'received_at' => optional($l->received_at)->toIso8601String(),
]);
$schedules = ReplicationSchedule::orderBy('name')->get()->map(fn (ReplicationSchedule $s) => [
'id' => $s->id,
'schedule_id' => $s->schedule_id,
'name' => $s->name,
'is_active' => $s->is_active,
]);
return Inertia::render('Admin/Replication/Index', [
'logs' => $logs,
'schedules' => $schedules,
'configured' => (bool) config('services.syncio.base_url'),
]);
}
public function storeSchedule(Request $request)
{
abort_unless(auth()->user()->isAdmin(), 403);
$data = $request->validate([
'schedule_id' => ['required', 'string', 'max:255', 'unique:replication_schedules,schedule_id'],
'name' => ['nullable', 'string', 'max:255'],
]);
ReplicationSchedule::create([
'schedule_id' => $data['schedule_id'],
'name' => $data['name'] ?? null,
'is_active' => true,
]);
return back()->with('success', 'Расписание добавлено.');
}
public function updateSchedule(Request $request, ReplicationSchedule $schedule)
{
abort_unless(auth()->user()->isAdmin(), 403);
$data = $request->validate([
'name' => ['nullable', 'string', 'max:255'],
'is_active' => ['required', 'boolean'],
]);
$schedule->update($data);
return back()->with('success', 'Расписание обновлено.');
}
public function destroySchedule(ReplicationSchedule $schedule)
{
abort_unless(auth()->user()->isAdmin(), 403);
$schedule->delete();
return back()->with('success', 'Расписание удалено.');
}
public function run(Request $request)
{
abort_unless(auth()->user()->isAdmin(), 403);
$data = $request->validate([
'schedule_id' => ['required', 'string', 'max:255'],
]);
$baseUrl = rtrim((string) config('services.syncio.base_url'), '/');
if ($baseUrl === '') {
return back()->with('error', 'Не задан SYNCIO_BASE_URL — запуск репликации недоступен.');
}
$scheduleId = $data['schedule_id'];
$url = "{$baseUrl}/migrations/schedules/{$scheduleId}/run";
try {
$response = Http::acceptJson()
->when(
config('services.syncio.token'),
fn ($http) => $http->withToken(config('services.syncio.token'))
)
->timeout(30)
->post($url);
if ($response->failed()) {
Log::warning('Syncio run failed', ['status' => $response->status(), 'body' => $response->body()]);
return back()->with('error', "Репликатор вернул ошибку ({$response->status()}).");
}
return back()->with('success', 'Репликация запущена. Результат придёт по вебхуку.');
} catch (\Throwable $e) {
Log::error('Syncio run exception', ['message' => $e->getMessage()]);
return back()->with('error', 'Не удалось связаться с репликатором: '.$e->getMessage());
}
}
}