Replace all uses of `forceFill`, `forceCreate`, and `forceFill` with their non-force equivalents across models, actions, controllers, and Livewire components. Add explicit `$fillable` arrays to all affected Eloquent models to enforce mass assignment protection. Add ModelFillableCreationTest and ModelFillableRegressionTest to verify that model creation respects fillable constraints and prevent regressions.
194 lines
5.1 KiB
PHP
194 lines
5.1 KiB
PHP
<?php
|
|
|
|
namespace App\Models;
|
|
|
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
|
|
|
class ServiceDatabase extends BaseModel
|
|
{
|
|
use HasFactory, SoftDeletes;
|
|
|
|
protected $fillable = [
|
|
'service_id',
|
|
'name',
|
|
'human_name',
|
|
'description',
|
|
'fqdn',
|
|
'ports',
|
|
'exposes',
|
|
'status',
|
|
'exclude_from_status',
|
|
'image',
|
|
'public_port',
|
|
'is_public',
|
|
'is_log_drain_enabled',
|
|
'is_include_timestamps',
|
|
'is_gzip_enabled',
|
|
'is_stripprefix_enabled',
|
|
'last_online_at',
|
|
'is_migrated',
|
|
'custom_type',
|
|
'public_port_timeout',
|
|
];
|
|
|
|
protected $casts = [
|
|
'public_port_timeout' => 'integer',
|
|
];
|
|
|
|
protected static function booted()
|
|
{
|
|
static::deleting(function ($service) {
|
|
$service->persistentStorages()->delete();
|
|
$service->fileStorages()->delete();
|
|
$service->scheduledBackups()->delete();
|
|
});
|
|
static::saving(function ($service) {
|
|
if ($service->isDirty('status')) {
|
|
$service->last_online_at = now();
|
|
}
|
|
});
|
|
}
|
|
|
|
public static function ownedByCurrentTeamAPI(int $teamId)
|
|
{
|
|
return ServiceDatabase::whereRelation('service.environment.project.team', 'id', $teamId)->orderBy('name');
|
|
}
|
|
|
|
/**
|
|
* Get query builder for service databases owned by current team.
|
|
* If you need all service databases without further query chaining, use ownedByCurrentTeamCached() instead.
|
|
*/
|
|
public static function ownedByCurrentTeam()
|
|
{
|
|
return ServiceDatabase::whereRelation('service.environment.project.team', 'id', currentTeam()->id)->orderBy('name');
|
|
}
|
|
|
|
/**
|
|
* Get all service databases owned by current team (cached for request duration).
|
|
*/
|
|
public static function ownedByCurrentTeamCached()
|
|
{
|
|
return once(function () {
|
|
return ServiceDatabase::ownedByCurrentTeam()->get();
|
|
});
|
|
}
|
|
|
|
public function restart()
|
|
{
|
|
$container_id = $this->name.'-'.$this->service->uuid;
|
|
remote_process(["docker restart {$container_id}"], $this->service->server);
|
|
}
|
|
|
|
public function isRunning()
|
|
{
|
|
return str($this->status)->contains('running');
|
|
}
|
|
|
|
public function isExited()
|
|
{
|
|
return str($this->status)->contains('exited');
|
|
}
|
|
|
|
public function isLogDrainEnabled()
|
|
{
|
|
return data_get($this, 'is_log_drain_enabled', false);
|
|
}
|
|
|
|
public function isStripprefixEnabled()
|
|
{
|
|
return data_get($this, 'is_stripprefix_enabled', true);
|
|
}
|
|
|
|
public function isGzipEnabled()
|
|
{
|
|
return data_get($this, 'is_gzip_enabled', true);
|
|
}
|
|
|
|
public function type()
|
|
{
|
|
return 'service';
|
|
}
|
|
|
|
public function serviceType()
|
|
{
|
|
return null;
|
|
}
|
|
|
|
public function databaseType()
|
|
{
|
|
if (filled($this->custom_type)) {
|
|
return 'standalone-'.$this->custom_type;
|
|
}
|
|
$image = str($this->image)->before(':');
|
|
if ($image->contains('supabase/postgres')) {
|
|
$finalImage = 'supabase/postgres';
|
|
} elseif ($image->contains('timescale')) {
|
|
$finalImage = 'postgresql';
|
|
} elseif ($image->contains('pgvector')) {
|
|
$finalImage = 'postgresql';
|
|
} elseif ($image->contains('postgres') || $image->contains('postgis')) {
|
|
$finalImage = 'postgresql';
|
|
} else {
|
|
$finalImage = $image;
|
|
}
|
|
|
|
return "standalone-$finalImage";
|
|
}
|
|
|
|
public function getServiceDatabaseUrl()
|
|
{
|
|
$port = $this->public_port;
|
|
$realIp = $this->service->server->ip;
|
|
if ($this->service->server->isLocalhost() || isDev()) {
|
|
$realIp = base_ip();
|
|
}
|
|
|
|
return "{$realIp}:{$port}";
|
|
}
|
|
|
|
public function team()
|
|
{
|
|
return data_get($this, 'service.environment.project.team');
|
|
}
|
|
|
|
public function workdir()
|
|
{
|
|
return service_configuration_dir()."/{$this->service->uuid}";
|
|
}
|
|
|
|
public function service()
|
|
{
|
|
return $this->belongsTo(Service::class);
|
|
}
|
|
|
|
public function persistentStorages()
|
|
{
|
|
return $this->morphMany(LocalPersistentVolume::class, 'resource');
|
|
}
|
|
|
|
public function fileStorages()
|
|
{
|
|
return $this->morphMany(LocalFileVolume::class, 'resource');
|
|
}
|
|
|
|
public function getFilesFromServer(bool $isInit = false)
|
|
{
|
|
getFilesystemVolumesFromServer($this, $isInit);
|
|
}
|
|
|
|
public function scheduledBackups()
|
|
{
|
|
return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
|
|
}
|
|
|
|
public function isBackupSolutionAvailable()
|
|
{
|
|
return str($this->databaseType())->contains('mysql') ||
|
|
str($this->databaseType())->contains('postgres') ||
|
|
str($this->databaseType())->contains('postgis') ||
|
|
str($this->databaseType())->contains('mariadb') ||
|
|
str($this->databaseType())->contains('mongo') ||
|
|
filled($this->custom_type);
|
|
}
|
|
}
|