Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file added .ai/mcp/mcp.json
Empty file.
94 changes: 0 additions & 94 deletions app/Console/Commands/AuditMorphColumns.php

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Illuminate\Database\Migrations\Migration;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;

return new class extends Migration
{
Expand Down Expand Up @@ -37,14 +38,35 @@ public function up(): void
->where('external_identity_id', $dup->old_ei_id)
->update(['external_identity_id' => $dup->new_ei_id]);

$oldXp = DB::table('characters')
$oldCharacter = DB::table('characters')
->where('user_id', $dup->old_user_id)
->value('experience') ?? 0;
->select(['experience', 'tenant_id'])
->first();

$oldXp = $oldCharacter->experience ?? 0;

if ($oldXp > 0) {
DB::table('characters')
$existingCharacter = DB::table('characters')
->where('user_id', $dup->new_user_id)
->increment('experience', $oldXp);
->exists();

if ($existingCharacter) {
DB::table('characters')
->where('user_id', $dup->new_user_id)
->increment('experience', $oldXp);
} else {
logger()->warning(sprintf('Migration: Creating missing character for user %s with %s XP from old user %s', $dup->new_user_id, $oldXp, $dup->old_user_id));

DB::table('characters')->insert([
'id' => Str::uuid()->toString(),
'user_id' => $dup->new_user_id,
'tenant_id' => $oldCharacter->tenant_id,
'experience' => $oldXp,
'reputation' => 0,
'created_at' => now(),
'updated_at' => now(),
]);
}
}

DB::table('external_identities')
Expand Down
126 changes: 126 additions & 0 deletions database/migrations/2026_03_22_000000_normalize_github_urls.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
<?php

declare(strict_types=1);

use Illuminate\Database\Migrations\Migration;
use Illuminate\Support\Facades\DB;

return new class extends Migration
{
private const array JUNK_KEYWORDS = [
'não', 'nao', 'tenho', 'possuo', 'ainda', 'lembro',
'sem ', 'sem.', 'nenhum', 'none', 'nope', 'null', 'undefined',
'n/a', 'n/d', 'cancelar', 'continuar', 'ignore', 'exit', 'pular',
'desconhecido', 'indisponivel', 'indisponível',
'privado', 'private', 'secret', 'segredo', 'prefiro',
'depois', 'esqueci', 'preguiça', 'sorry', 'desculpa',
'iniciante', 'construção', 'manutenção',
'não sei', 'nao sei', 'no momento', 'por enquanto',
'vou criar', 'vou fazer', 'vou ficar',
'tenho q', 'fiz ainda', 'criei ainda',
'sei nem', 'sei pra',
'não uso', 'nao uso', 'não utilizo',
'estou sem', 'fodase', 'fica pra',
];

private const array JUNK_EXACT = [
'.', '..', '...', '(...)', '-', '--', '-x-',
'a', 'x', 'xx', 'xxxx', 'd', 'dd', 'i', 'n', 'nn', 's', 't',
'no', 'non', 'nop', 'nt', 'we', 'af', 'sa', 'lk',
'0', '00', '1', '?', '??', ';', '_', '~',
'git', 'github', 'github.com', 'sem', 'nada', 'keine',
'oi', 'dope', 'scarlet', 'eew', 'cat.jsx',
'⛔', '🐰', '🥸', '—', '…',
];

public function up(): void
{
$rows = DB::table('user_information')
->whereNotNull('github_url')
->where('github_url', '!=', '')
->whereRaw("NOT (github_url LIKE 'https://github.com/%' AND LENGTH(github_url) > 19)")
->get(['id', 'github_url']);

foreach ($rows as $row) {
$lower = mb_strtolower(mb_trim($row->github_url));

if ($this->isJunk($lower)) {
DB::table('user_information')->where('id', $row->id)->update(['github_url' => null]);

continue;
}

$fixed = $this->tryFixUrl($lower);

if ($fixed !== null) {
DB::table('user_information')->where('id', $row->id)->update(['github_url' => $fixed]);

continue;
}

// Non-github links, gifs, random URLs — null them out
DB::table('user_information')->where('id', $row->id)->update(['github_url' => null]);
}
}

public function down(): void
{
// Irreversible: original junk values are not preserved.
}

private function isJunk(string $lower): bool
{
if (in_array($lower, self::JUNK_EXACT, true)) {
return true;
}

foreach (self::JUNK_KEYWORDS as $keyword) {
if (str_contains($lower, $keyword)) {
return true;
}
}

return false;
}

private function tryFixUrl(string $lower): ?string
{
if (preg_match('#^github\.com/[\w.\-]+#', $lower)) {
return 'https://'.$lower;
}

if (preg_match('#^(https?://)?www\.github\.com/([\w.\-]+)#i', $lower, $m)) {
return 'https://github.com/'.$m[2];
}

if (preg_match('#^https?://git[Hh]ub\.com/([\w.\-]+)#', $lower, $m)) {
return 'https://github.com/'.$m[1];
}

if (preg_match('#^https?//github\.com/([\w.\-]+)#', $lower, $m)) {
return 'https://github.com/'.$m[1];
}

if (preg_match('#^https?;//github\.com/([\w.\-]+)#', $lower, $m)) {
return 'https://github.com/'.$m[1];
}

if (preg_match('#^https?:+//github\.com/([\w.\-]+)#', $lower, $m)) {
return 'https://github.com/'.$m[1];
}

if (preg_match('#^h\w{3,6}s?[:/]+/*github\.com/([\w.\-]+)#', $lower, $m)) {
return 'https://github.com/'.$m[1];
}

if (preg_match('#^https?://(?:gi[th]*ub|gu[th]*ub|githubm)\.com/([\w.\-]+)#', $lower, $m)) {
return 'https://github.com/'.$m[1];
}

if (preg_match('#^https?://github,com/([\w.\-]+)#', $lower, $m)) {
return 'https://github.com/'.$m[1];
}

return null;
}
};
Loading