Commit 8afc149c authored by Mahmoud Aglan's avatar Mahmoud Aglan

Fix logs endpoint and add Activate button for trial instances

- Switch logs from broken POST /appDefinitions/logs to working GET /appData/{app}
- Add x-namespace header to auth call
- Add activate() method to transition trial/suspended → active
- Add Activate button in instance-show view
- Rename "Container Logs" to "Build Logs" (reflects actual content)
Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
parent 4cf0a477
...@@ -138,6 +138,10 @@ public function fetchLogs(): void ...@@ -138,6 +138,10 @@ public function fetchLogs(): void
$this->logs = $caprover->getAppLogs($this->instance->app_name); $this->logs = $caprover->getAppLogs($this->instance->app_name);
$this->showLogs = true; $this->showLogs = true;
$this->logsError = null; $this->logsError = null;
if (empty(trim($this->logs))) {
$this->logs = '(No build logs available yet. Logs appear after the first deployment build.)';
}
} catch (\Throwable $e) { } catch (\Throwable $e) {
$this->logsError = $e->getMessage(); $this->logsError = $e->getMessage();
$this->showLogs = true; $this->showLogs = true;
...@@ -242,6 +246,23 @@ public function rollback(int $version): void ...@@ -242,6 +246,23 @@ public function rollback(int $version): void
} }
} }
public function activate(): void
{
if (!in_array($this->instance->status, ['trial', 'suspended'])) {
session()->flash('error', 'Cannot activate from current status.');
return;
}
$this->instance->update([
'status' => 'active',
'trial_ends_at' => null,
]);
AuditLog::record('activated', $this->instance, ['from' => $this->instance->getOriginal('status')]);
$this->instance->refresh();
session()->flash('success', 'Instance activated successfully.');
}
public function suspend(): void public function suspend(): void
{ {
app(InstanceProvisionerService::class)->suspend($this->instance, 'Manual suspension from dashboard'); app(InstanceProvisionerService::class)->suspend($this->instance, 'Manual suspension from dashboard');
......
...@@ -23,6 +23,7 @@ private function authenticate(): string ...@@ -23,6 +23,7 @@ private function authenticate(): string
} }
$response = Http::withoutVerifying() $response = Http::withoutVerifying()
->withHeaders(['x-namespace' => 'captain'])
->post("{$this->baseUrl}/api/v2/login", [ ->post("{$this->baseUrl}/api/v2/login", [
'password' => config('manager.caprover_password'), 'password' => config('manager.caprover_password'),
])->json(); ])->json();
...@@ -234,19 +235,21 @@ public function getAppInfo(string $appName): array ...@@ -234,19 +235,21 @@ public function getAppInfo(string $appName): array
public function getAppLogs(string $appName): string public function getAppLogs(string $appName): string
{ {
$token = $this->authenticate(); $data = $this->api('get', "/api/v2/user/apps/appData/{$appName}");
$response = Http::withoutVerifying() $logs = $data['logs'] ?? [];
->withHeaders([
'x-namespace' => 'captain',
'x-captain-auth' => $token,
])->post("{$this->baseUrl}/api/v2/user/apps/appDefinitions/logs", [
'appName' => $appName,
'encoding' => 'utf-8',
]);
$result = $response->json(); if (empty($logs) || empty($logs['lines'])) {
return $result['data']['logs'] ?? ''; return '';
}
$lines = $logs['lines'];
if (is_array($lines)) {
return implode('', array_map(fn ($line) => rtrim($line, "\n") . "\n", $lines));
}
return (string) $lines;
} }
public function listApps(): array public function listApps(): array
......
...@@ -69,6 +69,13 @@ class="inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-gray-70 ...@@ -69,6 +69,13 @@ class="inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-gray-70
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"/></svg> <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"/></svg>
Open App Open App
</a> </a>
@if($instance->status === 'trial' || $instance->isTrialExpired())
<button wire:click="activate" wire:confirm="Activate this instance? Trial period will end and billing will begin."
class="inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-emerald-700 bg-emerald-50 border border-emerald-200 rounded-lg hover:bg-emerald-100 transition">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
Activate
</button>
@endif
@if($instance->isActive() || $instance->isTrial()) @if($instance->isActive() || $instance->isTrial())
<button wire:click="suspend" wire:confirm="Suspend this instance? The app will return 503." <button wire:click="suspend" wire:confirm="Suspend this instance? The app will return 503."
class="inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-red-700 bg-red-50 border border-red-200 rounded-lg hover:bg-red-100 transition"> class="inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-red-700 bg-red-50 border border-red-200 rounded-lg hover:bg-red-100 transition">
...@@ -355,7 +362,7 @@ class="inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-white b ...@@ -355,7 +362,7 @@ class="inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-white b
{{-- Logs --}} {{-- Logs --}}
<div class="bg-white rounded-xl border border-gray-200 overflow-hidden mb-6"> <div class="bg-white rounded-xl border border-gray-200 overflow-hidden mb-6">
<div class="px-5 py-4 border-b border-gray-200 flex items-center justify-between"> <div class="px-5 py-4 border-b border-gray-200 flex items-center justify-between">
<h3 class="text-sm font-semibold text-gray-900">Container Logs</h3> <h3 class="text-sm font-semibold text-gray-900">Build Logs</h3>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
@if($showLogs) @if($showLogs)
<button wire:click="refreshLogs" wire:loading.attr="disabled" wire:target="refreshLogs" <button wire:click="refreshLogs" wire:loading.attr="disabled" wire:target="refreshLogs"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment