feat: add Reverb realtime messaging

This commit is contained in:
2026-03-21 12:51:59 +01:00
parent 60f78e8235
commit e8b5edf5d2
45 changed files with 3609 additions and 339 deletions

View File

@@ -0,0 +1,192 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Str;
return new class extends Migration
{
public function up(): void
{
Schema::table('conversations', function (Blueprint $table): void {
if (! Schema::hasColumn('conversations', 'uuid')) {
$table->uuid('uuid')->nullable()->after('id')->unique();
}
if (! Schema::hasColumn('conversations', 'last_message_id')) {
$table->unsignedBigInteger('last_message_id')->nullable()->after('created_by')->index();
}
if (! Schema::hasColumn('conversations', 'is_active')) {
$table->boolean('is_active')->default(true)->after('last_message_at')->index();
}
});
Schema::table('conversation_participants', function (Blueprint $table): void {
if (! Schema::hasColumn('conversation_participants', 'last_read_message_id')) {
$table->unsignedBigInteger('last_read_message_id')->nullable()->after('last_read_at')->index();
}
if (! Schema::hasColumn('conversation_participants', 'is_hidden')) {
$table->boolean('is_hidden')->default(false)->after('is_archived');
}
$table->index(['user_id', 'last_read_at'], 'conversation_participants_user_last_read_idx');
});
Schema::table('messages', function (Blueprint $table): void {
if (! Schema::hasColumn('messages', 'uuid')) {
$table->uuid('uuid')->nullable()->after('id')->unique();
}
if (! Schema::hasColumn('messages', 'client_temp_id')) {
$table->string('client_temp_id', 120)->nullable()->after('uuid');
$table->index(['conversation_id', 'client_temp_id'], 'messages_conversation_client_temp_idx');
}
if (! Schema::hasColumn('messages', 'message_type')) {
$table->string('message_type', 32)->default('text')->after('sender_id');
}
if (! Schema::hasColumn('messages', 'meta_json')) {
$table->json('meta_json')->nullable()->after('body');
}
if (! Schema::hasColumn('messages', 'reply_to_message_id')) {
$table->unsignedBigInteger('reply_to_message_id')->nullable()->after('meta_json')->index();
}
});
Schema::table('message_attachments', function (Blueprint $table): void {
if (! Schema::hasColumn('message_attachments', 'disk')) {
$table->string('disk', 64)->default('local')->after('message_id');
}
});
if (! Schema::hasTable('message_reads')) {
Schema::create('message_reads', function (Blueprint $table): void {
$table->id();
$table->foreignId('message_id')->constrained()->cascadeOnDelete();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->timestamp('read_at');
$table->unique(['message_id', 'user_id']);
$table->index('user_id');
});
}
DB::table('conversations')
->select('id')
->whereNull('uuid')
->orderBy('id')
->chunkById(200, function ($rows): void {
foreach ($rows as $row) {
DB::table('conversations')
->where('id', $row->id)
->update(['uuid' => (string) Str::uuid()]);
}
});
DB::table('messages')
->select('id')
->whereNull('uuid')
->orderBy('id')
->chunkById(200, function ($rows): void {
foreach ($rows as $row) {
DB::table('messages')
->where('id', $row->id)
->update(['uuid' => (string) Str::uuid()]);
}
});
DB::table('conversations')
->select('id')
->orderBy('id')
->chunkById(200, function ($rows): void {
foreach ($rows as $row) {
$lastMessageId = DB::table('messages')
->where('conversation_id', $row->id)
->whereNull('deleted_at')
->orderByDesc('created_at')
->orderByDesc('id')
->value('id');
DB::table('conversations')
->where('id', $row->id)
->update([
'last_message_id' => $lastMessageId,
'is_active' => true,
]);
}
});
}
public function down(): void
{
if (Schema::hasTable('message_reads')) {
Schema::drop('message_reads');
}
Schema::table('message_attachments', function (Blueprint $table): void {
if (Schema::hasColumn('message_attachments', 'disk')) {
$table->dropColumn('disk');
}
});
Schema::table('messages', function (Blueprint $table): void {
if (Schema::hasColumn('messages', 'reply_to_message_id')) {
$table->dropIndex('messages_reply_to_message_id_index');
$table->dropColumn('reply_to_message_id');
}
if (Schema::hasColumn('messages', 'meta_json')) {
$table->dropColumn('meta_json');
}
if (Schema::hasColumn('messages', 'message_type')) {
$table->dropColumn('message_type');
}
if (Schema::hasColumn('messages', 'client_temp_id')) {
$table->dropIndex('messages_conversation_client_temp_idx');
$table->dropColumn('client_temp_id');
}
if (Schema::hasColumn('messages', 'uuid')) {
$table->dropUnique(['uuid']);
$table->dropColumn('uuid');
}
});
Schema::table('conversation_participants', function (Blueprint $table): void {
if (Schema::hasColumn('conversation_participants', 'is_hidden')) {
$table->dropColumn('is_hidden');
}
if (Schema::hasColumn('conversation_participants', 'last_read_message_id')) {
$table->dropIndex('conversation_participants_last_read_message_id_index');
$table->dropColumn('last_read_message_id');
}
$table->dropIndex('conversation_participants_user_last_read_idx');
});
Schema::table('conversations', function (Blueprint $table): void {
if (Schema::hasColumn('conversations', 'is_active')) {
$table->dropColumn('is_active');
}
if (Schema::hasColumn('conversations', 'last_message_id')) {
$table->dropIndex('conversations_last_message_id_index');
$table->dropColumn('last_message_id');
}
if (Schema::hasColumn('conversations', 'uuid')) {
$table->dropUnique(['uuid']);
$table->dropColumn('uuid');
}
});
}
};