From e4e01e8beb21baf4e59626c203ff711c35e4d352 Mon Sep 17 00:00:00 2001 From: danielhe4rt Date: Mon, 25 May 2026 23:52:09 -0300 Subject: [PATCH 1/7] fix(identity): handle OAuth token exchange failures and missing credentials gracefully Prevents 500 errors when: Discord/GitHub/Twitch return error payloads instead of access tokens, user denies OAuth prompt (null code), or provider credentials are not configured in production. --- .../Auth/Exceptions/OAuthFlowException.php | 5 ++++ .../Auth/Http/Controllers/OAuthController.php | 28 +++++++++++++++++-- .../src/OAuth/DiscordOAuthClient.php | 10 ++++++- .../src/IntegrationGithubServiceProvider.php | 16 ++++++++--- .../src/OAuth/GitHubOAuthClient.php | 10 ++++++- .../src/OAuth/TwitchOAuthClient.php | 10 ++++++- 6 files changed, 70 insertions(+), 9 deletions(-) diff --git a/app-modules/identity/src/Auth/Exceptions/OAuthFlowException.php b/app-modules/identity/src/Auth/Exceptions/OAuthFlowException.php index 9f746e9a..5ea3d836 100644 --- a/app-modules/identity/src/Auth/Exceptions/OAuthFlowException.php +++ b/app-modules/identity/src/Auth/Exceptions/OAuthFlowException.php @@ -28,4 +28,9 @@ public static function tenantNotFound(string $identifier): self { return new self(sprintf('Tenant "%s" not found.', $identifier)); } + + public static function tokenExchangeFailed(string $provider, string $error): self + { + return new self(sprintf('Token exchange failed for "%s": %s', $provider, $error)); + } } diff --git a/app-modules/identity/src/Auth/Http/Controllers/OAuthController.php b/app-modules/identity/src/Auth/Http/Controllers/OAuthController.php index 8e050382..a5381a94 100644 --- a/app-modules/identity/src/Auth/Http/Controllers/OAuthController.php +++ b/app-modules/identity/src/Auth/Http/Controllers/OAuthController.php @@ -9,9 +9,12 @@ use He4rt\Identity\Auth\Actions\HandleOAuthCallbackAction; use He4rt\Identity\Auth\DTOs\OAuthStateDTO; use He4rt\Identity\Auth\Enums\OAuthIntent; +use He4rt\Identity\Auth\Exceptions\OAuthFlowException; use He4rt\Identity\ExternalIdentity\Enums\IdentityProvider; use Illuminate\Http\RedirectResponse; use Illuminate\Support\Facades\Auth; +use Illuminate\Support\Facades\Log; +use RuntimeException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; final class OAuthController extends Controller @@ -22,7 +25,13 @@ public function getRedirect(string $tenant, string $panel, string $provider): Re throw_if($identityProvider === null, NotFoundHttpException::class); - $client = $identityProvider->getClient(); + try { + $client = $identityProvider->getClient(); + } catch (RuntimeException $runtimeException) { + Log::warning('OAuth client not configured', ['provider' => $provider, 'error' => $runtimeException->getMessage()]); + + return redirect()->to('/'); + } throw_unless($client instanceof OAuthClientContract, NotFoundHttpException::class); @@ -45,7 +54,22 @@ public function getAuthenticate(string $provider, HandleOAuthCallbackAction $act $state = OAuthStateDTO::fromEncryptedString(request()->input('state')); - $result = $action->execute($state, $identityProvider, request()->input('code')); + $code = request()->input('code'); + $oauthDenied = $code === null || request()->has('error'); + + if ($oauthDenied) { + $fallbackUrl = $state->returnUrl ?? '/'; + + return redirect()->to($fallbackUrl); + } + + try { + $result = $action->execute($state, $identityProvider, $code); + } catch (OAuthFlowException $oAuthFlowException) { + Log::warning('OAuth flow failed', ['provider' => $provider, 'error' => $oAuthFlowException->getMessage()]); + + return redirect()->to($state->returnUrl ?? '/'); + } if ($result->hasMergeConflict()) { session()->put('oauth_merge_pending', $result->mergeConflict->toSession()); diff --git a/app-modules/integration-discord/src/OAuth/DiscordOAuthClient.php b/app-modules/integration-discord/src/OAuth/DiscordOAuthClient.php index 701d4238..c5baf73b 100644 --- a/app-modules/integration-discord/src/OAuth/DiscordOAuthClient.php +++ b/app-modules/integration-discord/src/OAuth/DiscordOAuthClient.php @@ -8,6 +8,7 @@ use He4rt\Identity\Auth\DTOs\OAuthAccessDTO; use He4rt\Identity\Auth\DTOs\OAuthStateDTO; use He4rt\Identity\Auth\DTOs\OAuthUserDTO; +use He4rt\Identity\Auth\Exceptions\OAuthFlowException; use He4rt\IntegrationDiscord\Transport\DiscordOAuthConnector; use He4rt\IntegrationDiscord\Transport\Requests\OAuth\ExchangeCodeForToken; use He4rt\IntegrationDiscord\Transport\Requests\OAuth\GetCurrentUser; @@ -38,7 +39,14 @@ public function auth(string $code): OAuthAccessDTO redirectUri: $this->callbackUrl(), )); - return DiscordOAuthAccessDTO::make($response->json()); + $payload = $response->json(); + $tokenExchangeFailed = !isset($payload['access_token']); + + if ($tokenExchangeFailed) { + throw OAuthFlowException::tokenExchangeFailed('discord', $payload['error'] ?? 'unknown'); + } + + return DiscordOAuthAccessDTO::make($payload); } public function getAuthenticatedUser(OAuthAccessDTO $credentials): OAuthUserDTO diff --git a/app-modules/integration-github/src/IntegrationGithubServiceProvider.php b/app-modules/integration-github/src/IntegrationGithubServiceProvider.php index d596c9df..9fad34ac 100644 --- a/app-modules/integration-github/src/IntegrationGithubServiceProvider.php +++ b/app-modules/integration-github/src/IntegrationGithubServiceProvider.php @@ -7,15 +7,23 @@ use He4rt\IntegrationGithub\Transport\GitHubApiConnector; use He4rt\IntegrationGithub\Transport\GitHubOAuthConnector; use Illuminate\Support\ServiceProvider; +use RuntimeException; class IntegrationGithubServiceProvider extends ServiceProvider { public function register(): void { - $this->app->singleton(GitHubOAuthConnector::class, fn () => new GitHubOAuthConnector( - clientId: config('services.github.client_id'), - clientSecret: config('services.github.client_secret'), - )); + $this->app->singleton(GitHubOAuthConnector::class, function (): GitHubOAuthConnector { + $clientId = config('services.github.client_id'); + $clientSecret = config('services.github.client_secret'); + + throw_if($clientId === null || $clientSecret === null, RuntimeException::class, 'GitHub OAuth credentials are not configured (services.github.client_id / client_secret).'); + + return new GitHubOAuthConnector( + clientId: $clientId, + clientSecret: $clientSecret, + ); + }); $this->app->singleton(GitHubApiConnector::class, fn () => new GitHubApiConnector()); } diff --git a/app-modules/integration-github/src/OAuth/GitHubOAuthClient.php b/app-modules/integration-github/src/OAuth/GitHubOAuthClient.php index b83d0042..c098d18d 100644 --- a/app-modules/integration-github/src/OAuth/GitHubOAuthClient.php +++ b/app-modules/integration-github/src/OAuth/GitHubOAuthClient.php @@ -7,6 +7,7 @@ use App\Contracts\OAuthClientContract; use He4rt\Identity\Auth\DTOs\OAuthAccessDTO; use He4rt\Identity\Auth\DTOs\OAuthStateDTO; +use He4rt\Identity\Auth\Exceptions\OAuthFlowException; use He4rt\IntegrationGithub\OAuth\DTO\GitHubOAuthAccessDTO; use He4rt\IntegrationGithub\OAuth\DTO\GitHubOAuthUserDTO; use He4rt\IntegrationGithub\Transport\GitHubApiConnector; @@ -40,7 +41,14 @@ public function auth(string $code): GitHubOAuthAccessDTO redirectUri: $this->callbackUrl(), )); - return GitHubOAuthAccessDTO::make($response->json()); + $payload = $response->json(); + $tokenExchangeFailed = !isset($payload['access_token']); + + if ($tokenExchangeFailed) { + throw OAuthFlowException::tokenExchangeFailed('github', $payload['error_description'] ?? $payload['error'] ?? 'unknown'); + } + + return GitHubOAuthAccessDTO::make($payload); } public function getAuthenticatedUser(OAuthAccessDTO $credentials): GitHubOAuthUserDTO diff --git a/app-modules/integration-twitch/src/OAuth/TwitchOAuthClient.php b/app-modules/integration-twitch/src/OAuth/TwitchOAuthClient.php index e634fc7a..cb0f2a18 100644 --- a/app-modules/integration-twitch/src/OAuth/TwitchOAuthClient.php +++ b/app-modules/integration-twitch/src/OAuth/TwitchOAuthClient.php @@ -7,6 +7,7 @@ use App\Contracts\OAuthClientContract; use He4rt\Identity\Auth\DTOs\OAuthAccessDTO; use He4rt\Identity\Auth\DTOs\OAuthStateDTO; +use He4rt\Identity\Auth\Exceptions\OAuthFlowException; use He4rt\IntegrationTwitch\OAuth\DTO\TwitchOAuthAccessDTO; use He4rt\IntegrationTwitch\OAuth\DTO\TwitchOAuthDTO; use He4rt\IntegrationTwitch\Transport\Requests\OAuth\ExchangeCodeForToken; @@ -46,7 +47,14 @@ public function auth(string $code): TwitchOAuthAccessDTO redirectUri: $this->callbackUrl(), )); - return TwitchOAuthAccessDTO::make($response->json()); + $payload = $response->json(); + $tokenExchangeFailed = !isset($payload['access_token']); + + if ($tokenExchangeFailed) { + throw OAuthFlowException::tokenExchangeFailed('twitch', $payload['message'] ?? $payload['error'] ?? 'unknown'); + } + + return TwitchOAuthAccessDTO::make($payload); } public function getAuthenticatedUser(OAuthAccessDTO $credentials): TwitchOAuthDTO From 131f43fc1df3dc864b1d76d15cd33757dd25dae6 Mon Sep 17 00:00:00 2001 From: danielhe4rt Date: Mon, 25 May 2026 23:58:22 -0300 Subject: [PATCH 2/7] fix(panel-app): add light mode support to ConnectionHub and profile wrapper Add dark: prefixed variants to all hardcoded dark-mode colors so the component renders correctly in both light and dark themes. --- .../resources/views/pages/profile.blade.php | 4 +- .../views/livewire/connection-hub.blade.php | 52 +++++++++++-------- 2 files changed, 33 insertions(+), 23 deletions(-) diff --git a/app-modules/panel-app/resources/views/pages/profile.blade.php b/app-modules/panel-app/resources/views/pages/profile.blade.php index 6b6c573a..3ccd6abb 100644 --- a/app-modules/panel-app/resources/views/pages/profile.blade.php +++ b/app-modules/panel-app/resources/views/pages/profile.blade.php @@ -29,8 +29,8 @@ 'coverPreviewUrl' => $this->coverPreviewUrl ]) -
-

