Extract Ladill Servers as standalone app at servers.ladill.com.

VPS and dedicated server ordering, managed panels, SSO, billing, and launcher integration — forked from hosting infrastructure with server-focused routes and dashboard.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
isaacclad
2026-06-06 19:18:30 +00:00
co-authored by Cursor
commit b6c8ac343f
382 changed files with 67315 additions and 0 deletions
+116
View File
@@ -0,0 +1,116 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class AppInstallation extends Model
{
protected $fillable = [
'hosted_site_id',
'app_type',
'app_version',
'admin_path',
'admin_username',
'admin_email',
'admin_password_encrypted',
'database_name',
'database_username',
'database_password_encrypted',
'install_url',
'config',
'status',
'progress',
'progress_message',
'current_step',
'total_steps',
'error_message',
'installed_at',
'last_updated_at',
];
protected $casts = [
'config' => 'array',
'installed_at' => 'datetime',
'last_updated_at' => 'datetime',
'progress' => 'integer',
'total_steps' => 'integer',
];
public const APP_WORDPRESS = 'wordpress';
public const APP_JOOMLA = 'joomla';
public const APP_DRUPAL = 'drupal';
public const APP_OPENCART = 'opencart';
public const APP_MAGENTO = 'magento';
public function site(): BelongsTo
{
return $this->belongsTo(HostedSite::class, 'hosted_site_id');
}
public function isActive(): bool
{
return $this->status === 'active';
}
public function getAdminPasswordAttribute(): ?string
{
return $this->admin_password_encrypted ? decrypt($this->admin_password_encrypted) : null;
}
public function getDatabasePasswordAttribute(): ?string
{
return $this->database_password_encrypted ? decrypt($this->database_password_encrypted) : null;
}
public function getAdminUrlAttribute(): ?string
{
if (!$this->install_url || !$this->admin_path) {
return null;
}
return rtrim($this->install_url, '/') . '/' . ltrim($this->admin_path, '/');
}
public function updateProgress(int $step, int $totalSteps, string $message, ?string $currentStep = null): void
{
$progress = $totalSteps > 0 ? (int) round(($step / $totalSteps) * 100) : 0;
$this->update([
'progress' => min($progress, 100),
'progress_message' => $message,
'current_step' => $currentStep,
'total_steps' => $totalSteps,
]);
}
public function markFailed(string $errorMessage): void
{
$this->update([
'status' => 'failed',
'error_message' => $errorMessage,
'progress_message' => 'Installation failed',
]);
}
public function markCompleted(): void
{
$this->update([
'status' => 'active',
'progress' => 100,
'progress_message' => 'Installation complete',
'current_step' => null,
'installed_at' => now(),
]);
}
public function isInstalling(): bool
{
return $this->status === 'installing';
}
public function hasFailed(): bool
{
return $this->status === 'failed';
}
}