mirror of
https://github.com/dyzulk/trustlab-api.git
synced 2026-01-26 05:15:35 +07:00
feat: implement manual CDN sync for CA certificates and establish master AI rules
This commit is contained in:
@@ -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)
|
**WAJIB BACA:**
|
||||||
Sebelum melakukan modifikasi file atau memberikan saran teknis, AI HARUS membaca:
|
[Master AI Instructions (Root)](file:///D:/Lab/HomeLab/Github/dyzulk/trustlab/.agent/workflows/ai-instructions.md)
|
||||||
* `.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).
|
|
||||||
|
|
||||||
## 2. Protokol Komunikasi
|
Pahami bagian **PART 4: BACKEND (TRUSTLAB-API)** secara mendalam sebelum melakukan perubahan.
|
||||||
* **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.
|
|
||||||
|
|||||||
@@ -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()
|
protected function authorizeAdminOrOwner()
|
||||||
{
|
{
|
||||||
if (!auth()->user()->isAdminOrOwner()) {
|
if (!auth()->user()->isAdminOrOwner()) {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ namespace App\Services;
|
|||||||
|
|
||||||
use App\Models\CaCertificate;
|
use App\Models\CaCertificate;
|
||||||
use Illuminate\Support\Facades\Config;
|
use Illuminate\Support\Facades\Config;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
class OpenSslService
|
class OpenSslService
|
||||||
@@ -58,7 +59,7 @@ class OpenSslService
|
|||||||
? $this->formatHex($rootDetails['serialNumberHex'])
|
? $this->formatHex($rootDetails['serialNumberHex'])
|
||||||
: $this->formatSerialToHex($rootDetails['serialNumber']);
|
: $this->formatSerialToHex($rootDetails['serialNumber']);
|
||||||
|
|
||||||
CaCertificate::create([
|
$ca = CaCertificate::create([
|
||||||
'ca_type' => 'root',
|
'ca_type' => 'root',
|
||||||
'cert_content' => $rootCertPem,
|
'cert_content' => $rootCertPem,
|
||||||
'key_content' => $rootKeyPem,
|
'key_content' => $rootKeyPem,
|
||||||
@@ -69,6 +70,8 @@ class OpenSslService
|
|||||||
'valid_to' => date('Y-m-d H:i:s', $rootDetails['validTo_time_t']),
|
'valid_to' => date('Y-m-d H:i:s', $rootDetails['validTo_time_t']),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$this->uploadToCdn($ca);
|
||||||
|
|
||||||
// Intermediate CA 4096-bit
|
// Intermediate CA 4096-bit
|
||||||
$int4096Key = openssl_pkey_new([
|
$int4096Key = openssl_pkey_new([
|
||||||
'private_key_bits' => 4096,
|
'private_key_bits' => 4096,
|
||||||
@@ -95,7 +98,7 @@ class OpenSslService
|
|||||||
? $this->formatHex($int4096Details['serialNumberHex'])
|
? $this->formatHex($int4096Details['serialNumberHex'])
|
||||||
: $this->formatSerialToHex($int4096Details['serialNumber']);
|
: $this->formatSerialToHex($int4096Details['serialNumber']);
|
||||||
|
|
||||||
CaCertificate::create([
|
$ca4096 = CaCertificate::create([
|
||||||
'ca_type' => 'intermediate_4096',
|
'ca_type' => 'intermediate_4096',
|
||||||
'cert_content' => $int4096CertPem,
|
'cert_content' => $int4096CertPem,
|
||||||
'key_content' => $int4096KeyPem,
|
'key_content' => $int4096KeyPem,
|
||||||
@@ -106,6 +109,8 @@ class OpenSslService
|
|||||||
'valid_to' => date('Y-m-d H:i:s', $int4096Details['validTo_time_t']),
|
'valid_to' => date('Y-m-d H:i:s', $int4096Details['validTo_time_t']),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$this->uploadToCdn($ca4096);
|
||||||
|
|
||||||
// Intermediate CA 2048-bit
|
// Intermediate CA 2048-bit
|
||||||
$int2048Key = openssl_pkey_new([
|
$int2048Key = openssl_pkey_new([
|
||||||
'private_key_bits' => 2048,
|
'private_key_bits' => 2048,
|
||||||
@@ -132,7 +137,7 @@ class OpenSslService
|
|||||||
? $this->formatHex($int2048Details['serialNumberHex'])
|
? $this->formatHex($int2048Details['serialNumberHex'])
|
||||||
: $this->formatSerialToHex($int2048Details['serialNumber']);
|
: $this->formatSerialToHex($int2048Details['serialNumber']);
|
||||||
|
|
||||||
CaCertificate::create([
|
$ca2048 = CaCertificate::create([
|
||||||
'ca_type' => 'intermediate_2048',
|
'ca_type' => 'intermediate_2048',
|
||||||
'cert_content' => $int2048CertPem,
|
'cert_content' => $int2048CertPem,
|
||||||
'key_content' => $int2048KeyPem,
|
'key_content' => $int2048KeyPem,
|
||||||
@@ -143,6 +148,8 @@ class OpenSslService
|
|||||||
'valid_to' => date('Y-m-d H:i:s', $int2048Details['validTo_time_t']),
|
'valid_to' => date('Y-m-d H:i:s', $int2048Details['validTo_time_t']),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$this->uploadToCdn($ca2048);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} finally {
|
} finally {
|
||||||
if (file_exists($configFile)) unlink($configFile);
|
if (file_exists($configFile)) unlink($configFile);
|
||||||
@@ -404,4 +411,28 @@ class OpenSslService
|
|||||||
if ($configFile && file_exists($configFile)) unlink($configFile);
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::connection('mysql_ca')->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']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -59,6 +59,7 @@ Route::middleware(['auth:sanctum'])->group(function () {
|
|||||||
|
|
||||||
// Root CA Management (Admin Only)
|
// Root CA Management (Admin Only)
|
||||||
Route::get('/admin/ca-certificates', [RootCaApiController::class, 'index']);
|
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']);
|
Route::post('/admin/ca-certificates/{certificate}/renew', [RootCaApiController::class, 'renew']);
|
||||||
|
|
||||||
// API Keys Management
|
// API Keys Management
|
||||||
|
|||||||
Reference in New Issue
Block a user