Merge remote-tracking branch 'origin/next' into volume-backups-server-s3

This commit is contained in:
Andras Bacsai
2026-07-16 13:47:15 +02:00
38 changed files with 1634 additions and 134 deletions
@@ -850,6 +850,12 @@ class DatabasesController extends Controller
$this->authorize('manageBackups', $database);
if (! $database->isBackupSolutionAvailable()) {
return response()->json([
'message' => 'Scheduled backups are not supported for this database type.',
], 422);
}
// Validate frequency is a valid cron expression
$isValid = validate_cron_expression($request->frequency);
if (! $isValid) {
@@ -915,6 +921,8 @@ class DatabasesController extends Controller
$backupData['databases_to_backup'] = $database->mysql_database;
} elseif ($database->type() === 'standalone-mariadb') {
$backupData['databases_to_backup'] = $database->mariadb_database;
} elseif ($database->type() === 'standalone-clickhouse') {
$backupData['databases_to_backup'] = $database->clickhouse_db;
}
}
@@ -3016,7 +3024,7 @@ class DatabasesController extends Controller
),
]
)]
public function move_by_uuid(Request $request): \Illuminate\Http\JsonResponse
public function move_by_uuid(Request $request): JsonResponse
{
$teamId = getTeamIdFromToken();
if (is_null($teamId)) {
@@ -490,7 +490,7 @@ class ServiceApplicationsController extends Controller
], 400);
}
$lines = (int) ($request->query('lines', 100) ?: 100);
$lines = normalizeLogLines($request->query('lines'));
$logs = getContainerLogs($server, $containerName, $lines);
return response()->json([
@@ -311,7 +311,7 @@ class ServiceDatabasesController extends Controller
return response()->json(['message' => 'Service database container is not running.'], 400);
}
$lines = (int) ($request->query('lines', 100) ?: 100);
$lines = normalizeLogLines($request->query('lines'));
return response()->json([
'logs' => getContainerLogs($server, $containerName, $lines),
+62 -28
View File
@@ -15,6 +15,7 @@ use App\Rules\ValidHostname;
use App\Services\VultrService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use OpenApi\Attributes as OA;
class VultrController extends Controller
@@ -286,6 +287,10 @@ class VultrController extends Controller
return response()->json(['message' => 'Private key not found.'], 404);
}
$vultrService = null;
$vultrInstanceId = null;
$server = null;
try {
$vultrService = new VultrService($token->token);
$publicKey = $privateKey->getPublicKey();
@@ -317,33 +322,41 @@ class VultrController extends Controller
}
$vultrInstance = $vultrService->createInstance($params);
$vultrInstanceId = (string) $vultrInstance['id'];
$ipAddress = $vultrService->getPublicIp($vultrInstance, $request->disable_public_ipv4, $request->enable_ipv6) ?? Server::PLACEHOLDER_IP;
$server = Server::create([
'name' => $normalizedServerName,
'ip' => $ipAddress,
'user' => 'root',
'port' => 22,
'team_id' => $teamId,
'private_key_id' => $privateKey->id,
'cloud_provider_token_id' => $token->id,
'vultr_instance_id' => $vultrInstance['id'],
'vultr_instance_status' => $vultrInstance['status'] ?? null,
]);
$vultrInstance = $vultrService->waitForPublicIp($vultrInstance, $request->disable_public_ipv4, $request->enable_ipv6);
$assignedIpAddress = $vultrService->getPublicIp($vultrInstance, $request->disable_public_ipv4, $request->enable_ipv6);
if ($assignedIpAddress && $assignedIpAddress !== $server->ip) {
$ipAddress = $assignedIpAddress;
$server->update([
'ip' => $assignedIpAddress,
'vultr_instance_status' => $vultrInstance['status'] ?? $server->vultr_instance_status,
$server = DB::transaction(function () use ($normalizedServerName, $ipAddress, $teamId, $privateKey, $token, $vultrInstanceId, $vultrInstance): Server {
$server = Server::create([
'name' => $normalizedServerName,
'ip' => $ipAddress,
'user' => 'root',
'port' => 22,
'team_id' => $teamId,
'private_key_id' => $privateKey->id,
'cloud_provider_token_id' => $token->id,
'vultr_instance_id' => $vultrInstanceId,
'vultr_instance_status' => $vultrInstance['status'] ?? null,
]);
}
$server->proxy->set('status', 'exited');
$server->proxy->set('type', ProxyTypes::TRAEFIK->value);
$server->save();
$server->proxy->set('status', 'exited');
$server->proxy->set('type', ProxyTypes::TRAEFIK->value);
$server->save();
return $server;
});
try {
$vultrInstance = $vultrService->waitForPublicIp($vultrInstance, $request->disable_public_ipv4, $request->enable_ipv6);
$assignedIpAddress = $vultrService->getPublicIp($vultrInstance, $request->disable_public_ipv4, $request->enable_ipv6);
if ($assignedIpAddress && $assignedIpAddress !== $server->ip) {
$server->update([
'ip' => $assignedIpAddress,
'vultr_instance_status' => $vultrInstance['status'] ?? $server->vultr_instance_status,
]);
}
} catch (\Throwable $e) {
report($e);
}
if ($request->instant_validate) {
ValidateServer::dispatch($server);
@@ -353,27 +366,48 @@ class VultrController extends Controller
'team_id' => $teamId,
'server_uuid' => $server->uuid,
'server_name' => $server->name,
'vultr_instance_id' => $vultrInstance['id'],
'ip' => $ipAddress,
'vultr_instance_id' => $vultrInstanceId,
'ip' => $server->ip,
]);
return response()->json([
'uuid' => $server->uuid,
'vultr_instance_id' => $vultrInstance['id'],
'ip' => $ipAddress,
'vultr_instance_id' => $vultrInstanceId,
'ip' => $server->ip,
])->setStatusCode(201);
} catch (RateLimitException $e) {
$this->deleteUntrackedInstance($vultrService, $vultrInstanceId, $server);
$response = response()->json(['message' => $e->getMessage()], 429);
if ($e->retryAfter !== null) {
$response->header('Retry-After', $e->retryAfter);
}
return $response;
} catch (\Throwable) {
} catch (\Throwable $e) {
$this->deleteUntrackedInstance($vultrService, $vultrInstanceId, $server);
logger()->error('Failed to create Vultr server', [
'error' => $e->getMessage(),
]);
return response()->json(['message' => 'Failed to create Vultr server.'], 500);
}
}
private function deleteUntrackedInstance(?VultrService $vultrService, ?string $vultrInstanceId, ?Server $server): void
{
if (! $vultrService || ! $vultrInstanceId || $server) {
return;
}
try {
$vultrService->deleteInstance($vultrInstanceId);
} catch (\Throwable $e) {
report($e);
}
}
private function findMatchingSshKey(array $sshKeys, string $publicKey): ?array
{
$normalizedPublicKey = $this->normalizePublicKey($publicKey);