feat(v5): add mesh app canvas

This commit is contained in:
Andras Bacsai committed 2026-06-20 09:23:16 +02:00
1 parent 155b07378c
commit 86156b6f7a
53 files changed
+6037 -113

No files matched your search

+81
View File
@@ -0,0 +1,81 @@
<?php
namespace App\Models\V5;
use App\Events\V5CanvasResourceUpdated;
use App\Models\Environment;
use App\Models\Project;
use App\Models\Team;
use App\Models\User;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class Application extends V5Model
{
protected $table = 'v5_applications';
protected $fillable = [
'team_id',
'project_id',
'environment_id',
'server_id',
'created_by_user_id',
'name',
'image',
'container_name',
'status',
'status_message',
'runtime_container_id',
'mesh_namespace',
'canvas_x',
'canvas_y',
];
protected $attributes = [
'status' => 'creating',
'mesh_namespace' => 'default',
'canvas_x' => 0,
'canvas_y' => 0,
];
protected static function booted(): void
{
static::updated(function (self $application): void {
if ($application->wasChanged(['status', 'status_message', 'runtime_container_id'])) {
V5CanvasResourceUpdated::dispatch($application->team_id, $application->id);
}
});
}
protected function casts(): array
{
return [
'canvas_x' => 'integer',
'canvas_y' => 'integer',
];
}
public function team(): BelongsTo
{
return $this->belongsTo(Team::class);
}
public function server(): BelongsTo
{
return $this->belongsTo(Server::class);
}
public function project(): BelongsTo
{
return $this->belongsTo(Project::class);
}
public function environment(): BelongsTo
{
return $this->belongsTo(Environment::class);
}
public function creator(): BelongsTo
{
return $this->belongsTo(User::class, 'created_by_user_id');
}
}
+39
View File
@@ -0,0 +1,39 @@
<?php
namespace App\Models\V5;
use App\Models\Team;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class ContainerStatus extends V5Model
{
protected $table = 'v5_container_statuses';
protected $fillable = [
'team_id',
'server_id',
'container_id',
'container_name',
'image',
'status',
'status_message',
'last_seen_at',
];
protected function casts(): array
{
return [
'last_seen_at' => 'datetime',
];
}
public function team(): BelongsTo
{
return $this->belongsTo(Team::class);
}
public function server(): BelongsTo
{
return $this->belongsTo(Server::class);
}
}
+63
View File
@@ -0,0 +1,63 @@
<?php
namespace App\Models\V5;
use App\Models\Environment;
use App\Models\Project;
use App\Models\Team;
use App\Models\User;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\MorphTo;
class ResourceConnection extends V5Model
{
protected $table = 'v5_resource_connections';
protected $fillable = [
'team_id',
'project_id',
'environment_id',
'resource_one_type',
'resource_one_id',
'resource_two_type',
'resource_two_id',
'resource_pair_key',
'created_by_user_id',
];
public function team(): BelongsTo
{
return $this->belongsTo(Team::class);
}
public function project(): BelongsTo
{
return $this->belongsTo(Project::class);
}
public function environment(): BelongsTo
{
return $this->belongsTo(Environment::class);
}
public function creator(): BelongsTo
{
return $this->belongsTo(User::class, 'created_by_user_id');
}
public function resourceOne(): MorphTo
{
return $this->morphTo('resource_one');
}
public function resourceTwo(): MorphTo
{
return $this->morphTo('resource_two');
}
public function rules(): HasMany
{
return $this->hasMany(ResourceConnectionRule::class, 'connection_id');
}
}
+47
View File
@@ -0,0 +1,47 @@
<?php
namespace App\Models\V5;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\MorphTo;
class ResourceConnectionRule extends V5Model
{
protected $table = 'v5_resource_connection_rules';
protected $fillable = [
'connection_id',
'source_resource_type',
'source_resource_id',
'target_resource_type',
'target_resource_id',
'protocol',
'port',
];
protected $attributes = [
'protocol' => 'tcp',
];
protected function casts(): array
{
return [
'port' => 'integer',
];
}
public function connection(): BelongsTo
{
return $this->belongsTo(ResourceConnection::class, 'connection_id');
}
public function sourceResource(): MorphTo
{
return $this->morphTo('source_resource');
}
public function targetResource(): MorphTo
{
return $this->morphTo('target_resource');
}
}
+66
View File
@@ -2,6 +2,8 @@
namespace App\Models\V5;
use App\Events\V5CanvasResourceUpdated;
use App\Events\V5ClusterUpdated;
use App\Models\PrivateKey;
use App\Models\Team;
use App\Models\User;
@@ -22,6 +24,7 @@ class Server extends V5Model
'ssh_user',
'ssh_port',
'status',
'caddy_ingress_status',
'capabilities',
'builder_enabled',
'builder_capacity',
@@ -32,6 +35,8 @@ class Server extends V5Model
'wireguard_management_ip',
'wireguard_public_key',
'container_subnets',
'canvas_x',
'canvas_y',
'last_bootstrapped_at',
'last_bootstrap_action',
'last_bootstrap_status',
@@ -42,18 +47,79 @@ class Server extends V5Model
'last_status_checked_at',
];
protected static function booted(): void
{
static::updated(function (self $server): void {
if (! $server->wasChanged('status') && ! $server->wasChanged('caddy_ingress_status')) {
return;
}
if ($server->wasChanged('status') && $server->cluster_id !== null) {
V5ClusterUpdated::dispatch($server->team_id, $server->cluster_id);
}
if ($server->isIngress()) {
V5CanvasResourceUpdated::dispatch($server->team_id, null, $server->id);
}
});
}
protected function casts(): array
{
return [
'capabilities' => 'array',
'builder_enabled' => 'boolean',
'container_subnets' => 'array',
'canvas_x' => 'integer',
'canvas_y' => 'integer',
'last_bootstrapped_at' => 'datetime',
'last_bootstrap_ran_at' => 'datetime',
'last_status_checked_at' => 'datetime',
];
}
public function hasCapability(string $capability): bool
{
return in_array($capability, $this->capabilities ?? [], true);
}
/**
* @return array<int, string>
*/
public function withCapability(string $capability): array
{
return collect($this->capabilities ?? [])
->push($capability)
->unique()
->values()
->all();
}
/**
* @return array<int, string>
*/
public function withoutCapability(string $capability): array
{
return collect($this->capabilities ?? [])
->reject(fn (string $existingCapability) => $existingCapability === $capability)
->values()
->all();
}
public function isIngress(): bool
{
return $this->hasCapability('ingress');
}
public function caddyIngressStatus(): string
{
if ($this->caddy_ingress_status !== null) {
return $this->caddy_ingress_status;
}
return $this->status === 'installed' ? 'running' : 'unknown';
}
public function cluster(): BelongsTo
{
return $this->belongsTo(Cluster::class);