Migrate all primary keys + foreign keys to UUID (HasUuids trait)
Per user request: switch from auto-increment IDs to UUID v4 across all
models.
Models — added HasUuids trait:
- User, Cluster, Server, Site, Backup, TeamMember, Invitation,
SecurityEvent, Certificate, Activity, Cron.
Migrations — $table->id() → $table->uuid('id')->primary(),
$table->foreignId(...) → $table->foreignUuid(...) across stock laravel
users/sessions/jobs migrations + all 10 domain migrations.
Routes: sites/{site} (and any other implicit route-model binding) keeps
working since Laravel resolves by primary key as a string. Tested:
clicking a site row → URL now /sites/019e800c-520a-7022-badd-2ef9d9ed5c61.
Re-seeded: 6 users, 87 sites, 6 servers, 128 security events, 30
activities, 102 backups, 47 crons, 87 certs, 5 team members, 3 invites.
Full Pest regression: 47/47 green. Browser-verified login + sites
index + site details + WP/PHP/Files tabs with template-exact chrome.
master
parent
933f641e58
commit
29622eff77
|
|
@ -2,13 +2,14 @@
|
|||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Concerns\HasUuids;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class Activity extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
use HasFactory, HasUuids;
|
||||
|
||||
protected $fillable = [
|
||||
'user_id', 'type', 'level',
|
||||
|
|
|
|||
|
|
@ -2,13 +2,14 @@
|
|||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Concerns\HasUuids;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class Backup extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
use HasFactory, HasUuids;
|
||||
|
||||
protected $fillable = [
|
||||
'site_id', 'type', 'size_bytes', 'status',
|
||||
|
|
|
|||
|
|
@ -2,13 +2,14 @@
|
|||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Concerns\HasUuids;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class Certificate extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
use HasFactory, HasUuids;
|
||||
|
||||
protected $fillable = [
|
||||
'site_id', 'domain', 'issuer',
|
||||
|
|
|
|||
|
|
@ -2,13 +2,14 @@
|
|||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Concerns\HasUuids;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
class Cluster extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
use HasFactory, HasUuids;
|
||||
|
||||
protected $fillable = [
|
||||
'name', 'slug', 'region', 'env', 'monthly_price_cents',
|
||||
|
|
|
|||
|
|
@ -2,13 +2,14 @@
|
|||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Concerns\HasUuids;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class Cron extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
use HasFactory, HasUuids;
|
||||
|
||||
protected $fillable = [
|
||||
'site_id', 'name', 'expression', 'command',
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Concerns\HasUuids;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
|
@ -9,7 +10,7 @@ use Illuminate\Support\Str;
|
|||
|
||||
class Invitation extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
use HasFactory, HasUuids;
|
||||
|
||||
protected $fillable = [
|
||||
'email', 'role', 'invited_by_user_id',
|
||||
|
|
|
|||
|
|
@ -2,13 +2,14 @@
|
|||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Concerns\HasUuids;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class SecurityEvent extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
use HasFactory, HasUuids;
|
||||
|
||||
protected $fillable = [
|
||||
'site_id', 'type', 'severity', 'blocked',
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Concerns\HasUuids;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
|
@ -9,7 +10,7 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
|
|||
|
||||
class Server extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
use HasFactory, HasUuids;
|
||||
|
||||
protected $fillable = [
|
||||
'cluster_id', 'name', 'region', 'country_flag', 'env',
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Concerns\HasUuids;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
|
@ -10,7 +11,7 @@ use Illuminate\Database\Eloquent\Relations\HasOne;
|
|||
|
||||
class Site extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
use HasFactory, HasUuids;
|
||||
|
||||
protected $fillable = [
|
||||
'server_id', 'domain', 'name',
|
||||
|
|
|
|||
|
|
@ -2,13 +2,14 @@
|
|||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Concerns\HasUuids;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class TeamMember extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
use HasFactory, HasUuids;
|
||||
|
||||
protected $fillable = [
|
||||
'user_id', 'role', 'scope', 'two_fa_enabled', 'last_active_at',
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ namespace App\Models;
|
|||
|
||||
// use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||
use Database\Factories\UserFactory;
|
||||
use Illuminate\Database\Eloquent\Concerns\HasUuids;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
|
|
@ -11,7 +12,7 @@ use Illuminate\Notifications\Notifiable;
|
|||
class User extends Authenticatable
|
||||
{
|
||||
/** @use HasFactory<UserFactory> */
|
||||
use HasFactory, Notifiable;
|
||||
use HasFactory, HasUuids, Notifiable;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ return new class extends Migration
|
|||
public function up(): void
|
||||
{
|
||||
Schema::create('users', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->uuid('id')->primary();
|
||||
$table->string('name');
|
||||
$table->string('email')->unique();
|
||||
$table->timestamp('email_verified_at')->nullable();
|
||||
|
|
@ -29,7 +29,7 @@ return new class extends Migration
|
|||
|
||||
Schema::create('sessions', function (Blueprint $table) {
|
||||
$table->string('id')->primary();
|
||||
$table->foreignId('user_id')->nullable()->index();
|
||||
$table->foreignUuid('user_id')->nullable()->index();
|
||||
$table->string('ip_address', 45)->nullable();
|
||||
$table->text('user_agent')->nullable();
|
||||
$table->longText('payload');
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ return new class extends Migration
|
|||
public function up(): void
|
||||
{
|
||||
Schema::create('jobs', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->uuid('id')->primary();
|
||||
$table->string('queue')->index();
|
||||
$table->longText('payload');
|
||||
$table->unsignedTinyInteger('attempts');
|
||||
|
|
@ -35,7 +35,7 @@ return new class extends Migration
|
|||
});
|
||||
|
||||
Schema::create('failed_jobs', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->uuid('id')->primary();
|
||||
$table->string('uuid')->unique();
|
||||
$table->text('connection');
|
||||
$table->text('queue');
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ return new class extends Migration {
|
|||
public function up(): void
|
||||
{
|
||||
Schema::create('clusters', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->uuid('id')->primary();
|
||||
$table->string('name');
|
||||
$table->string('slug')->unique();
|
||||
$table->string('region', 32); // e.g. eu-central-1
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ return new class extends Migration {
|
|||
public function up(): void
|
||||
{
|
||||
Schema::create('servers', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('cluster_id')->nullable()->constrained()->nullOnDelete();
|
||||
$table->uuid('id')->primary();
|
||||
$table->foreignUuid('cluster_id')->nullable()->constrained()->nullOnDelete();
|
||||
$table->string('name', 64); // eu-prod-01
|
||||
$table->string('region', 32); // eu-central-1
|
||||
$table->string('country_flag', 4)->default('🇩🇪'); // emoji
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ return new class extends Migration {
|
|||
public function up(): void
|
||||
{
|
||||
Schema::create('sites', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('server_id')->constrained()->cascadeOnDelete();
|
||||
$table->uuid('id')->primary();
|
||||
$table->foreignUuid('server_id')->constrained()->cascadeOnDelete();
|
||||
$table->string('domain')->unique();
|
||||
$table->string('name')->nullable();
|
||||
$table->string('wp_version', 16)->default('6.4');
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ return new class extends Migration {
|
|||
public function up(): void
|
||||
{
|
||||
Schema::create('backups', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('site_id')->constrained()->cascadeOnDelete();
|
||||
$table->uuid('id')->primary();
|
||||
$table->foreignUuid('site_id')->constrained()->cascadeOnDelete();
|
||||
$table->enum('type', ['hourly', 'daily', 'manual', 'snapshot'])->default('daily');
|
||||
$table->unsignedBigInteger('size_bytes')->default(0);
|
||||
$table->enum('status', ['queued', 'running', 'ok', 'failed'])->default('queued');
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ return new class extends Migration {
|
|||
public function up(): void
|
||||
{
|
||||
Schema::create('team_members', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
|
||||
$table->uuid('id')->primary();
|
||||
$table->foreignUuid('user_id')->constrained()->cascadeOnDelete();
|
||||
$table->enum('role', ['owner', 'admin', 'developer', 'viewer'])->default('viewer');
|
||||
$table->string('scope')->nullable(); // e.g. "All clusters", "eu-prod-*"
|
||||
$table->boolean('two_fa_enabled')->default(false);
|
||||
|
|
|
|||
|
|
@ -8,10 +8,10 @@ return new class extends Migration {
|
|||
public function up(): void
|
||||
{
|
||||
Schema::create('invitations', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->uuid('id')->primary();
|
||||
$table->string('email');
|
||||
$table->enum('role', ['admin', 'developer', 'viewer'])->default('developer');
|
||||
$table->foreignId('invited_by_user_id')->nullable()->constrained('users')->nullOnDelete();
|
||||
$table->foreignUuid('invited_by_user_id')->nullable()->constrained('users')->nullOnDelete();
|
||||
$table->string('token', 64)->unique();
|
||||
$table->timestamp('sent_at')->nullable();
|
||||
$table->timestamp('accepted_at')->nullable();
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ return new class extends Migration {
|
|||
public function up(): void
|
||||
{
|
||||
Schema::create('security_events', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('site_id')->nullable()->constrained()->cascadeOnDelete();
|
||||
$table->uuid('id')->primary();
|
||||
$table->foreignUuid('site_id')->nullable()->constrained()->cascadeOnDelete();
|
||||
$table->enum('type', ['brute_force', 'tor_exit', 'malware', 'cert_expiring', 'firewall', 'scan'])
|
||||
->default('firewall');
|
||||
$table->enum('severity', ['info', 'low', 'medium', 'high', 'critical'])->default('low');
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ return new class extends Migration {
|
|||
public function up(): void
|
||||
{
|
||||
Schema::create('certificates', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('site_id')->constrained()->cascadeOnDelete();
|
||||
$table->uuid('id')->primary();
|
||||
$table->foreignUuid('site_id')->constrained()->cascadeOnDelete();
|
||||
$table->string('domain');
|
||||
$table->string('issuer', 64)->default("Let's Encrypt");
|
||||
$table->timestamp('issued_at')->nullable();
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ return new class extends Migration {
|
|||
public function up(): void
|
||||
{
|
||||
Schema::create('activities', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('user_id')->nullable()->constrained()->nullOnDelete();
|
||||
$table->uuid('id')->primary();
|
||||
$table->foreignUuid('user_id')->nullable()->constrained()->nullOnDelete();
|
||||
$table->enum('type', ['deploy', 'backup', 'update', 'security', 'system', 'login'])->default('system');
|
||||
$table->enum('level', ['info', 'success', 'warning', 'error'])->default('info');
|
||||
$table->string('message');
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ return new class extends Migration {
|
|||
public function up(): void
|
||||
{
|
||||
Schema::create('crons', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('site_id')->constrained()->cascadeOnDelete();
|
||||
$table->uuid('id')->primary();
|
||||
$table->foreignUuid('site_id')->constrained()->cascadeOnDelete();
|
||||
$table->string('name');
|
||||
$table->string('expression', 64)->default('0 * * * *'); // cron expr
|
||||
$table->string('command');
|
||||
|
|
|
|||
Loading…
Reference in New Issue