mirror of
https://github.com/dyzulk/trustlab-api.git
synced 2026-01-26 13:22:05 +07:00
feat: unify ca_certificates migrations (clean slate)
This commit is contained in:
@@ -27,6 +27,7 @@ class PublicCaController extends Controller
|
|||||||
'serial' => $cert->serial_number,
|
'serial' => $cert->serial_number,
|
||||||
'expires_at' => $cert->valid_to->toIso8601String(),
|
'expires_at' => $cert->valid_to->toIso8601String(),
|
||||||
'cdn_url' => $cert->cert_path ? Storage::disk('r2-public')->url($cert->cert_path) : null,
|
'cdn_url' => $cert->cert_path ? Storage::disk('r2-public')->url($cert->cert_path) : null,
|
||||||
|
'der_cdn_url' => $cert->der_path ? Storage::disk('r2-public')->url($cert->der_path) : null,
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -52,6 +53,11 @@ class PublicCaController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($format === 'der') {
|
if ($format === 'der') {
|
||||||
|
// Redirect to CDN if path exists and format is DER
|
||||||
|
if ($cert->der_path) {
|
||||||
|
return redirect()->away(Storage::disk('r2-public')->url($cert->der_path));
|
||||||
|
}
|
||||||
|
|
||||||
// Convert PEM to DER (Base64 decode the body)
|
// Convert PEM to DER (Base64 decode the body)
|
||||||
$pem = $cert->cert_content;
|
$pem = $cert->cert_content;
|
||||||
$lines = explode("\n", trim($pem));
|
$lines = explode("\n", trim($pem));
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ class CaCertificate extends Model
|
|||||||
'valid_from',
|
'valid_from',
|
||||||
'valid_to',
|
'valid_to',
|
||||||
'cert_path',
|
'cert_path',
|
||||||
|
'der_path',
|
||||||
'last_synced_at',
|
'last_synced_at',
|
||||||
'download_count',
|
'download_count',
|
||||||
'last_downloaded_at'
|
'last_downloaded_at'
|
||||||
|
|||||||
@@ -412,20 +412,39 @@ class OpenSslService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Upload CA certificate (public) to R2 CDN.
|
* Upload CA certificate (public) to R2 CDN in both PEM and DER formats.
|
||||||
*/
|
*/
|
||||||
public function uploadToCdn(CaCertificate $cert)
|
public function uploadToCdn(CaCertificate $cert)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$filename = 'ca/' . Str::slug($cert->common_name) . '-' . $cert->uuid . '.crt';
|
$baseFilename = 'ca/' . Str::slug($cert->common_name) . '-' . $cert->uuid;
|
||||||
|
$pemFilename = $baseFilename . '.crt';
|
||||||
|
$derFilename = $baseFilename . '.der';
|
||||||
|
|
||||||
Storage::disk('r2-public')->put($filename, $cert->cert_content, [
|
// 1. Upload PEM (.crt)
|
||||||
|
Storage::disk('r2-public')->put($pemFilename, $cert->cert_content, [
|
||||||
|
'visibility' => 'public',
|
||||||
|
'ContentType' => 'application/x-x509-ca-cert'
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 2. Convert to DER and Upload (.der)
|
||||||
|
$lines = explode("\n", trim($cert->cert_content));
|
||||||
|
$payload = '';
|
||||||
|
foreach ($lines as $line) {
|
||||||
|
if (!str_starts_with($line, '-----')) {
|
||||||
|
$payload .= trim($line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$derContent = base64_decode($payload);
|
||||||
|
|
||||||
|
Storage::disk('r2-public')->put($derFilename, $derContent, [
|
||||||
'visibility' => 'public',
|
'visibility' => 'public',
|
||||||
'ContentType' => 'application/x-x509-ca-cert'
|
'ContentType' => 'application/x-x509-ca-cert'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$cert->update([
|
$cert->update([
|
||||||
'cert_path' => $filename,
|
'cert_path' => $pemFilename,
|
||||||
|
'der_path' => $derFilename,
|
||||||
'last_synced_at' => now()
|
'last_synced_at' => now()
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|||||||
@@ -11,23 +11,42 @@ return new class extends Migration
|
|||||||
*/
|
*/
|
||||||
public function up(): void
|
public function up(): void
|
||||||
{
|
{
|
||||||
|
// 1. Create table if not exists (Clean Slate for fresh install)
|
||||||
if (!Schema::connection('mysql_ca')->hasTable('ca_certificates')) {
|
if (!Schema::connection('mysql_ca')->hasTable('ca_certificates')) {
|
||||||
Schema::connection('mysql_ca')->create('ca_certificates', function (Blueprint $table) {
|
Schema::connection('mysql_ca')->create('ca_certificates', function (Blueprint $table) {
|
||||||
$table->string('uuid', 32)->primary();
|
$table->string('uuid', 32)->primary();
|
||||||
$table->string('ca_type'); // root, intermediate_4096, intermediate_2048
|
$table->string('ca_type');
|
||||||
$table->longText('cert_content')->nullable();
|
$table->longText('cert_content')->nullable();
|
||||||
$table->longText('key_content')->nullable();
|
$table->longText('key_content')->nullable();
|
||||||
$table->string('serial_number')->nullable();
|
$table->string('serial_number')->nullable();
|
||||||
|
|
||||||
|
// CDN Integration Columns
|
||||||
|
$table->string('cert_path')->nullable();
|
||||||
|
$table->string('der_path')->nullable();
|
||||||
|
$table->timestamp('last_synced_at')->nullable();
|
||||||
|
|
||||||
$table->string('common_name')->nullable();
|
$table->string('common_name')->nullable();
|
||||||
$table->string('organization')->nullable();
|
$table->string('organization')->nullable();
|
||||||
$table->dateTime('valid_from')->nullable();
|
$table->dateTime('valid_from')->nullable();
|
||||||
$table->dateTime('valid_to')->nullable();
|
$table->dateTime('valid_to')->nullable();
|
||||||
|
|
||||||
// Tracking
|
|
||||||
$table->unsignedBigInteger('download_count')->default(0);
|
$table->unsignedBigInteger('download_count')->default(0);
|
||||||
$table->timestamp('last_downloaded_at')->nullable();
|
$table->timestamp('last_downloaded_at')->nullable();
|
||||||
$table->timestamps();
|
$table->timestamps();
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
// 2. Self-Healing: Add missing columns if table already exists (Production Sync)
|
||||||
|
Schema::connection('mysql_ca')->table('ca_certificates', function (Blueprint $table) {
|
||||||
|
if (!Schema::connection('mysql_ca')->hasColumn('ca_certificates', 'cert_path')) {
|
||||||
|
$table->string('cert_path')->nullable()->after('serial_number');
|
||||||
|
}
|
||||||
|
if (!Schema::connection('mysql_ca')->hasColumn('ca_certificates', 'der_path')) {
|
||||||
|
$table->string('der_path')->nullable()->after('cert_path');
|
||||||
|
}
|
||||||
|
if (!Schema::connection('mysql_ca')->hasColumn('ca_certificates', 'last_synced_at')) {
|
||||||
|
$table->timestamp('last_synced_at')->nullable()->after('der_path');
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
<?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']);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Reference in New Issue
Block a user