withCount('migrationRuns') ->get(); return Inertia::render('Migrations/Index', [ 'schedules' => $schedules, ]); } public function create() { $sourceDatabases = SourceDatabase::where('is_active', true)->get(); $targetDatabases = TargetDatabase::where('is_active', true)->get(); $tables = Table::with('sourceDatabase')->get()->groupBy('source_database_id'); return Inertia::render('Migrations/ScheduleForm', [ 'schedule' => null, 'sourceDatabases' => $sourceDatabases, 'targetDatabases' => $targetDatabases, 'tables' => $tables, ]); } public function store(Request $request) { $validated = $request->validate([ 'name' => 'required|string|max:255', 'source_database_id' => 'required|exists:source_databases,id', 'target_database_id' => 'required|exists:target_databases,id', 'tables' => 'required|array|min:1', 'tables.*' => 'exists:tables,id', 'cron_expression' => 'required|string|max:100', 'timezone' => 'nullable|string|max:50', 'is_active' => 'boolean', 'run_in_parallel' => 'boolean', 'batch_size' => 'integer|min:1', 'truncate_before_migration' => 'boolean', 'create_indexes_after' => 'boolean', 'python_script_path' => 'nullable|string', 'description' => 'nullable|string', ]); $validated['timezone'] = $validated['timezone'] ?? 'UTC'; $validated['is_active'] = $validated['is_active'] ?? true; $validated['is_incremental'] = $validated['is_incremental'] ?? false; $validated['incremental_column'] = $validated['is_incremental'] ? ($validated['incremental_column'] ?? 'updated_at') : null; $validated['use_life_table'] = $validated['use_life_table'] ?? false; if ($validated['use_life_table']) { $validated['life_table_name'] = $validated['life_table_name'] ?? null; $validated['life_id_column'] = $validated['life_id_column'] ?? null; $validated['base_id_column'] = $validated['base_id_column'] ?? null; $validated['operation_column'] = $validated['operation_column'] ?? 'x_Operation'; $validated['datetime_column'] = $validated['datetime_column'] ?? 'x_DateTime'; } $validated['run_in_parallel'] = $validated['run_in_parallel'] ?? false; $validated['batch_size'] = $validated['batch_size'] ?? 1000; $validated['truncate_before_migration'] = $validated['truncate_before_migration'] ?? false; $validated['create_indexes_after'] = $validated['create_indexes_after'] ?? true; $validated['python_script_args'] = [ 'incremental' => $validated['is_incremental'], 'incremental_column' => $validated['incremental_column'], 'use_life_table' => $validated['use_life_table'], 'life_table_name' => $validated['life_table_name'], 'life_id_column' => $validated['life_id_column'], 'base_id_column' => $validated['base_id_column'], 'operation_column' => $validated['operation_column'], 'datetime_column' => $validated['datetime_column'], ]; $schedule = MigrationSchedule::create($validated); // Attach tables to schedule $schedule->scheduledTables()->sync($validated['tables']); return redirect()->route('migrations.index') ->with('success', 'Migration schedule created successfully.'); } public function edit(MigrationSchedule $schedule) { $schedule->load('scheduledTables'); $sourceDatabases = SourceDatabase::where('is_active', true)->get(); $targetDatabases = TargetDatabase::where('is_active', true)->get(); $tables = Table::with('sourceDatabase') ->where('source_database_id', $schedule->source_database_id) ->get(); return Inertia::render('Migrations/ScheduleForm', [ 'schedule' => $schedule, 'sourceDatabases' => $sourceDatabases, 'targetDatabases' => $targetDatabases, 'tables' => $tables, ]); } public function update(Request $request, MigrationSchedule $schedule) { $validated = $request->validate([ 'name' => 'required|string|max:255', 'source_database_id' => 'required|exists:source_databases,id', 'target_database_id' => 'required|exists:target_databases,id', 'tables' => 'required|array|min:1', 'tables.*' => 'exists:tables,id', 'cron_expression' => 'required|string|max:100', 'timezone' => 'nullable|string|max:50', 'is_active' => 'boolean', 'is_incremental' => 'boolean', 'incremental_column' => 'nullable|string|max:100', 'use_life_table' => 'boolean', 'life_table_name' => 'nullable|string|max:255', 'life_id_column' => 'nullable|string|max:100', 'base_id_column' => 'nullable|string|max:100', 'operation_column' => 'nullable|string|max:100', 'datetime_column' => 'nullable|string|max:100', 'run_in_parallel' => 'boolean', 'batch_size' => 'integer|min:1', 'truncate_before_migration' => 'boolean', 'create_indexes_after' => 'boolean', 'python_script_path' => 'nullable|string', 'description' => 'nullable|string', ]); $validated['is_incremental'] = $validated['is_incremental'] ?? false; $validated['incremental_column'] = $validated['is_incremental'] ? ($validated['incremental_column'] ?? 'updated_at') : null; $validated['use_life_table'] = $validated['use_life_table'] ?? false; if ($validated['use_life_table']) { $validated['life_table_name'] = $validated['life_table_name'] ?? null; $validated['life_id_column'] = $validated['life_id_column'] ?? null; $validated['base_id_column'] = $validated['base_id_column'] ?? null; $validated['operation_column'] = $validated['operation_column'] ?? 'x_Operation'; $validated['datetime_column'] = $validated['datetime_column'] ?? 'x_DateTime'; } $validated['python_script_args'] = [ 'incremental' => $validated['is_incremental'], 'incremental_column' => $validated['incremental_column'], 'use_life_table' => $validated['use_life_table'], 'life_table_name' => $validated['life_table_name'], 'life_id_column' => $validated['life_id_column'], 'base_id_column' => $validated['base_id_column'], 'operation_column' => $validated['operation_column'], 'datetime_column' => $validated['datetime_column'], ]; $schedule->update($validated); // Sync tables $schedule->scheduledTables()->sync($validated['tables']); return redirect()->route('migrations.index') ->with('success', 'Migration schedule updated successfully.'); } public function destroy(MigrationSchedule $schedule) { $schedule->delete(); return redirect()->route('migrations.index') ->with('success', 'Migration schedule deleted successfully.'); } public function runNow(MigrationSchedule $schedule) { // Check if there's already a running or pending migration for this schedule $existingRun = MigrationRun::where('schedule_id', $schedule->id) ->whereIn('status', ['pending', 'running']) ->latest() ->first(); if ($existingRun) { return redirect()->back() ->with('warning', 'Миграция уже выполняется или ожидает в очереди.'); } $migrationRun = MigrationRun::create([ 'schedule_id' => $schedule->id, 'status' => 'pending', 'total_tables' => count($schedule->tables), ]); RunMigrationJob::dispatch($migrationRun)->onQueue('default'); return redirect()->back() ->with('success', 'Миграция запущена.'); } public function toggle(MigrationSchedule $schedule) { $schedule->update(['is_active' => !$schedule->is_active]); return redirect()->back() ->with('success', 'Schedule ' . ($schedule->is_active ? 'activated' : 'deactivated') . '.'); } public function show(MigrationSchedule $schedule) { $schedule->load(['sourceDatabase', 'targetDatabase', 'scheduledTables']); $runs = MigrationRun::where('schedule_id', $schedule->id) ->orderBy('created_at', 'desc') ->limit(50) ->get(); return Inertia::render('Migrations/Show', [ 'schedule' => $schedule, 'runs' => $runs, ]); } }