Files
ladill-servers/app/Services/Hosting/ServerAgentReleaseService.php
T
isaaccladandCursor b6c8ac343f 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>
2026-06-06 19:18:30 +00:00

118 lines
4.0 KiB
PHP

<?php
namespace App\Services\Hosting;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\URL;
use Phar;
use PharData;
use RuntimeException;
class ServerAgentReleaseService
{
public function currentVersion(): string
{
return (string) config('hosting.server_agent.release_version', '0.2.0');
}
/**
* @return array{version: string, archive_path: string, checksum_sha256: string, filename: string}
*/
public function ensureRelease(?string $version = null): array
{
$version ??= $this->currentVersion();
$releaseDirectory = storage_path('app/server-agent/releases/'.$version);
$packageDirectory = $releaseDirectory.'/package';
$archiveFilename = "ladill-server-agent-{$version}.tar.gz";
$archivePath = $releaseDirectory.'/'.$archiveFilename;
$tarPath = $releaseDirectory."/ladill-server-agent-{$version}.tar";
File::ensureDirectoryExists($packageDirectory);
File::ensureDirectoryExists($releaseDirectory);
$renderedFiles = [
'install.sh' => $this->renderInstallScript(),
'ladill-server-agent' => $this->renderAgentScript($version),
'ladill-server-agent.service' => $this->renderTemplate(resource_path('server-agent/ladill-server-agent.service.tpl')),
'manifest.json' => json_encode([
'name' => 'ladill-server-agent',
'version' => $version,
'built_at' => now()->toIso8601String(),
], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)."\n",
];
foreach ($renderedFiles as $filename => $contents) {
File::put($packageDirectory.'/'.$filename, $contents);
}
if (! File::exists($archivePath)) {
if (File::exists($tarPath)) {
File::delete($tarPath);
}
try {
$phar = new PharData($tarPath);
foreach (array_keys($renderedFiles) as $filename) {
$phar->addFile($packageDirectory.'/'.$filename, $filename);
}
$phar->compress(Phar::GZ);
unset($phar);
} catch (\Throwable $e) {
throw new RuntimeException('Unable to build the server agent release archive.', previous: $e);
} finally {
if (File::exists($tarPath)) {
File::delete($tarPath);
}
}
}
if (! File::exists($archivePath)) {
throw new RuntimeException('The server agent release archive was not created.');
}
return [
'version' => $version,
'archive_path' => $archivePath,
'checksum_sha256' => hash_file('sha256', $archivePath),
'filename' => $archiveFilename,
];
}
public function temporaryDownloadUrl(?string $version = null, ?\DateTimeInterface $expiresAt = null): string
{
$version ??= $this->currentVersion();
$expiresAt ??= now()->addMinutes((int) config('hosting.server_agent.signed_release_ttl_minutes', 10080));
return URL::temporarySignedRoute('server-agent.releases.download', $expiresAt, [
'version' => $version,
]);
}
private function renderAgentScript(string $version): string
{
return str_replace(
['{{AGENT_VERSION}}', '{{HEARTBEAT_INTERVAL_SECONDS}}'],
[
$version,
(string) config('hosting.server_agent.heartbeat_interval_seconds', 15),
],
$this->renderTemplate(resource_path('server-agent/ladill-server-agent.sh.tpl'))
);
}
private function renderInstallScript(): string
{
return $this->renderTemplate(resource_path('server-agent/install.sh.tpl'));
}
private function renderTemplate(string $path): string
{
$contents = @file_get_contents($path);
if ($contents === false) {
throw new RuntimeException("Unable to load server agent release template: {$path}");
}
return $contents;
}
}