Files
Atomcms-edit/app/Services/UpdateHistoryService.php
T
2026-05-09 17:32:17 +02:00

187 lines
7.6 KiB
PHP
Executable File

<?php
namespace App\Services;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
class UpdateHistoryService
{
private const string TABLE = 'update_history';
public function ensureTableExists(): void
{
if (! Schema::hasTable(self::TABLE)) {
Schema::create(self::TABLE, function ($table) {
$table->id();
$table->string('type'); // nitro, emulator, sql, config
$table->string('action'); // update, build, deploy, fix
$table->string('item')->nullable(); // filename, version, etc
$table->string('status'); // success, failed, pending
$table->text('message')->nullable();
$table->string('user')->nullable();
$table->string('ip')->nullable();
$table->timestamp('created_at')->useCurrent();
$table->index(['type', 'created_at']);
$table->index('status');
});
}
}
public function log(string $type, string $action, ?string $item = null, string $status = 'success', ?string $message = null): void
{
$this->ensureTableExists();
DB::table(self::TABLE)->insert([
'type' => $type,
'action' => $action,
'item' => $item,
'status' => $status,
'message' => $message,
'user' => auth()->user()?->name ?? 'System',
'ip' => request()->ip(),
'created_at' => now(),
]);
}
public function getRecent(int $limit = 50): array
{
$this->ensureTableExists();
return DB::table(self::TABLE)
->orderBy('created_at', 'desc')
->limit($limit)
->get()
->toArray();
}
public function getByType(string $type, int $limit = 50): array
{
$this->ensureTableExists();
return DB::table(self::TABLE)
->where('type', $type)
->orderBy('created_at', 'desc')
->limit($limit)
->get()
->toArray();
}
public function getByStatus(string $status, int $limit = 50): array
{
$this->ensureTableExists();
return DB::table(self::TABLE)
->where('status', $status)
->orderBy('created_at', 'desc')
->limit($limit)
->get()
->toArray();
}
public function getStats(): array
{
$this->ensureTableExists();
$total = DB::table(self::TABLE)->count();
$success = DB::table(self::TABLE)->where('status', 'success')->count();
$failed = DB::table(self::TABLE)->where('status', 'failed')->count();
$lastUpdate = DB::table(self::TABLE)
->orderBy('created_at', 'desc')
->first();
$byType = DB::table(self::TABLE)
->select('type', DB::raw('count(*) as count'))
->groupBy('type')
->pluck('count', 'type')
->toArray();
return [
'total' => $total,
'success' => $success,
'failed' => $failed,
'success_rate' => $total > 0 ? round(($success / $total) * 100) : 100,
'last_update' => $lastUpdate,
'by_type' => $byType,
];
}
public function getHtml(): string
{
$this->ensureTableExists();
$updates = $this->getRecent(20);
$stats = $this->getStats();
$html = '<div style="font-family: -apple-system, BlinkMacSystemFont, sans-serif;">';
// Stats
$html .= '<div style="display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; margin-bottom: 16px;">';
$html .= '<div style="background: rgba(255,255,255,0.05); padding: 12px; border-radius: 8px; text-align: center;">';
$html .= '<div style="font-size: 24px; font-weight: 700; color: #4ade80;">' . $stats['total'] . '</div>';
$html .= '<div style="font-size: 10px; color: #64748b; text-transform: uppercase;">Totaal</div></div>';
$html .= '<div style="background: rgba(255,255,255,0.05); padding: 12px; border-radius: 8px; text-align: center;">';
$html .= '<div style="font-size: 24px; font-weight: 700; color: #4ade80;">' . $stats['success'] . '</div>';
$html .= '<div style="font-size: 10px; color: #64748b; text-transform: uppercase;">Geslaagd</div></div>';
$html .= '<div style="background: rgba(255,255,255,0.05); padding: 12px; border-radius: 8px; text-align: center;">';
$html .= '<div style="font-size: 24px; font-weight: 700; color: #f87171;">' . $stats['failed'] . '</div>';
$html .= '<div style="font-size: 10px; color: #64748b; text-transform: uppercase;">Mislukt</div></div>';
$html .= '<div style="background: rgba(255,255,255,0.05); padding: 12px; border-radius: 8px; text-align: center;">';
$html .= '<div style="font-size: 24px; font-weight: 700; color: #60a5fa;">' . $stats['success_rate'] . '%</div>';
$html .= '<div style="font-size: 10px; color: #64748b; text-transform: uppercase;">Succes</div></div>';
$html .= '</div>';
// History list
if ($updates === []) {
$html .= '<div style="text-align: center; padding: 24px; color: #64748b;">';
$html .= '<div style="font-size: 32px; margin-bottom: 8px;">📋</div>';
$html .= 'Nog geen update geschiedenis</div>';
} else {
$html .= '<div style="max-height: 400px; overflow-y: auto;">';
foreach ($updates as $update) {
$icon = match ($update->status) {
'success' => '✅',
'failed' => '❌',
'pending' => '⏳',
default => '⚪'
};
$typeColor = match ($update->type) {
'nitro' => '#4ade80',
'emulator' => '#60a5fa',
'sql' => '#fbbf24',
'config' => '#a78bfa',
default => '#94a3b8'
};
$time = Carbon::parse($update->created_at)->diffForHumans();
$html .= '<div style="display: flex; align-items: center; gap: 12px; padding: 10px 12px; background: rgba(255,255,255,0.03); border-radius: 8px; margin-bottom: 6px;">';
$html .= '<div style="font-size: 16px;">' . $icon . '</div>';
$html .= '<div style="flex: 1;">';
$html .= '<div style="display: flex; align-items: center; gap: 8px;">';
$html .= '<span style="background: ' . $typeColor . '20; color: ' . $typeColor . '; padding: 2px 8px; border-radius: 12px; font-size: 10px; font-weight: 600; text-transform: uppercase;">' . e($update->type) . '</span>';
$html .= '<span style="color: #e2e8f0; font-size: 13px;">' . e($update->action) . '</span>';
if ($update->item) {
$html .= '<span style="color: #94a3b8; font-size: 12px;">' . e($update->item) . '</span>';
}
$html .= '</div>';
if ($update->message) {
$html .= '<div style="color: #64748b; font-size: 11px; margin-top: 2px;">' . e($update->message) . '</div>';
}
$html .= '</div>';
$html .= '<div style="text-align: right;">';
$html .= '<div style="color: #64748b; font-size: 10px;">' . e($update->user) . '</div>';
$html .= '<div style="color: #475569; font-size: 9px;">' . e($time) . '</div>';
$html .= '</div>';
$html .= '</div>';
}
$html .= '</div>';
}
return $html . '</div>';
}
}