Initial Ladill Hosting app with Gitea deploy pipeline.
Deploy Ladill Hosting / deploy (push) Failing after 17s

Shared web hosting extracted from the platform monolith, with CI deploy
to /var/www/ladill-hosting matching Bird/Domains/Email.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
isaacclad
2026-06-06 16:24:20 +00:00
co-authored by Cursor
commit e251a4cf60
367 changed files with 66268 additions and 0 deletions
+123
View File
@@ -0,0 +1,123 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
class HostingPlan extends Model
{
use HasFactory, SoftDeletes;
protected $fillable = [
'name',
'slug',
'description',
'type',
'price_monthly',
'price_yearly',
'currency',
'disk_gb',
'bandwidth_gb',
'cpu_cores',
'ram_mb',
'max_domains',
'max_databases',
'max_email_accounts',
'max_ftp_accounts',
'ssl_included',
'backups_included',
'backup_retention_days',
'features',
'php_versions',
'is_active',
'is_featured',
'sort_order',
'contabo_product_id',
];
protected $casts = [
'price_monthly' => 'decimal:2',
'price_yearly' => 'decimal:2',
'disk_gb' => 'integer',
'bandwidth_gb' => 'integer',
'cpu_cores' => 'integer',
'ram_mb' => 'integer',
'max_domains' => 'integer',
'max_databases' => 'integer',
'max_email_accounts' => 'integer',
'max_ftp_accounts' => 'integer',
'ssl_included' => 'boolean',
'backups_included' => 'boolean',
'backup_retention_days' => 'integer',
'features' => 'array',
'php_versions' => 'array',
'is_active' => 'boolean',
'is_featured' => 'boolean',
'sort_order' => 'integer',
];
public const TYPE_SHARED = 'shared';
public const TYPE_WORDPRESS = 'wordpress';
public const TYPE_VPS = 'vps';
public const TYPE_DEDICATED = 'dedicated';
public const TYPE_EMAIL = 'email';
public function accounts(): HasMany
{
return $this->hasMany(HostingAccount::class);
}
public function vpsInstances(): HasMany
{
return $this->hasMany(VpsInstance::class);
}
public function scopeActive($query)
{
return $query->where('is_active', true);
}
public function scopeOfType($query, string $type)
{
return $query->where('type', $type);
}
public function scopeFeatured($query)
{
return $query->where('is_featured', true);
}
public function scopeOrdered($query)
{
return $query->orderBy('sort_order')->orderBy('price_monthly');
}
public function getYearlyDiscount(): ?float
{
if (!$this->price_yearly || !$this->price_monthly) {
return null;
}
$monthlyTotal = $this->price_monthly * 12;
return round((($monthlyTotal - $this->price_yearly) / $monthlyTotal) * 100, 1);
}
public function formatDiskSize(): string
{
if ($this->disk_gb >= 1000) {
return round($this->disk_gb / 1000, 1) . ' TB';
}
return $this->disk_gb . ' GB';
}
public function formatRam(): string
{
if ($this->ram_mb >= 1024) {
return round($this->ram_mb / 1024, 1) . ' GB';
}
return $this->ram_mb . ' MB';
}
}