fix(bio): prevent duplicate slugs within same workspace
- Migration: add UNIQUE(workspace_id, slug) to bio_pages Old (subdomain_host, slug) unique allowed MySQL NULL bypass — two free-plan bios with NULL subdomain_host + same slug were not caught - Edit::save(): validate slug uniqueness before update, dispatch toast on conflict - Edit::save(): validate slug format (alphanumeric + dash/underscore, max 64) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>main
parent
deb5f310a1
commit
308778b99d
|
|
@ -43,10 +43,30 @@ class Edit extends Component
|
|||
{
|
||||
$page = BioPage::findOrFail($this->pageId);
|
||||
|
||||
$newSlug = $state['slug'] ?? $page->slug;
|
||||
|
||||
$slugTaken = BioPage::where('workspace_id', $page->workspace_id)
|
||||
->where('slug', $newSlug)
|
||||
->where('id', '!=', $page->id)
|
||||
->whereNull('deleted_at')
|
||||
->exists();
|
||||
|
||||
if ($slugTaken) {
|
||||
$this->dispatch('toast', message: 'Slug "' . $newSlug . '" ist bereits vergeben.', type: 'error');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (! preg_match('/^[a-zA-Z0-9_-]{1,64}$/', $newSlug)) {
|
||||
$this->dispatch('toast', message: 'Slug: nur Buchstaben, Zahlen, - und _. Max 64 Zeichen.', type: 'error');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$displayName = $state['profile']['displayName'] ?? '';
|
||||
|
||||
$page->update([
|
||||
'slug' => $state['slug'] ?? $page->slug,
|
||||
'slug' => $newSlug,
|
||||
'title' => ['en' => $displayName, 'de' => $displayName],
|
||||
'is_published' => false,
|
||||
'theme' => [
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('bio_pages', function (Blueprint $table) {
|
||||
// Slug must be unique within a workspace regardless of subdomain_host.
|
||||
// The old (subdomain_host, slug) unique allowed MySQL NULL bypass —
|
||||
// two rows with NULL subdomain_host + same slug were not caught.
|
||||
$table->unique(['workspace_id', 'slug'], 'bio_pages_workspace_slug_unique');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('bio_pages', function (Blueprint $table) {
|
||||
$table->dropUnique('bio_pages_workspace_slug_unique');
|
||||
});
|
||||
}
|
||||
};
|
||||
Loading…
Reference in New Issue