parent
							
								
									08ba19f355
								
							
						
					
					
						commit
						563721e67c
					
				| @ -0,0 +1,16 @@ | |||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | namespace App\Livewire; | ||||||
|  | 
 | ||||||
|  | use App\Models\airflow; | ||||||
|  | use App\Models\User; | ||||||
|  | 
 | ||||||
|  | class AirFlowTargetTable extends BaseTableComponent | ||||||
|  | { | ||||||
|  |     public function mount() | ||||||
|  |     { | ||||||
|  |         $this->headers = ['ID', 'Имя', 'URL', 'Username', 'Порт', 'Версия', 'Активный']; | ||||||
|  |         $this->data = Airflow::all(['id', 'name', 'url', 'username', 'port', 'version', 'is_active'])->toArray(); | ||||||
|  |         $this->view_item_route = "airflow.view"; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,28 @@ | |||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | namespace App\Livewire; | ||||||
|  | 
 | ||||||
|  | use App\Models\airflow; | ||||||
|  | use Livewire\Component; | ||||||
|  | 
 | ||||||
|  | class AirflowDataView extends Component | ||||||
|  | { | ||||||
|  |     public $airflowId; | ||||||
|  |     public $airflow; | ||||||
|  | 
 | ||||||
|  |     public function mount($airflowId) | ||||||
|  |     { | ||||||
|  |         $this->airflowId = $airflowId; | ||||||
|  |         $this->loadAirflowData(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function loadAirflowData() | ||||||
|  |     { | ||||||
|  |         $this->airflow = Airflow::with('user')->find($this->airflowId); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function render() | ||||||
|  |     { | ||||||
|  |         return view('livewire.airflow-data-view'); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,49 @@ | |||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | namespace App\Livewire; | ||||||
|  | 
 | ||||||
|  | use App\Models\airflow; | ||||||
|  | use Illuminate\Support\Facades\Auth; | ||||||
|  | use Livewire\Component; | ||||||
|  | 
 | ||||||
|  | class AirflowForm extends Component | ||||||
|  | { | ||||||
|  |     public $name; | ||||||
|  |     public $url; | ||||||
|  |     public $username; | ||||||
|  |     public $password; | ||||||
|  |     public $api_token; | ||||||
|  |     public $port; | ||||||
|  |     public $version; | ||||||
|  | 
 | ||||||
|  |     public function submit() | ||||||
|  |     { | ||||||
|  |         $this->validate([ | ||||||
|  |             'name' => 'required|string|max:255', | ||||||
|  |             'url' => 'required|url', | ||||||
|  |             'username' => 'required|string|max:255', | ||||||
|  |             'password' => 'required|string|max:255', | ||||||
|  |             'api_token' => 'nullable|string|max:255', | ||||||
|  |             'port' => 'required|integer', | ||||||
|  |             'version' => 'nullable|string|max:255', | ||||||
|  |         ]); | ||||||
|  | 
 | ||||||
|  |         Airflow::create([ | ||||||
|  |             'name' => $this->name, | ||||||
|  |             'url' => $this->url, | ||||||
|  |             'username' => $this->username, | ||||||
|  |             'password' => $this->password, | ||||||
|  |             'api_token' => $this->api_token, | ||||||
|  |             'port' => $this->port, | ||||||
|  |             'version' => $this->version, | ||||||
|  |             'user_id' => Auth::user()->id, | ||||||
|  |         ]); | ||||||
|  | 
 | ||||||
|  |         session()->flash('message', 'Airflow cluster created successfully.'); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function render() | ||||||
|  |     { | ||||||
|  |         return view('livewire.airflow-form'); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,17 @@ | |||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | namespace App\Livewire; | ||||||
|  | 
 | ||||||
|  | use Livewire\Component; | ||||||
|  | 
 | ||||||
|  | class BaseTableComponent extends Component | ||||||
|  | { | ||||||
|  |     public $data = []; | ||||||
|  |     public $headers = []; | ||||||
|  |     public $view_item_route = null; | ||||||
|  | 
 | ||||||
|  |     public function render() | ||||||
|  |     { | ||||||
|  |         return view('livewire.base-table-component'); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,13 @@ | |||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | namespace App\Livewire; | ||||||
|  | 
 | ||||||
|  | use Livewire\Component; | ||||||
|  | 
 | ||||||
|  | class UsersTable extends Component | ||||||
|  | { | ||||||
|  |     public function render() | ||||||
|  |     { | ||||||
|  |         return view('livewire.users-table'); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,186 @@ | |||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | return [ | ||||||
|  | 
 | ||||||
|  |     'models' => [ | ||||||
|  | 
 | ||||||
|  |         /* | ||||||
|  |          * When using the "HasPermissions" trait from this package, we need to know which | ||||||
|  |          * Eloquent model should be used to retrieve your permissions. Of course, it | ||||||
|  |          * is often just the "Permission" model but you may use whatever you like. | ||||||
|  |          * | ||||||
|  |          * The model you want to use as a Permission model needs to implement the | ||||||
|  |          * `Spatie\Permission\Contracts\Permission` contract. | ||||||
|  |          */ | ||||||
|  | 
 | ||||||
|  |         'permission' => Spatie\Permission\Models\Permission::class, | ||||||
|  | 
 | ||||||
|  |         /* | ||||||
|  |          * When using the "HasRoles" trait from this package, we need to know which | ||||||
|  |          * Eloquent model should be used to retrieve your roles. Of course, it | ||||||
|  |          * is often just the "Role" model but you may use whatever you like. | ||||||
|  |          * | ||||||
|  |          * The model you want to use as a Role model needs to implement the | ||||||
|  |          * `Spatie\Permission\Contracts\Role` contract. | ||||||
|  |          */ | ||||||
|  | 
 | ||||||
|  |         'role' => Spatie\Permission\Models\Role::class, | ||||||
|  | 
 | ||||||
|  |     ], | ||||||
|  | 
 | ||||||
|  |     'table_names' => [ | ||||||
|  | 
 | ||||||
|  |         /* | ||||||
|  |          * When using the "HasRoles" trait from this package, we need to know which | ||||||
|  |          * table should be used to retrieve your roles. We have chosen a basic | ||||||
|  |          * default value but you may easily change it to any table you like. | ||||||
|  |          */ | ||||||
|  | 
 | ||||||
|  |         'roles' => 'roles', | ||||||
|  | 
 | ||||||
|  |         /* | ||||||
|  |          * When using the "HasPermissions" trait from this package, we need to know which | ||||||
|  |          * table should be used to retrieve your permissions. We have chosen a basic | ||||||
|  |          * default value but you may easily change it to any table you like. | ||||||
|  |          */ | ||||||
|  | 
 | ||||||
|  |         'permissions' => 'permissions', | ||||||
|  | 
 | ||||||
|  |         /* | ||||||
|  |          * When using the "HasPermissions" trait from this package, we need to know which | ||||||
|  |          * table should be used to retrieve your models permissions. We have chosen a | ||||||
|  |          * basic default value but you may easily change it to any table you like. | ||||||
|  |          */ | ||||||
|  | 
 | ||||||
|  |         'model_has_permissions' => 'model_has_permissions', | ||||||
|  | 
 | ||||||
|  |         /* | ||||||
|  |          * When using the "HasRoles" trait from this package, we need to know which | ||||||
|  |          * table should be used to retrieve your models roles. We have chosen a | ||||||
|  |          * basic default value but you may easily change it to any table you like. | ||||||
|  |          */ | ||||||
|  | 
 | ||||||
|  |         'model_has_roles' => 'model_has_roles', | ||||||
|  | 
 | ||||||
|  |         /* | ||||||
|  |          * When using the "HasRoles" trait from this package, we need to know which | ||||||
|  |          * table should be used to retrieve your roles permissions. We have chosen a | ||||||
|  |          * basic default value but you may easily change it to any table you like. | ||||||
|  |          */ | ||||||
|  | 
 | ||||||
|  |         'role_has_permissions' => 'role_has_permissions', | ||||||
|  |     ], | ||||||
|  | 
 | ||||||
|  |     'column_names' => [ | ||||||
|  |         /* | ||||||
|  |          * Change this if you want to name the related pivots other than defaults | ||||||
|  |          */ | ||||||
|  |         'role_pivot_key' => null, //default 'role_id', | ||||||
|  |         'permission_pivot_key' => null, //default 'permission_id', | ||||||
|  | 
 | ||||||
|  |         /* | ||||||
|  |          * Change this if you want to name the related model primary key other than | ||||||
|  |          * `model_id`. | ||||||
|  |          * | ||||||
|  |          * For example, this would be nice if your primary keys are all UUIDs. In | ||||||
|  |          * that case, name this `model_uuid`. | ||||||
|  |          */ | ||||||
|  | 
 | ||||||
|  |         'model_morph_key' => 'model_id', | ||||||
|  | 
 | ||||||
|  |         /* | ||||||
|  |          * Change this if you want to use the teams feature and your related model's | ||||||
|  |          * foreign key is other than `team_id`. | ||||||
|  |          */ | ||||||
|  | 
 | ||||||
|  |         'team_foreign_key' => 'team_id', | ||||||
|  |     ], | ||||||
|  | 
 | ||||||
|  |     /* | ||||||
|  |      * When set to true, the method for checking permissions will be registered on the gate. | ||||||
|  |      * Set this to false if you want to implement custom logic for checking permissions. | ||||||
|  |      */ | ||||||
|  | 
 | ||||||
|  |     'register_permission_check_method' => true, | ||||||
|  | 
 | ||||||
|  |     /* | ||||||
|  |      * When set to true, Laravel\Octane\Events\OperationTerminated event listener will be registered | ||||||
|  |      * this will refresh permissions on every TickTerminated, TaskTerminated and RequestTerminated | ||||||
|  |      * NOTE: This should not be needed in most cases, but an Octane/Vapor combination benefited from it. | ||||||
|  |      */ | ||||||
|  |     'register_octane_reset_listener' => false, | ||||||
|  | 
 | ||||||
|  |     /* | ||||||
|  |      * Teams Feature. | ||||||
|  |      * When set to true the package implements teams using the 'team_foreign_key'. | ||||||
|  |      * If you want the migrations to register the 'team_foreign_key', you must | ||||||
|  |      * set this to true before doing the migration. | ||||||
|  |      * If you already did the migration then you must make a new migration to also | ||||||
|  |      * add 'team_foreign_key' to 'roles', 'model_has_roles', and 'model_has_permissions' | ||||||
|  |      * (view the latest version of this package's migration file) | ||||||
|  |      */ | ||||||
|  | 
 | ||||||
|  |     'teams' => false, | ||||||
|  | 
 | ||||||
|  |     /* | ||||||
|  |      * Passport Client Credentials Grant | ||||||
|  |      * When set to true the package will use Passports Client to check permissions | ||||||
|  |      */ | ||||||
|  | 
 | ||||||
|  |     'use_passport_client_credentials' => false, | ||||||
|  | 
 | ||||||
|  |     /* | ||||||
|  |      * When set to true, the required permission names are added to exception messages. | ||||||
|  |      * This could be considered an information leak in some contexts, so the default | ||||||
|  |      * setting is false here for optimum safety. | ||||||
|  |      */ | ||||||
|  | 
 | ||||||
|  |     'display_permission_in_exception' => false, | ||||||
|  | 
 | ||||||
|  |     /* | ||||||
|  |      * When set to true, the required role names are added to exception messages. | ||||||
|  |      * This could be considered an information leak in some contexts, so the default | ||||||
|  |      * setting is false here for optimum safety. | ||||||
|  |      */ | ||||||
|  | 
 | ||||||
|  |     'display_role_in_exception' => false, | ||||||
|  | 
 | ||||||
|  |     /* | ||||||
|  |      * By default wildcard permission lookups are disabled. | ||||||
|  |      * See documentation to understand supported syntax. | ||||||
|  |      */ | ||||||
|  | 
 | ||||||
|  |     'enable_wildcard_permission' => false, | ||||||
|  | 
 | ||||||
|  |     /* | ||||||
|  |      * The class to use for interpreting wildcard permissions. | ||||||
|  |      * If you need to modify delimiters, override the class and specify its name here. | ||||||
|  |      */ | ||||||
|  |     // 'permission.wildcard_permission' => Spatie\Permission\WildcardPermission::class, | ||||||
|  | 
 | ||||||
|  |     /* Cache-specific settings */ | ||||||
|  | 
 | ||||||
|  |     'cache' => [ | ||||||
|  | 
 | ||||||
|  |         /* | ||||||
|  |          * By default all permissions are cached for 24 hours to speed up performance. | ||||||
|  |          * When permissions or roles are updated the cache is flushed automatically. | ||||||
|  |          */ | ||||||
|  | 
 | ||||||
|  |         'expiration_time' => \DateInterval::createFromDateString('24 hours'), | ||||||
|  | 
 | ||||||
|  |         /* | ||||||
|  |          * The cache key used to store all permissions. | ||||||
|  |          */ | ||||||
|  | 
 | ||||||
|  |         'key' => 'spatie.permission.cache', | ||||||
|  | 
 | ||||||
|  |         /* | ||||||
|  |          * You may optionally indicate a specific cache driver to use for permission and | ||||||
|  |          * role caching using any of the `store` drivers listed in the cache.php config | ||||||
|  |          * file. Using 'default' here means to use the `default` set in cache.php. | ||||||
|  |          */ | ||||||
|  | 
 | ||||||
|  |         'store' => 'default', | ||||||
|  |     ], | ||||||
|  | ]; | ||||||
| @ -0,0 +1,36 @@ | |||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | use Illuminate\Database\Migrations\Migration; | ||||||
|  | use Illuminate\Database\Schema\Blueprint; | ||||||
|  | use Illuminate\Support\Facades\Schema; | ||||||
|  | 
 | ||||||
|  | return new class extends Migration | ||||||
|  | { | ||||||
|  |     /** | ||||||
|  |      * Run the migrations. | ||||||
|  |      */ | ||||||
|  |     public function up(): void | ||||||
|  |     { | ||||||
|  |         Schema::create('airflows', function (Blueprint $table) { | ||||||
|  |             $table->id(); | ||||||
|  |             $table->string('name'); // Имя кластера | ||||||
|  |             $table->string('url'); // URL или IP кластера Airflow | ||||||
|  |             $table->string('username')->nullable(); // Логин для доступа (если используется) | ||||||
|  |             $table->string('password')->nullable(); // Пароль для доступа (если используется) | ||||||
|  |             $table->string('api_token')->nullable(); // Токен для доступа (если используется) | ||||||
|  |             $table->string('port')->default('8080'); // Порт Airflow API | ||||||
|  |             $table->string('version')->nullable(); // Версия Airflow | ||||||
|  |             $table->boolean('is_active')->default(true); // Статус кластера (активен/неактивен) | ||||||
|  |             $table->foreignId('user_id')->constrained(); // Кто создал/управляет кластером | ||||||
|  |             $table->timestamps(); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Reverse the migrations. | ||||||
|  |      */ | ||||||
|  |     public function down(): void | ||||||
|  |     { | ||||||
|  |         Schema::dropIfExists('airflows'); | ||||||
|  |     } | ||||||
|  | }; | ||||||
| @ -0,0 +1,35 @@ | |||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | use Illuminate\Database\Migrations\Migration; | ||||||
|  | use Illuminate\Database\Schema\Blueprint; | ||||||
|  | use Illuminate\Support\Facades\Schema; | ||||||
|  | 
 | ||||||
|  | return new class extends Migration | ||||||
|  | { | ||||||
|  |     /** | ||||||
|  |      * Run the migrations. | ||||||
|  |      */ | ||||||
|  |     public function up(): void | ||||||
|  |     { | ||||||
|  |         Schema::create('dags', function (Blueprint $table) { | ||||||
|  |             $table->id(); | ||||||
|  |             $table->unsignedBigInteger('user_id'); | ||||||
|  |             $table->foreignId('airflow_id')->constrained('airflows')->onDelete('cascade'); | ||||||
|  |             $table->string('name'); | ||||||
|  |             $table->string('description')->nullable(); | ||||||
|  |             $table->string('file_path'); | ||||||
|  |             $table->enum('status', ['pending', 'approved', 'rejected'])->default('pending'); | ||||||
|  | 
 | ||||||
|  |             $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); | ||||||
|  |             $table->timestamps(); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Reverse the migrations. | ||||||
|  |      */ | ||||||
|  |     public function down(): void | ||||||
|  |     { | ||||||
|  |         Schema::dropIfExists('dags'); | ||||||
|  |     } | ||||||
|  | }; | ||||||
| @ -0,0 +1,140 @@ | |||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | use Illuminate\Support\Facades\Schema; | ||||||
|  | use Illuminate\Database\Schema\Blueprint; | ||||||
|  | use Illuminate\Database\Migrations\Migration; | ||||||
|  | 
 | ||||||
|  | return new class extends Migration | ||||||
|  | { | ||||||
|  |     /** | ||||||
|  |      * Run the migrations. | ||||||
|  |      */ | ||||||
|  |     public function up(): void | ||||||
|  |     { | ||||||
|  |         $teams = config('permission.teams'); | ||||||
|  |         $tableNames = config('permission.table_names'); | ||||||
|  |         $columnNames = config('permission.column_names'); | ||||||
|  |         $pivotRole = $columnNames['role_pivot_key'] ?? 'role_id'; | ||||||
|  |         $pivotPermission = $columnNames['permission_pivot_key'] ?? 'permission_id'; | ||||||
|  | 
 | ||||||
|  |         if (empty($tableNames)) { | ||||||
|  |             throw new \Exception('Error: config/permission.php not loaded. Run [php artisan config:clear] and try again.'); | ||||||
|  |         } | ||||||
|  |         if ($teams && empty($columnNames['team_foreign_key'] ?? null)) { | ||||||
|  |             throw new \Exception('Error: team_foreign_key on config/permission.php not loaded. Run [php artisan config:clear] and try again.'); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         Schema::create($tableNames['permissions'], function (Blueprint $table) { | ||||||
|  |             //$table->engine('InnoDB'); | ||||||
|  |             $table->bigIncrements('id'); // permission id | ||||||
|  |             $table->string('name');       // For MyISAM use string('name', 225); // (or 166 for InnoDB with Redundant/Compact row format) | ||||||
|  |             $table->string('guard_name'); // For MyISAM use string('guard_name', 25); | ||||||
|  |             $table->timestamps(); | ||||||
|  | 
 | ||||||
|  |             $table->unique(['name', 'guard_name']); | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         Schema::create($tableNames['roles'], function (Blueprint $table) use ($teams, $columnNames) { | ||||||
|  |             //$table->engine('InnoDB'); | ||||||
|  |             $table->bigIncrements('id'); // role id | ||||||
|  |             if ($teams || config('permission.testing')) { // permission.testing is a fix for sqlite testing | ||||||
|  |                 $table->unsignedBigInteger($columnNames['team_foreign_key'])->nullable(); | ||||||
|  |                 $table->index($columnNames['team_foreign_key'], 'roles_team_foreign_key_index'); | ||||||
|  |             } | ||||||
|  |             $table->string('name');       // For MyISAM use string('name', 225); // (or 166 for InnoDB with Redundant/Compact row format) | ||||||
|  |             $table->string('guard_name'); // For MyISAM use string('guard_name', 25); | ||||||
|  |             $table->timestamps(); | ||||||
|  |             if ($teams || config('permission.testing')) { | ||||||
|  |                 $table->unique([$columnNames['team_foreign_key'], 'name', 'guard_name']); | ||||||
|  |             } else { | ||||||
|  |                 $table->unique(['name', 'guard_name']); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         Schema::create($tableNames['model_has_permissions'], function (Blueprint $table) use ($tableNames, $columnNames, $pivotPermission, $teams) { | ||||||
|  |             $table->unsignedBigInteger($pivotPermission); | ||||||
|  | 
 | ||||||
|  |             $table->string('model_type'); | ||||||
|  |             $table->unsignedBigInteger($columnNames['model_morph_key']); | ||||||
|  |             $table->index([$columnNames['model_morph_key'], 'model_type'], 'model_has_permissions_model_id_model_type_index'); | ||||||
|  | 
 | ||||||
|  |             $table->foreign($pivotPermission) | ||||||
|  |                 ->references('id') // permission id | ||||||
|  |                 ->on($tableNames['permissions']) | ||||||
|  |                 ->onDelete('cascade'); | ||||||
|  |             if ($teams) { | ||||||
|  |                 $table->unsignedBigInteger($columnNames['team_foreign_key']); | ||||||
|  |                 $table->index($columnNames['team_foreign_key'], 'model_has_permissions_team_foreign_key_index'); | ||||||
|  | 
 | ||||||
|  |                 $table->primary([$columnNames['team_foreign_key'], $pivotPermission, $columnNames['model_morph_key'], 'model_type'], | ||||||
|  |                     'model_has_permissions_permission_model_type_primary'); | ||||||
|  |             } else { | ||||||
|  |                 $table->primary([$pivotPermission, $columnNames['model_morph_key'], 'model_type'], | ||||||
|  |                     'model_has_permissions_permission_model_type_primary'); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         Schema::create($tableNames['model_has_roles'], function (Blueprint $table) use ($tableNames, $columnNames, $pivotRole, $teams) { | ||||||
|  |             $table->unsignedBigInteger($pivotRole); | ||||||
|  | 
 | ||||||
|  |             $table->string('model_type'); | ||||||
|  |             $table->unsignedBigInteger($columnNames['model_morph_key']); | ||||||
|  |             $table->index([$columnNames['model_morph_key'], 'model_type'], 'model_has_roles_model_id_model_type_index'); | ||||||
|  | 
 | ||||||
|  |             $table->foreign($pivotRole) | ||||||
|  |                 ->references('id') // role id | ||||||
|  |                 ->on($tableNames['roles']) | ||||||
|  |                 ->onDelete('cascade'); | ||||||
|  |             if ($teams) { | ||||||
|  |                 $table->unsignedBigInteger($columnNames['team_foreign_key']); | ||||||
|  |                 $table->index($columnNames['team_foreign_key'], 'model_has_roles_team_foreign_key_index'); | ||||||
|  | 
 | ||||||
|  |                 $table->primary([$columnNames['team_foreign_key'], $pivotRole, $columnNames['model_morph_key'], 'model_type'], | ||||||
|  |                     'model_has_roles_role_model_type_primary'); | ||||||
|  |             } else { | ||||||
|  |                 $table->primary([$pivotRole, $columnNames['model_morph_key'], 'model_type'], | ||||||
|  |                     'model_has_roles_role_model_type_primary'); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         Schema::create($tableNames['role_has_permissions'], function (Blueprint $table) use ($tableNames, $pivotRole, $pivotPermission) { | ||||||
|  |             $table->unsignedBigInteger($pivotPermission); | ||||||
|  |             $table->unsignedBigInteger($pivotRole); | ||||||
|  | 
 | ||||||
|  |             $table->foreign($pivotPermission) | ||||||
|  |                 ->references('id') // permission id | ||||||
|  |                 ->on($tableNames['permissions']) | ||||||
|  |                 ->onDelete('cascade'); | ||||||
|  | 
 | ||||||
|  |             $table->foreign($pivotRole) | ||||||
|  |                 ->references('id') // role id | ||||||
|  |                 ->on($tableNames['roles']) | ||||||
|  |                 ->onDelete('cascade'); | ||||||
|  | 
 | ||||||
|  |             $table->primary([$pivotPermission, $pivotRole], 'role_has_permissions_permission_id_role_id_primary'); | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         app('cache') | ||||||
|  |             ->store(config('permission.cache.store') != 'default' ? config('permission.cache.store') : null) | ||||||
|  |             ->forget(config('permission.cache.key')); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Reverse the migrations. | ||||||
|  |      */ | ||||||
|  |     public function down(): void | ||||||
|  |     { | ||||||
|  |         $tableNames = config('permission.table_names'); | ||||||
|  | 
 | ||||||
|  |         if (empty($tableNames)) { | ||||||
|  |             throw new \Exception('Error: config/permission.php not found and defaults could not be merged. Please publish the package configuration before proceeding, or drop the tables manually.'); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         Schema::drop($tableNames['role_has_permissions']); | ||||||
|  |         Schema::drop($tableNames['model_has_roles']); | ||||||
|  |         Schema::drop($tableNames['model_has_permissions']); | ||||||
|  |         Schema::drop($tableNames['roles']); | ||||||
|  |         Schema::drop($tableNames['permissions']); | ||||||
|  |     } | ||||||
|  | }; | ||||||
| @ -0,0 +1,32 @@ | |||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | use Illuminate\Database\Migrations\Migration; | ||||||
|  | use Illuminate\Database\Schema\Blueprint; | ||||||
|  | use Illuminate\Support\Facades\Schema; | ||||||
|  | 
 | ||||||
|  | return new class extends Migration | ||||||
|  | { | ||||||
|  |     /** | ||||||
|  |      * Run the migrations. | ||||||
|  |      */ | ||||||
|  |     public function up(): void | ||||||
|  |     { | ||||||
|  |         Schema::create('dag_events', function (Blueprint $table) { | ||||||
|  |             $table->id(); | ||||||
|  |             $table->foreignId('dag_id')->constrained()->onDelete('cascade'); | ||||||
|  |             $table->foreignId('dag_run_id')->nullable()->constrained()->onDelete('cascade'); | ||||||
|  |             $table->string('event_type'); // Тип события: 'failure', 'success', 'task_failed', и т.д. | ||||||
|  |             $table->text('message')->nullable(); // Дополнительное сообщение | ||||||
|  |             $table->json('metadata')->nullable(); // Дополнительные данные | ||||||
|  |             $table->timestamps(); // created_at будет временем события | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Reverse the migrations. | ||||||
|  |      */ | ||||||
|  |     public function down(): void | ||||||
|  |     { | ||||||
|  |         Schema::dropIfExists('dag_events'); | ||||||
|  |     } | ||||||
|  | }; | ||||||
| @ -0,0 +1,47 @@ | |||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | use Illuminate\Database\Migrations\Migration; | ||||||
|  | use Illuminate\Database\Schema\Blueprint; | ||||||
|  | use Illuminate\Support\Facades\Schema; | ||||||
|  | 
 | ||||||
|  | return new class extends Migration | ||||||
|  | { | ||||||
|  |     /** | ||||||
|  |      * Run the migrations. | ||||||
|  |      */ | ||||||
|  |     public function up(): void | ||||||
|  |     { | ||||||
|  |         Schema::create('task_instances', function (Blueprint $table) { | ||||||
|  |             $table->id(); | ||||||
|  |             $table->foreignId('dag_id')->constrained()->onDelete('cascade'); | ||||||
|  |             $table->foreignId('dag_run_id')->constrained()->onDelete('cascade'); | ||||||
|  |             $table->foreignId('airflow_id')->after('dag_id')->constrained('airflows')->onDelete('cascade'); | ||||||
|  |             $table->string('task_id'); // ID задачи в Airflow | ||||||
|  |             $table->enum('state', [ | ||||||
|  |                 'success', | ||||||
|  |                 'running', | ||||||
|  |                 'failed', | ||||||
|  |                 'queued', | ||||||
|  |                 'skipped', | ||||||
|  |                 'up_for_retry', | ||||||
|  |                 'upstream_failed', | ||||||
|  |                 'up_for_reschedule', | ||||||
|  |                 'none', | ||||||
|  |             ])->nullable(); | ||||||
|  |             $table->timestamp('execution_date')->nullable(); | ||||||
|  |             $table->timestamp('start_date')->nullable(); | ||||||
|  |             $table->timestamp('end_date')->nullable(); | ||||||
|  |             $table->longText('log')->nullable(); // Логи выполнения задачи | ||||||
|  |             $table->json('metadata')->nullable(); // Дополнительные данные | ||||||
|  |             $table->timestamps(); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Reverse the migrations. | ||||||
|  |      */ | ||||||
|  |     public function down(): void | ||||||
|  |     { | ||||||
|  |         Schema::dropIfExists('task_instances'); | ||||||
|  |     } | ||||||
|  | }; | ||||||
											
												
													File diff suppressed because it is too large
													Load Diff
												
											
										
									
								| @ -0,0 +1,39 @@ | |||||||
|  | <div class="p-6 bg-white border border-gray-200 rounded-lg shadow"> | ||||||
|  |     <div class="flex justify-between items-center"> | ||||||
|  |         <h2 class="text-xl font-bold text-gray-900"> | ||||||
|  |             Airflow Cluster: {{ $airflow->name }} | ||||||
|  |         </h2> | ||||||
|  |         <span class="text-sm text-gray-500"> | ||||||
|  |             Updated at: {{ now()->format('H:i:s') }} | ||||||
|  |         </span> | ||||||
|  |     </div> | ||||||
|  | 
 | ||||||
|  |     <div class="mt-4"> | ||||||
|  |         <p><strong>URL:</strong> <a href="{{ $airflow->url }}" class="text-blue-500" target="_blank">{{ $airflow->url }}</a></p> | ||||||
|  |         <p><strong>Version:</strong> {{ $airflow->version }}</p> | ||||||
|  |         <p><strong>Port:</strong> {{ $airflow->port }}</p> | ||||||
|  |         <p><strong>Status:</strong> | ||||||
|  |             <span class="px-2 py-1 rounded-full text-sm {{ $airflow->is_active ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800' }}"> | ||||||
|  |                 {{ $airflow->is_active ? 'Active' : 'Inactive' }} | ||||||
|  |             </span> | ||||||
|  |         </p> | ||||||
|  |         <p><strong>Managed by:</strong> {{ $airflow->user->name }}</p> | ||||||
|  |     </div> | ||||||
|  | 
 | ||||||
|  |     <div class="mt-4"> | ||||||
|  |         <h3 class="text-lg font-semibold text-gray-800">DAGs in this cluster:</h3> | ||||||
|  |         <ul class="mt-2 list-disc list-inside"> | ||||||
|  |             @foreach ($airflow->dags as $dag) | ||||||
|  |                 <li>{{ $dag->name }}</li> | ||||||
|  |             @endforeach | ||||||
|  |         </ul> | ||||||
|  |     </div> | ||||||
|  | </div> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  |     document.addEventListener('livewire:load', function () { | ||||||
|  |         setInterval(() => { | ||||||
|  |             Livewire.emit('refreshData'); | ||||||
|  |         }, 5000); | ||||||
|  |     }); | ||||||
|  | </script> | ||||||
| @ -0,0 +1,53 @@ | |||||||
|  | <div class="max-w-lg mx-auto p-6 bg-white rounded-lg shadow-lg"> | ||||||
|  |     @if (session()->has('message')) | ||||||
|  |         <div class="mb-4 p-4 bg-green-100 text-green-700 rounded-lg"> | ||||||
|  |             {{ session('message') }} | ||||||
|  |         </div> | ||||||
|  |     @endif | ||||||
|  | 
 | ||||||
|  |     <form wire:submit.prevent="submit" class="space-y-4"> | ||||||
|  |         <div class="form-group"> | ||||||
|  |             <label for="name" class="block text-gray-700 font-semibold">Name</label> | ||||||
|  |             <input type="text" id="name" wire:model="name" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"> | ||||||
|  |             @error('name') <span class="text-red-500 text-sm">{{ $message }}</span> @enderror | ||||||
|  |         </div> | ||||||
|  | 
 | ||||||
|  |         <div class="form-group"> | ||||||
|  |             <label for="url" class="block text-gray-700 font-semibold">URL</label> | ||||||
|  |             <input type="text" id="url" wire:model="url" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"> | ||||||
|  |             @error('url') <span class="text-red-500 text-sm">{{ $message }}</span> @enderror | ||||||
|  |         </div> | ||||||
|  | 
 | ||||||
|  |         <div class="form-group"> | ||||||
|  |             <label for="username" class="block text-gray-700 font-semibold">Username</label> | ||||||
|  |             <input type="text" id="username" wire:model="username" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"> | ||||||
|  |             @error('username') <span class="text-red-500 text-sm">{{ $message }}</span> @enderror | ||||||
|  |         </div> | ||||||
|  | 
 | ||||||
|  |         <div class="form-group"> | ||||||
|  |             <label for="password" class="block text-gray-700 font-semibold">Password</label> | ||||||
|  |             <input type="password" id="password" wire:model="password" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"> | ||||||
|  |             @error('password') <span class="text-red-500 text-sm">{{ $message }}</span> @enderror | ||||||
|  |         </div> | ||||||
|  | 
 | ||||||
|  |         <div class="form-group"> | ||||||
|  |             <label for="api_token" class="block text-gray-700 font-semibold">API Token</label> | ||||||
|  |             <input type="text" id="api_token" wire:model="api_token" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"> | ||||||
|  |             @error('api_token') <span class="text-red-500 text-sm">{{ $message }}</span> @enderror | ||||||
|  |         </div> | ||||||
|  | 
 | ||||||
|  |         <div class="form-group"> | ||||||
|  |             <label for="port" class="block text-gray-700 font-semibold">Port</label> | ||||||
|  |             <input type="text" id="port" wire:model="port" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"> | ||||||
|  |             @error('port') <span class="text-red-500 text-sm">{{ $message }}</span> @enderror | ||||||
|  |         </div> | ||||||
|  | 
 | ||||||
|  |         <div class="form-group"> | ||||||
|  |             <label for="version" class="block text-gray-700 font-semibold">Version</label> | ||||||
|  |             <input type="text" id="version" wire:model="version" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"> | ||||||
|  |             @error('version') <span class="text-red-500 text-sm">{{ $message }}</span> @enderror | ||||||
|  |         </div> | ||||||
|  | 
 | ||||||
|  |         <button type="submit" class="w-full bg-blue-500 text-white font-semibold py-2 px-4 rounded-lg hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-opacity-50">Save</button> | ||||||
|  |     </form> | ||||||
|  | </div> | ||||||
| @ -0,0 +1,33 @@ | |||||||
|  | <div class="bg-white shadow-lg rounded-xl p-6 overflow-hidden"> | ||||||
|  |     @if(empty($data) || count($data) == 0) | ||||||
|  |         <div class="flex flex-col items-center justify-center"> | ||||||
|  |             <svg class="w-16 h-16 text-gray-400" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | ||||||
|  |                 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 17v-2a4 4 0 00-4-4H3m18 0h-2a4 4 0 00-4 4v2m-4-4v6m4-6v6M7 17v6m10-6v6"/> | ||||||
|  |             </svg> | ||||||
|  |             <p class="text-gray-600 mt-4">Нет данных для отображения.</p> | ||||||
|  |         </div> | ||||||
|  |     @else | ||||||
|  |         <table class="min-w-full bg-gray-50 rounded-lg shadow-md"> | ||||||
|  |             <thead class="bg-gradient-to-r from-gray-100 to-gray-200 text-gray-700"> | ||||||
|  |             <tr> | ||||||
|  |                 @foreach($headers as $header) | ||||||
|  |                     <th class="px-6 py-4 text-left text-sm font-semibold text-gray-800 border-b border-gray-300"> | ||||||
|  |                         {{ $header }} | ||||||
|  |                     </th> | ||||||
|  |                 @endforeach | ||||||
|  |             </tr> | ||||||
|  |             </thead> | ||||||
|  |             <tbody class="text-gray-700"> | ||||||
|  |             @foreach($data as $row) | ||||||
|  |                 <tr class="bg-white hover:bg-gray-50 transition-colors shadow-sm hover:shadow-lg"> | ||||||
|  |                     @foreach($row as $cell) | ||||||
|  |                         <td class="px-6 py-4 border-b border-gray-200 text-sm"> | ||||||
|  |                             {{ $cell }} | ||||||
|  |                         </td> | ||||||
|  |                     @endforeach | ||||||
|  |                 </tr> | ||||||
|  |             @endforeach | ||||||
|  |             </tbody> | ||||||
|  |         </table> | ||||||
|  |     @endif | ||||||
|  | </div> | ||||||
| @ -0,0 +1,3 @@ | |||||||
|  | <div> | ||||||
|  |     {{-- Stop trying to control. --}} | ||||||
|  | </div> | ||||||
| @ -0,0 +1,19 @@ | |||||||
|  | <x-app-layout> | ||||||
|  |     <x-slot name="header"> | ||||||
|  |         <h2 class="font-semibold text-xl text-gray-800 leading-tight"> | ||||||
|  |             AirFlow Clusters | ||||||
|  |         </h2> | ||||||
|  |     </x-slot> | ||||||
|  | 
 | ||||||
|  |     <div class="py-12"> | ||||||
|  |         <div class="max-w-7xl mx-auto sm:px-6 lg:px-8 flex space-x-4"> | ||||||
|  |             <div class="w-1/3"> | ||||||
|  |                 <livewire:airflow-data-view :airflowId="$id" /> | ||||||
|  |             </div> | ||||||
|  | 
 | ||||||
|  |             <div class="w-2/3"> | ||||||
|  |                 HERE WAS A TABLE | ||||||
|  |             </div> | ||||||
|  |         </div> | ||||||
|  |     </div> | ||||||
|  | </x-app-layout> | ||||||
| @ -0,0 +1,19 @@ | |||||||
|  | <x-app-layout> | ||||||
|  |     <x-slot name="header"> | ||||||
|  |         <h2 class="font-semibold text-xl text-gray-800 leading-tight"> | ||||||
|  |             AirFlow Clusters | ||||||
|  |         </h2> | ||||||
|  |     </x-slot> | ||||||
|  | 
 | ||||||
|  |     <div class="py-12"> | ||||||
|  |         <div class="max-w-7xl mx-auto sm:px-6 lg:px-8 flex space-x-4"> | ||||||
|  |             <div class="w-1/3"> | ||||||
|  |                 <livewire:airflow-form /> | ||||||
|  |             </div> | ||||||
|  | 
 | ||||||
|  |             <div class="w-2/3"> | ||||||
|  |                 <livewire:air-flow-target-table /> | ||||||
|  |             </div> | ||||||
|  |         </div> | ||||||
|  |     </div> | ||||||
|  | </x-app-layout> | ||||||
| @ -0,0 +1,19 @@ | |||||||
|  | <x-app-layout> | ||||||
|  |     <x-slot name="header"> | ||||||
|  |         <h2 class="font-semibold text-xl text-gray-800 leading-tight"> | ||||||
|  |             All Dags | ||||||
|  |         </h2> | ||||||
|  |     </x-slot> | ||||||
|  | 
 | ||||||
|  |     <div class="py-12"> | ||||||
|  |         <div class="max-w-7xl mx-auto sm:px-6 lg:px-8 flex space-x-4"> | ||||||
|  |             <div class="w-1/3"> | ||||||
|  | 
 | ||||||
|  |             </div> | ||||||
|  | 
 | ||||||
|  |             <div class="w-2/3"> | ||||||
|  | 
 | ||||||
|  |             </div> | ||||||
|  |         </div> | ||||||
|  |     </div> | ||||||
|  | </x-app-layout> | ||||||
| @ -0,0 +1 @@ | |||||||
|  | <?php | ||||||
											
												
													File diff suppressed because it is too large
													Load Diff
												
											
										
									
								
					Loading…
					
					
				
		Reference in new issue