diff --git a/.agent/workflows/ai-instructions.md b/.agent/workflows/ai-instructions.md index bff82c4..fc5f87d 100644 --- a/.agent/workflows/ai-instructions.md +++ b/.agent/workflows/ai-instructions.md @@ -1,27 +1,12 @@ --- -description: Pedoman Perilaku AI (Mencegah Pemborosan Kredit & Miskomunikasi) +description: Referensi Aturan AI TrustLab-API --- -# Aturan Khusus untuk AI Agent +# ⚠️ BACA ATURAN UTAMA SEBELUM BEKERJA -Untuk menghindari kesalahan komunikasi, pemborosan token, dan ketidakefisienan, SETIAP AI agent yang bekerja pada proyek ini WAJIB mengikuti protokol berikut: +Semua aturan untuk proyek ini telah dipusatkan di root folder untuk menghindari miskomunikasi. -## 1. Tahap Inisialisasi (WAJIB) -Sebelum melakukan modifikasi file atau memberikan saran teknis, AI HARUS membaca: -* `.agent/workflows/manage-env.md` (Pahami sistem 5-file environment). -* `.agent/workflows/database-guidelines.md` (Pahami arsitektur Multi-DB). -* `.agent/workflows/deployment.md` (Pahami alur CI/CD Webhook). +**WAJIB BACA:** +[Master AI Instructions (Root)](file:///D:/Lab/HomeLab/Github/dyzulk/trustlab/.agent/workflows/ai-instructions.md) -## 2. Protokol Komunikasi -* **BACA SEMUA POIN**: Jika User memberikan daftar bernomor (1, 2, 3), AI wajib menjawab atau memproses SEMUA nomor tersebut. Jangan melompati poin hanya karena merasa satu poin sudah mewakili. -* **KONFIRMASI SEBELUM EKSEKUSI**: Untuk tindakan yang berisiko tinggi (push ke public repo, mengubah config produksi), AI wajib memaparkan rencana audit keamanan terlebih dahulu. -* **RELEVANSI PROYEK**: Utamakan sistem yang sudah ada di proyek (seperti 5-file env) daripada menyarankan standar generik industri yang mungkin tidak cocok dengan workflow User. - -## 3. Keamanan & Sanitasi -* **Public Repository Awareness**: Selalu asumsikan repository ini adalah **PUBLIK**. -* Dilarang keras menyarankan `git add -f` pada file sensitif. -* Dilarang menyertakan path absolut server, IP rill, atau credential rill di dalam file yang akan di-push (gunakan file `.example` atau `.local` yang di-ignore). - -## 4. Efisiensi Kredit -* Lakukan pengecekan menyeluruh (Audit) secara internal sebelum memanggil tool atau menyimpulkan tugas selesai. -* Kesalahan membaca instruksi User yang mengakibatkan revisi berulang dianggap sebagai kegagalan efisiensi AI. +Pahami bagian **PART 4: BACKEND (TRUSTLAB-API)** secara mendalam sebelum melakukan perubahan. diff --git a/app/Http/Controllers/Api/RootCaApiController.php b/app/Http/Controllers/Api/RootCaApiController.php index ef787c6..72834f8 100644 --- a/app/Http/Controllers/Api/RootCaApiController.php +++ b/app/Http/Controllers/Api/RootCaApiController.php @@ -60,6 +60,32 @@ class RootCaApiController extends Controller } } + public function syncToCdn() + { + $this->authorizeAdminOrOwner(); + + try { + $certificates = CaCertificate::all(); + $count = 0; + + foreach ($certificates as $cert) { + if ($this->sslService->uploadToCdn($cert)) { + $count++; + } + } + + return response()->json([ + 'status' => 'success', + 'message' => "Successfully synced {$count} certificates to CDN." + ]); + } catch (\Exception $e) { + return response()->json([ + 'status' => 'error', + 'message' => 'Sync failed: ' . $e->getMessage() + ], 500); + } + } + protected function authorizeAdminOrOwner() { if (!auth()->user()->isAdminOrOwner()) { diff --git a/app/Services/OpenSslService.php b/app/Services/OpenSslService.php index 43a1114..b1ed1f3 100644 --- a/app/Services/OpenSslService.php +++ b/app/Services/OpenSslService.php @@ -4,6 +4,7 @@ namespace App\Services; use App\Models\CaCertificate; use Illuminate\Support\Facades\Config; +use Illuminate\Support\Facades\Storage; use Illuminate\Support\Str; class OpenSslService @@ -58,7 +59,7 @@ class OpenSslService ? $this->formatHex($rootDetails['serialNumberHex']) : $this->formatSerialToHex($rootDetails['serialNumber']); - CaCertificate::create([ + $ca = CaCertificate::create([ 'ca_type' => 'root', 'cert_content' => $rootCertPem, 'key_content' => $rootKeyPem, @@ -69,6 +70,8 @@ class OpenSslService 'valid_to' => date('Y-m-d H:i:s', $rootDetails['validTo_time_t']), ]); + $this->uploadToCdn($ca); + // Intermediate CA 4096-bit $int4096Key = openssl_pkey_new([ 'private_key_bits' => 4096, @@ -95,7 +98,7 @@ class OpenSslService ? $this->formatHex($int4096Details['serialNumberHex']) : $this->formatSerialToHex($int4096Details['serialNumber']); - CaCertificate::create([ + $ca4096 = CaCertificate::create([ 'ca_type' => 'intermediate_4096', 'cert_content' => $int4096CertPem, 'key_content' => $int4096KeyPem, @@ -106,6 +109,8 @@ class OpenSslService 'valid_to' => date('Y-m-d H:i:s', $int4096Details['validTo_time_t']), ]); + $this->uploadToCdn($ca4096); + // Intermediate CA 2048-bit $int2048Key = openssl_pkey_new([ 'private_key_bits' => 2048, @@ -132,7 +137,7 @@ class OpenSslService ? $this->formatHex($int2048Details['serialNumberHex']) : $this->formatSerialToHex($int2048Details['serialNumber']); - CaCertificate::create([ + $ca2048 = CaCertificate::create([ 'ca_type' => 'intermediate_2048', 'cert_content' => $int2048CertPem, 'key_content' => $int2048KeyPem, @@ -143,6 +148,8 @@ class OpenSslService 'valid_to' => date('Y-m-d H:i:s', $int2048Details['validTo_time_t']), ]); + $this->uploadToCdn($ca2048); + return true; } finally { if (file_exists($configFile)) unlink($configFile); @@ -404,4 +411,28 @@ class OpenSslService if ($configFile && file_exists($configFile)) unlink($configFile); } } + /** + * Upload CA certificate (public) to R2 CDN. + */ + public function uploadToCdn(CaCertificate $cert) + { + try { + $filename = 'ca/' . Str::slug($cert->common_name) . '-' . $cert->uuid . '.crt'; + + Storage::disk('r2-public')->put($filename, $cert->cert_content, [ + 'visibility' => 'public', + 'ContentType' => 'application/x-x509-ca-cert' + ]); + + $cert->update([ + 'cert_path' => $filename, + 'last_synced_at' => now() + ]); + + return true; + } catch (\Exception $e) { + \Log::error("Failed to upload CA to R2: " . $e->getMessage()); + return false; + } + } } diff --git a/database/migrations/2026_01_06_000002_add_cert_path_to_ca_certificates_table.php b/database/migrations/2026_01_06_000002_add_cert_path_to_ca_certificates_table.php new file mode 100644 index 0000000..f47181b --- /dev/null +++ b/database/migrations/2026_01_06_000002_add_cert_path_to_ca_certificates_table.php @@ -0,0 +1,29 @@ +table('ca_certificates', function (Blueprint $table) { + $table->string('cert_path')->nullable()->after('serial_number'); + $table->timestamp('last_synced_at')->nullable()->after('cert_path'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::connection('mysql_ca')->table('ca_certificates', function (Blueprint $table) { + $table->dropColumn(['cert_path', 'last_synced_at']); + }); + } +}; diff --git a/routes/api.php b/routes/api.php index 9c8f488..6e743b6 100644 --- a/routes/api.php +++ b/routes/api.php @@ -59,6 +59,7 @@ Route::middleware(['auth:sanctum'])->group(function () { // Root CA Management (Admin Only) Route::get('/admin/ca-certificates', [RootCaApiController::class, 'index']); + Route::post('/admin/ca-certificates/sync-cdn', [RootCaApiController::class, 'syncToCdn']); Route::post('/admin/ca-certificates/{certificate}/renew', [RootCaApiController::class, 'renew']); // API Keys Management