+
+

{{ __('panel-app::profile.sections.connections') }}

diff --git a/resources/views/livewire/connection-hub.blade.php b/resources/views/livewire/connection-hub.blade.php index f7765567..a419c078 100644 --- a/resources/views/livewire/connection-hub.blade.php +++ b/resources/views/livewire/connection-hub.blade.php @@ -25,10 +25,11 @@ @endphp
!$connected + ]) + @if ($connected) style="border-color: {{ $brandColor }}30;" @endif >
{{-- Provider icon --}} @@ -45,7 +46,7 @@ class="h-4 w-4"
@if ($connected)
@endif
@@ -53,9 +54,12 @@ class="absolute -right-0.5 -bottom-0.5 h-2.5 w-2.5 rounded-full border-2 border- {{-- Content --}}
- {{ $provider->getLabel() }} + {{ $provider->getLabel() }} @if ($connected) - + {{ $connected->connected_at ->timezone(config('app.display_timezone')) @@ -75,7 +79,7 @@ class="h-3.5 w-3.5 rounded-full" loading="lazy" /> @endif - {{ + {{ $connected->metadata['username'] ?? $connected->external_account_id }} @@ -88,7 +92,7 @@ class="h-3.5 w-3.5 rounded-full" @@ -101,17 +105,17 @@ class="shrink-0 rounded-md px-2 py-0.5 text-[11px] font-medium text-gray-400 rin {{-- Permissions --}} @if (!$connected && count($scopes) > 0) -
+
@foreach ($scopes as $scope) {{ $scope }} @@ -147,27 +151,33 @@ class="rounded bg-gray-800/80 px-1 py-0.5 font-mono text-[9px] text-gray-400 rin x-transition class="fixed inset-0 z-50 flex items-center justify-center bg-black/60 p-4" > -
+
-

Conta existente encontrada

+

Conta existente encontrada

-

Já existe uma conta vinculada a esse provedor:

+

Já existe uma conta vinculada a esse provedor:

-
+
- @ {{ $mergeTarget['username'] }} - {{ $mergeTarget['created_at'] }} + @ {{ $mergeTarget['username'] }} + {{ $mergeTarget['created_at'] }}
@if ($mergeTarget['messages_count'] > 0) -
+
{{ number_format($mergeTarget['messages_count']) }} mensagens
@endif
-

Ao unificar, sua conta atual será absorvida por essa conta e você será relogado automaticamente.

+

Ao unificar, sua conta atual será absorvida por essa conta e você será relogado automaticamente.

From 03dd58ac048a530b2752b3fd9edd9da8347d4959 Mon Sep 17 00:00:00 2001 From: danielhe4rt Date: Tue, 26 May 2026 00:01:21 -0300 Subject: [PATCH 3/7] fix(identity): clear disconnected_at when reconnecting a provider AttachProviderToUser now resets disconnected_at to null on updateOrCreate, ensuring reconnected providers show as active in both Login and Link flows. --- app-modules/identity/src/Auth/Actions/AttachProviderToUser.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app-modules/identity/src/Auth/Actions/AttachProviderToUser.php b/app-modules/identity/src/Auth/Actions/AttachProviderToUser.php index f226d6f3..a7533acb 100644 --- a/app-modules/identity/src/Auth/Actions/AttachProviderToUser.php +++ b/app-modules/identity/src/Auth/Actions/AttachProviderToUser.php @@ -32,6 +32,7 @@ public function execute(User|Tenant $owner, Tenant $tenant, OAuthUserDTO $oauthU 'username' => $oauthUser->username, ]), 'connected_at' => now(), + 'disconnected_at' => null, 'connected_by' => auth()->id(), ] ); From e190406288b21acf35d04a9c089feffd9b4b47fb Mon Sep 17 00:00:00 2001 From: danielhe4rt Date: Tue, 26 May 2026 00:04:41 -0300 Subject: [PATCH 4/7] feat(portal): add Twitch login, user area navbar link, and fix hero avatar key MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add Twitch as OAuth login provider on the login page - Add outlined "Área do Usuário" button to portal navbar linking to /app - Fix HeroSection crash: metadata key is 'username' not 'name' --- .../resources/views/auth/login.blade.php | 8 +++++ .../views/components/navbar.blade.php | 30 +++++++++++-------- .../portal/src/Livewire/HeroSection.php | 4 ++- 3 files changed, 29 insertions(+), 13 deletions(-) diff --git a/app-modules/panel-app/resources/views/auth/login.blade.php b/app-modules/panel-app/resources/views/auth/login.blade.php index 7165646d..67b2fdb9 100644 --- a/app-modules/panel-app/resources/views/auth/login.blade.php +++ b/app-modules/panel-app/resources/views/auth/login.blade.php @@ -96,6 +96,14 @@ class="flex items-center justify-center gap-2.5 rounded-lg bg-zinc-800 px-4 py-2 @svg ('fab-github', 'h-5 w-5') Continuar com GitHub + + + @svg ('fab-twitch', 'h-5 w-5') + Continuar com Twitch +
{{-- Divider --}} diff --git a/app-modules/portal/resources/views/components/navbar.blade.php b/app-modules/portal/resources/views/components/navbar.blade.php index 2ed4fd2f..a4c6efcc 100644 --- a/app-modules/portal/resources/views/components/navbar.blade.php +++ b/app-modules/portal/resources/views/components/navbar.blade.php @@ -23,21 +23,27 @@ class="relative pb-1 text-sm font-medium text-gray-400 transition-colors after:a @endforeach
- - Discord - +
+ -
diff --git a/app-modules/portal/src/Livewire/HeroSection.php b/app-modules/portal/src/Livewire/HeroSection.php index 843bd628..ebc35bde 100644 --- a/app-modules/portal/src/Livewire/HeroSection.php +++ b/app-modules/portal/src/Livewire/HeroSection.php @@ -84,7 +84,9 @@ private function fetchAvatars(): array ->inRandomOrder() ->limit(10) ->pluck('metadata') - ->map(fn (array $metadata) => sprintf('https://github.com/%s.png', $metadata['name'])) + ->filter(fn (array $metadata) => isset($metadata['username'])) + ->map(fn (array $metadata) => sprintf('https://github.com/%s.png', $metadata['username'])) + ->values() ->all(); } From 4e70f2b77f23257d9f9fe6040daff74e9ac4fa95 Mon Sep 17 00:00:00 2001 From: danielhe4rt Date: Tue, 26 May 2026 00:08:29 -0300 Subject: [PATCH 5/7] fix(portal): support both legacy and new GitHub metadata formats in hero avatars Legacy records use 'name', new records use 'username'. Try both keys so the query returns results from all 2590 GitHub identities. --- app-modules/portal/src/Livewire/HeroSection.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app-modules/portal/src/Livewire/HeroSection.php b/app-modules/portal/src/Livewire/HeroSection.php index ebc35bde..53656286 100644 --- a/app-modules/portal/src/Livewire/HeroSection.php +++ b/app-modules/portal/src/Livewire/HeroSection.php @@ -78,14 +78,17 @@ private function fetchAvatars(): array return []; } + $githubHandle = fn (array $metadata): ?string => $metadata['username'] ?? $metadata['name'] ?? null; + return ExternalIdentity::query() ->where('provider', IdentityProvider::GitHub) ->whereIn('model_id', $activeUserIds) ->inRandomOrder() ->limit(10) ->pluck('metadata') - ->filter(fn (array $metadata) => isset($metadata['username'])) - ->map(fn (array $metadata) => sprintf('https://github.com/%s.png', $metadata['username'])) + ->map(fn (array $metadata) => $githubHandle($metadata)) + ->filter() + ->map(fn (string $handle) => sprintf('https://github.com/%s.png', $handle)) ->values() ->all(); } From bdb0f4f6f1db3dd50900102bcc592074d59979ba Mon Sep 17 00:00:00 2001 From: danielhe4rt Date: Tue, 26 May 2026 00:24:49 -0300 Subject: [PATCH 6/7] fix(migrations): replace bigint tenant_id/owner_id with uuid to match current schema All foreignId('tenant_id') changed to foreignUuid('tenant_id') and foreignIdFor references to UUID tables updated. Also restores the missing set_up_tenant_module migration that adds tenant_id to legacy tables (providers, messages, characters, etc). --- .../2026_03_18_000000_create_interactions_table.php | 2 +- .../2026_04_17_000001_add_metadata_to_messages_table.php | 4 ++-- .../2026_04_17_000002_create_moderation_events_table.php | 2 +- ...2026_04_17_000003_create_activity_reactions_table.php | 2 +- ..._04_20_110053_create_message_mentions_and_threads.php | 4 ++-- .../2026_04_20_111057_create_fase3_activity_tables.php | 6 +++--- .../2026_05_09_154113_create_activity_timeline_table.php | 6 ++---- .../2025_11_02_172528_create_tenants_table.php | 5 ++--- .../2025_11_02_172618_set_up_tenant_module.php | 9 ++------- .../2025_11_08_161609_create_tenant_users_table.php | 2 +- .../2026_05_19_200000_create_discord_guilds_table.php | 2 +- ...26_05_22_000001_create_twitch_subscriptions_table.php | 2 +- ...2_000002_add_tenant_id_to_twitch_event_logs_table.php | 2 +- .../2026_05_02_000001_create_moderation_cases_table.php | 2 +- .../2026_05_02_000005_create_moderation_rules_table.php | 2 +- ...26_05_02_000006_create_moderation_audit_log_table.php | 2 +- ...4_add_tenant_id_to_moderation_actions_and_appeals.php | 4 ++-- .../2026_05_21_000000_create_user_profiles_table.php | 2 +- 18 files changed, 26 insertions(+), 34 deletions(-) diff --git a/app-modules/activity/database/migrations/2026_03_18_000000_create_interactions_table.php b/app-modules/activity/database/migrations/2026_03_18_000000_create_interactions_table.php index 35861eb8..b71c6012 100644 --- a/app-modules/activity/database/migrations/2026_03_18_000000_create_interactions_table.php +++ b/app-modules/activity/database/migrations/2026_03_18_000000_create_interactions_table.php @@ -13,7 +13,7 @@ public function up(): void Schema::create('interactions', function (Blueprint $table): void { $table->uuid('id')->primary(); $table->foreignUuid('character_id')->constrained('characters'); - $table->foreignId('tenant_id')->constrained('tenants'); + $table->foreignUuid('tenant_id')->constrained('tenants'); $table->string('type'); $table->string('provider'); $table->string('value_tier'); diff --git a/app-modules/activity/database/migrations/2026_04_17_000001_add_metadata_to_messages_table.php b/app-modules/activity/database/migrations/2026_04_17_000001_add_metadata_to_messages_table.php index d3842a1e..756e1f70 100644 --- a/app-modules/activity/database/migrations/2026_04_17_000001_add_metadata_to_messages_table.php +++ b/app-modules/activity/database/migrations/2026_04_17_000001_add_metadata_to_messages_table.php @@ -12,13 +12,13 @@ public function up(): void { if (!Schema::hasColumn('messages', 'tenant_id')) { Schema::table('messages', function (Blueprint $table): void { - $table->foreignId('tenant_id')->nullable()->after('id')->constrained('tenants'); + $table->foreignUuid('tenant_id')->nullable()->after('id')->constrained('tenants'); }); } if (!Schema::hasColumn('voice_messages', 'tenant_id')) { Schema::table('voice_messages', function (Blueprint $table): void { - $table->foreignId('tenant_id')->nullable()->after('id')->constrained('tenants'); + $table->foreignUuid('tenant_id')->nullable()->after('id')->constrained('tenants'); }); } diff --git a/app-modules/activity/database/migrations/2026_04_17_000002_create_moderation_events_table.php b/app-modules/activity/database/migrations/2026_04_17_000002_create_moderation_events_table.php index e997bde3..86099760 100644 --- a/app-modules/activity/database/migrations/2026_04_17_000002_create_moderation_events_table.php +++ b/app-modules/activity/database/migrations/2026_04_17_000002_create_moderation_events_table.php @@ -12,7 +12,7 @@ public function up(): void { Schema::create('moderation_events', function (Blueprint $table): void { $table->uuid('id')->primary(); - $table->foreignId('tenant_id')->constrained('tenants'); + $table->foreignUuid('tenant_id')->constrained('tenants'); $table->foreignUuid('external_identity_id')->nullable()->constrained('external_identities'); $table->foreignUuid('moderator_identity_id')->nullable()->constrained('external_identities'); $table->string('type'); diff --git a/app-modules/activity/database/migrations/2026_04_17_000003_create_activity_reactions_table.php b/app-modules/activity/database/migrations/2026_04_17_000003_create_activity_reactions_table.php index 6617425d..e1469e67 100644 --- a/app-modules/activity/database/migrations/2026_04_17_000003_create_activity_reactions_table.php +++ b/app-modules/activity/database/migrations/2026_04_17_000003_create_activity_reactions_table.php @@ -12,7 +12,7 @@ public function up(): void { Schema::create('activity_reactions', function (Blueprint $table): void { $table->uuid('id')->primary(); - $table->foreignId('tenant_id')->constrained('tenants'); + $table->foreignUuid('tenant_id')->constrained('tenants'); $table->uuidMorphs('reactable'); $table->string('emoji_key', 128); diff --git a/app-modules/activity/database/migrations/2026_04_20_110053_create_message_mentions_and_threads.php b/app-modules/activity/database/migrations/2026_04_20_110053_create_message_mentions_and_threads.php index 03f752ff..8bdee176 100644 --- a/app-modules/activity/database/migrations/2026_04_20_110053_create_message_mentions_and_threads.php +++ b/app-modules/activity/database/migrations/2026_04_20_110053_create_message_mentions_and_threads.php @@ -12,7 +12,7 @@ public function up(): void { Schema::create('message_mentions', function (Blueprint $table): void { $table->uuid('id')->primary(); - $table->foreignId('tenant_id')->constrained('tenants'); + $table->foreignUuid('tenant_id')->constrained('tenants'); $table->foreignUuid('message_id')->constrained('messages')->cascadeOnDelete(); $table->foreignUuid('mentioned_identity_id')->nullable()->constrained('external_identities'); $table->string('mentioned_provider_account_id'); @@ -36,7 +36,7 @@ public function up(): void Schema::create('message_threads', function (Blueprint $table): void { $table->uuid('id')->primary(); - $table->foreignId('tenant_id')->constrained('tenants'); + $table->foreignUuid('tenant_id')->constrained('tenants'); $table->foreignUuid('message_id')->constrained('messages')->cascadeOnDelete(); $table->string('provider_thread_id'); $table->string('name')->nullable(); diff --git a/app-modules/activity/database/migrations/2026_04_20_111057_create_fase3_activity_tables.php b/app-modules/activity/database/migrations/2026_04_20_111057_create_fase3_activity_tables.php index 41f5b75e..19499269 100644 --- a/app-modules/activity/database/migrations/2026_04_20_111057_create_fase3_activity_tables.php +++ b/app-modules/activity/database/migrations/2026_04_20_111057_create_fase3_activity_tables.php @@ -13,7 +13,7 @@ public function up(): void { Schema::create('message_attachments', function (Blueprint $table): void { $table->uuid('id')->primary(); - $table->foreignId('tenant_id')->constrained('tenants'); + $table->foreignUuid('tenant_id')->constrained('tenants'); $table->foreignUuid('message_id')->constrained('messages')->cascadeOnDelete(); $table->string('provider_attachment_id')->nullable(); $table->text('url'); @@ -29,7 +29,7 @@ public function up(): void Schema::create('message_embeds', function (Blueprint $table): void { $table->uuid('id')->primary(); - $table->foreignId('tenant_id')->constrained('tenants'); + $table->foreignUuid('tenant_id')->constrained('tenants'); $table->foreignUuid('message_id')->constrained('messages')->cascadeOnDelete(); $table->text('url')->nullable(); $table->text('title')->nullable(); @@ -48,7 +48,7 @@ public function up(): void Schema::create('membership_events', function (Blueprint $table): void { $table->uuid('id')->primary(); - $table->foreignId('tenant_id')->constrained('tenants'); + $table->foreignUuid('tenant_id')->constrained('tenants'); $table->foreignUuid('external_identity_id')->constrained('external_identities'); $table->string('kind'); $table->timestamp('occurred_at'); diff --git a/app-modules/activity/database/migrations/2026_05_09_154113_create_activity_timeline_table.php b/app-modules/activity/database/migrations/2026_05_09_154113_create_activity_timeline_table.php index dc0542c3..85af46c8 100644 --- a/app-modules/activity/database/migrations/2026_05_09_154113_create_activity_timeline_table.php +++ b/app-modules/activity/database/migrations/2026_05_09_154113_create_activity_timeline_table.php @@ -2,8 +2,6 @@ declare(strict_types=1); -use He4rt\Identity\Tenant\Models\Tenant; -use He4rt\Identity\User\Models\User; use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; @@ -14,8 +12,8 @@ public function up(): void { Schema::create('activity_timeline', function (Blueprint $table): void { $table->uuid('id')->primary(); - $table->foreignIdFor(User::class, 'user_id'); - $table->foreignIdFor(Tenant::class, 'tenant_id'); + $table->foreignUuid('user_id'); + $table->foreignUuid('tenant_id'); $table->uuidMorphs('postable'); $table->foreignUuid('root_id')->nullable(); $table->foreignUuid('parent_id')->nullable(); diff --git a/app-modules/identity/database/migrations/2025_11_02_172528_create_tenants_table.php b/app-modules/identity/database/migrations/2025_11_02_172528_create_tenants_table.php index b30e7442..646dd32d 100644 --- a/app-modules/identity/database/migrations/2025_11_02_172528_create_tenants_table.php +++ b/app-modules/identity/database/migrations/2025_11_02_172528_create_tenants_table.php @@ -2,7 +2,6 @@ declare(strict_types=1); -use He4rt\Identity\User\Models\User; use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; @@ -12,10 +11,10 @@ public function up(): void { Schema::create('tenants', function (Blueprint $table): void { - $table->id(); + $table->uuid('id')->primary(); $table->string('name'); $table->string('slug'); - $table->foreignIdFor(User::class, 'owner_id')->constrained('users'); + $table->foreignUuid('owner_id')->constrained('users'); $table->boolean('active'); $table->timestamps(); $table->softDeletes(); diff --git a/app-modules/identity/database/migrations/2025_11_02_172618_set_up_tenant_module.php b/app-modules/identity/database/migrations/2025_11_02_172618_set_up_tenant_module.php index 50131957..253c2bc7 100644 --- a/app-modules/identity/database/migrations/2025_11_02_172618_set_up_tenant_module.php +++ b/app-modules/identity/database/migrations/2025_11_02_172618_set_up_tenant_module.php @@ -26,7 +26,7 @@ public function up(): void foreach ($tables as $table => $indexableColumns) { Schema::table($table, function (Blueprint $table) use ($indexableColumns): void { - $table->foreignId('tenant_id') + $table->foreignUuid('tenant_id') ->after('id') ->constrained('tenants') ->nullOnDelete(); @@ -36,12 +36,7 @@ public function up(): void } }); } - } - public function down(): void - { - // Don't listen to the haters - // Schema::dropIfExists('tenant'); - } + public function down(): void {} }; diff --git a/app-modules/identity/database/migrations/2025_11_08_161609_create_tenant_users_table.php b/app-modules/identity/database/migrations/2025_11_08_161609_create_tenant_users_table.php index 0c707ceb..7fa0dde1 100644 --- a/app-modules/identity/database/migrations/2025_11_08_161609_create_tenant_users_table.php +++ b/app-modules/identity/database/migrations/2025_11_08_161609_create_tenant_users_table.php @@ -14,7 +14,7 @@ public function up(): void { Schema::create('tenant_users', function (Blueprint $table): void { - $table->foreignId('tenant_id')->constrained('tenants')->cascadeOnDelete(); + $table->foreignUuid('tenant_id')->constrained('tenants')->cascadeOnDelete(); $table->foreignUuid('user_id')->constrained('users')->cascadeOnDelete(); $table->timestamps(); }); diff --git a/app-modules/integration-discord/database/migrations/2026_05_19_200000_create_discord_guilds_table.php b/app-modules/integration-discord/database/migrations/2026_05_19_200000_create_discord_guilds_table.php index d9716613..a2bfa070 100644 --- a/app-modules/integration-discord/database/migrations/2026_05_19_200000_create_discord_guilds_table.php +++ b/app-modules/integration-discord/database/migrations/2026_05_19_200000_create_discord_guilds_table.php @@ -15,7 +15,7 @@ public function up(): void { Schema::create('discord_guilds', function (Blueprint $table): void { $table->id(); - $table->foreignId('tenant_id')->nullable()->constrained('tenants')->nullOnDelete(); + $table->foreignUuid('tenant_id')->nullable()->constrained('tenants')->nullOnDelete(); $table->string('discord_guild_id')->unique(); $table->string('name'); $table->string('icon')->nullable(); diff --git a/app-modules/integration-twitch/database/migrations/2026_05_22_000001_create_twitch_subscriptions_table.php b/app-modules/integration-twitch/database/migrations/2026_05_22_000001_create_twitch_subscriptions_table.php index f97c0330..3cc876a1 100644 --- a/app-modules/integration-twitch/database/migrations/2026_05_22_000001_create_twitch_subscriptions_table.php +++ b/app-modules/integration-twitch/database/migrations/2026_05_22_000001_create_twitch_subscriptions_table.php @@ -21,7 +21,7 @@ public function up(): void $table->string('callback_url')->nullable(); $table->integer('cost')->default(0); $table->string('version')->default('1'); - $table->foreignId('tenant_id')->constrained()->cascadeOnDelete(); + $table->foreignUuid('tenant_id')->constrained()->cascadeOnDelete(); $table->timestamps(); $table->index('type'); diff --git a/app-modules/integration-twitch/database/migrations/2026_05_22_000002_add_tenant_id_to_twitch_event_logs_table.php b/app-modules/integration-twitch/database/migrations/2026_05_22_000002_add_tenant_id_to_twitch_event_logs_table.php index e02ec95c..07304e3a 100644 --- a/app-modules/integration-twitch/database/migrations/2026_05_22_000002_add_tenant_id_to_twitch_event_logs_table.php +++ b/app-modules/integration-twitch/database/migrations/2026_05_22_000002_add_tenant_id_to_twitch_event_logs_table.php @@ -11,7 +11,7 @@ public function up(): void { Schema::table('twitch_event_logs', function (Blueprint $table): void { - $table->foreignId('tenant_id')->nullable()->after('id')->constrained()->nullOnDelete(); + $table->foreignUuid('tenant_id')->nullable()->after('id')->constrained()->nullOnDelete(); }); } diff --git a/app-modules/moderation/database/migrations/2026_05_02_000001_create_moderation_cases_table.php b/app-modules/moderation/database/migrations/2026_05_02_000001_create_moderation_cases_table.php index 067c6db0..7aa2b87c 100644 --- a/app-modules/moderation/database/migrations/2026_05_02_000001_create_moderation_cases_table.php +++ b/app-modules/moderation/database/migrations/2026_05_02_000001_create_moderation_cases_table.php @@ -28,7 +28,7 @@ public function up(): void $table->timestampTz('assigned_at')->nullable(); $table->timestampTz('resolved_at')->nullable(); $table->foreignUuid('author_id')->nullable()->constrained('users')->nullOnDelete(); - $table->foreignId('tenant_id')->nullable()->constrained('tenants')->nullOnDelete(); + $table->foreignUuid('tenant_id')->nullable()->constrained('tenants')->nullOnDelete(); $table->timestampsTz(); $table->index(['tenant_id', 'status', 'priority', 'created_at'], 'idx_cases_queue'); diff --git a/app-modules/moderation/database/migrations/2026_05_02_000005_create_moderation_rules_table.php b/app-modules/moderation/database/migrations/2026_05_02_000005_create_moderation_rules_table.php index 58f7737a..9d13477d 100644 --- a/app-modules/moderation/database/migrations/2026_05_02_000005_create_moderation_rules_table.php +++ b/app-modules/moderation/database/migrations/2026_05_02_000005_create_moderation_rules_table.php @@ -20,7 +20,7 @@ public function up(): void $table->string('severity', 20); $table->string('action_on_match', 30); $table->boolean('is_active')->default(true); - $table->foreignId('tenant_id')->nullable()->constrained('tenants')->nullOnDelete(); + $table->foreignUuid('tenant_id')->nullable()->constrained('tenants')->nullOnDelete(); $table->timestampsTz(); $table->index(['is_active', 'tenant_id'], 'idx_rules_active'); diff --git a/app-modules/moderation/database/migrations/2026_05_02_000006_create_moderation_audit_log_table.php b/app-modules/moderation/database/migrations/2026_05_02_000006_create_moderation_audit_log_table.php index 615d7099..fec7a6dc 100644 --- a/app-modules/moderation/database/migrations/2026_05_02_000006_create_moderation_audit_log_table.php +++ b/app-modules/moderation/database/migrations/2026_05_02_000006_create_moderation_audit_log_table.php @@ -18,7 +18,7 @@ public function up(): void $table->uuid('case_id')->nullable(); $table->jsonb('details'); $table->string('platform', 20)->nullable(); - $table->unsignedBigInteger('tenant_id')->nullable(); + $table->uuid('tenant_id')->nullable(); $table->timestampTz('created_at')->useCurrent(); $table->index(['tenant_id', 'created_at'], 'idx_audit_tenant_date'); diff --git a/app-modules/moderation/database/migrations/2026_05_02_142344_add_tenant_id_to_moderation_actions_and_appeals.php b/app-modules/moderation/database/migrations/2026_05_02_142344_add_tenant_id_to_moderation_actions_and_appeals.php index b3244c1d..5d1035bb 100644 --- a/app-modules/moderation/database/migrations/2026_05_02_142344_add_tenant_id_to_moderation_actions_and_appeals.php +++ b/app-modules/moderation/database/migrations/2026_05_02_142344_add_tenant_id_to_moderation_actions_and_appeals.php @@ -12,11 +12,11 @@ public function up(): void { Schema::table('moderation_actions', function (Blueprint $table): void { - $table->foreignId('tenant_id')->nullable()->after('automated')->constrained('tenants')->nullOnDelete(); + $table->foreignUuid('tenant_id')->nullable()->after('automated')->constrained('tenants')->nullOnDelete(); }); Schema::table('moderation_appeals', function (Blueprint $table): void { - $table->foreignId('tenant_id')->nullable()->after('sla_deadline')->constrained('tenants')->nullOnDelete(); + $table->foreignUuid('tenant_id')->nullable()->after('sla_deadline')->constrained('tenants')->nullOnDelete(); }); DB::statement(' diff --git a/app-modules/profile/database/migrations/2026_05_21_000000_create_user_profiles_table.php b/app-modules/profile/database/migrations/2026_05_21_000000_create_user_profiles_table.php index 2474cb0b..3c511e24 100644 --- a/app-modules/profile/database/migrations/2026_05_21_000000_create_user_profiles_table.php +++ b/app-modules/profile/database/migrations/2026_05_21_000000_create_user_profiles_table.php @@ -14,7 +14,7 @@ public function up(): void Schema::create('user_profiles', function (Blueprint $table): void { $table->uuid('id')->primary(); $table->foreignUuid('user_id')->constrained('users')->cascadeOnDelete(); - $table->foreignId('tenant_id')->constrained('tenants')->cascadeOnDelete(); + $table->foreignUuid('tenant_id')->constrained('tenants')->cascadeOnDelete(); $table->string('nickname')->nullable(); $table->date('birthdate')->nullable(); $table->text('about')->nullable(); From ecbbbd64db90acc595cbfacd96d3cb262b3546b0 Mon Sep 17 00:00:00 2001 From: danielhe4rt Date: Tue, 26 May 2026 00:25:13 -0300 Subject: [PATCH 7/7] chore(migrations): remove data migration files already applied in production --- ...leanup_duplicate_messages_for_tenant_2.php | 31 ------ ...ore_tenant_id_indexes_and_foreign_keys.php | 100 ------------------ 2 files changed, 131 deletions(-) delete mode 100644 app-modules/activity/database/migrations/2026_04_18_000000_cleanup_duplicate_messages_for_tenant_2.php delete mode 100644 app-modules/identity/database/migrations/2026_05_26_020000_restore_tenant_id_indexes_and_foreign_keys.php diff --git a/app-modules/activity/database/migrations/2026_04_18_000000_cleanup_duplicate_messages_for_tenant_2.php b/app-modules/activity/database/migrations/2026_04_18_000000_cleanup_duplicate_messages_for_tenant_2.php deleted file mode 100644 index 07f150cc..00000000 --- a/app-modules/activity/database/migrations/2026_04_18_000000_cleanup_duplicate_messages_for_tenant_2.php +++ /dev/null @@ -1,31 +0,0 @@ - 1 - ) - '); - } - - public function down(): void {} -}; diff --git a/app-modules/identity/database/migrations/2026_05_26_020000_restore_tenant_id_indexes_and_foreign_keys.php b/app-modules/identity/database/migrations/2026_05_26_020000_restore_tenant_id_indexes_and_foreign_keys.php deleted file mode 100644 index bf6e92c6..00000000 --- a/app-modules/identity/database/migrations/2026_05_26_020000_restore_tenant_id_indexes_and_foreign_keys.php +++ /dev/null @@ -1,100 +0,0 @@ -