diff --git a/database/seeders/WordFilterSeeder.php b/database/seeders/WordFilterSeeder.php index d781a16e6..fae88fab0 100644 --- a/database/seeders/WordFilterSeeder.php +++ b/database/seeders/WordFilterSeeder.php @@ -2,6 +2,7 @@ namespace Database\Seeders; +use App\Models\WordFilter; use Illuminate\Database\Seeder; class WordFilterSeeder extends Seeder @@ -11,6 +12,363 @@ class WordFilterSeeder extends Seeder */ public function run(): void { - // + // This seeder adds filters without removing existing ones + // Demo filters use updateOrCreate to avoid duplicates + + // First, create some specific examples for demonstration + $this->createDemoFilters(); + + // Then, generate random filters using the factory + $this->createRandomFilters(); + } + + /** + * Create specific demo filters with realistic examples. + */ + protected function createDemoFilters(): void + { + // Replace type filters - obscure profanity + WordFilter::updateOrCreate( + ['pattern' => 'badword'], + [ + 'replacement' => '******', + 'filter_type' => 'replace', + 'pattern_type' => 'exact', + 'severity' => 'high', + 'is_active' => true, + 'case_sensitive' => false, + 'applies_to' => ['posts', 'private_messages', 'signatures'], + 'notes' => 'Common profanity - exact match', + ] + ); + + WordFilter::updateOrCreate( + ['pattern' => 'damn'], + [ + 'replacement' => 'd***', + 'filter_type' => 'replace', + 'pattern_type' => 'exact', + 'severity' => 'low', + 'is_active' => true, + 'case_sensitive' => false, + 'applies_to' => ['posts'], + 'notes' => 'Mild profanity', + ] + ); + + // Wildcard patterns + WordFilter::updateOrCreate( + ['pattern' => '*spam*'], + [ + 'replacement' => '[removed]', + 'filter_type' => 'replace', + 'pattern_type' => 'wildcard', + 'severity' => 'medium', + 'is_active' => true, + 'case_sensitive' => false, + 'applies_to' => ['posts', 'private_messages'], + 'notes' => 'Spam-related content', + ] + ); + + WordFilter::updateOrCreate( + ['pattern' => 'hate*'], + [ + 'replacement' => '****', + 'filter_type' => 'replace', + 'pattern_type' => 'wildcard', + 'severity' => 'high', + 'is_active' => true, + 'case_sensitive' => false, + 'applies_to' => ['posts', 'private_messages', 'usernames'], + 'notes' => 'Hate speech patterns', + ] + ); + + // Regex patterns + WordFilter::updateOrCreate( + ['pattern' => '/\\b(viagra|cialis|levitra)\\b/i'], + [ + 'replacement' => '[pharmaceutical]', + 'filter_type' => 'replace', + 'pattern_type' => 'regex', + 'severity' => 'medium', + 'is_active' => true, + 'case_sensitive' => false, + 'applies_to' => ['posts', 'private_messages', 'signatures'], + 'notes' => 'Pharmaceutical spam', + ] + ); + + WordFilter::updateOrCreate( + ['pattern' => '/\\b\\d{3}-\\d{3}-\\d{4}\\b/'], + [ + 'replacement' => '[phone number]', + 'filter_type' => 'replace', + 'pattern_type' => 'regex', + 'severity' => 'low', + 'is_active' => true, + 'case_sensitive' => false, + 'applies_to' => ['posts', 'private_messages'], + 'notes' => 'Phone number pattern (US format)', + ] + ); + + // Block type filters - completely prevent posting + WordFilter::updateOrCreate( + ['pattern' => 'blocked_domain.com'], + [ + 'replacement' => null, + 'filter_type' => 'block', + 'pattern_type' => 'exact', + 'severity' => 'high', + 'is_active' => true, + 'case_sensitive' => false, + 'applies_to' => ['posts', 'private_messages', 'signatures'], + 'notes' => 'Known malicious domain', + ] + ); + + WordFilter::updateOrCreate( + ['pattern' => '*malware*'], + [ + 'replacement' => null, + 'filter_type' => 'block', + 'pattern_type' => 'wildcard', + 'severity' => 'high', + 'is_active' => true, + 'case_sensitive' => false, + 'applies_to' => ['posts', 'private_messages'], + 'notes' => 'Malware-related content', + ] + ); + + WordFilter::updateOrCreate( + ['pattern' => '/\\b(torrent|magnet:|ed2k:)\\/\\/\\b/i'], + [ + 'replacement' => null, + 'filter_type' => 'block', + 'pattern_type' => 'regex', + 'severity' => 'medium', + 'is_active' => false, // Disabled by default for torrent sites + 'case_sensitive' => false, + 'applies_to' => ['posts'], + 'notes' => 'Torrent/P2P links - disabled for torrent trackers', + ] + ); + + // Moderate type filters - flag for review + WordFilter::updateOrCreate( + ['pattern' => 'suspicious'], + [ + 'replacement' => null, + 'filter_type' => 'moderate', + 'pattern_type' => 'exact', + 'severity' => 'low', + 'is_active' => true, + 'case_sensitive' => false, + 'applies_to' => ['posts'], + 'notes' => 'Flag for manual review', + ] + ); + + WordFilter::updateOrCreate( + ['pattern' => '*scam*'], + [ + 'replacement' => null, + 'filter_type' => 'moderate', + 'pattern_type' => 'wildcard', + 'severity' => 'medium', + 'is_active' => true, + 'case_sensitive' => false, + 'applies_to' => ['posts', 'private_messages'], + 'notes' => 'Potential scam content', + ] + ); + + WordFilter::updateOrCreate( + ['pattern' => '/\\$\\d{3,}/i'], + [ + 'replacement' => null, + 'filter_type' => 'moderate', + 'pattern_type' => 'regex', + 'severity' => 'low', + 'is_active' => true, + 'case_sensitive' => false, + 'applies_to' => ['posts', 'private_messages'], + 'notes' => 'Large dollar amounts - potential scam', + ] + ); + + // Email masking + WordFilter::updateOrCreate( + ['pattern' => '/([a-zA-Z0-9._%+-]+)@([a-zA-Z0-9.-]+\\.[a-zA-Z]{2,})/i'], + [ + 'replacement' => '[email protected]', + 'filter_type' => 'replace', + 'pattern_type' => 'regex', + 'severity' => 'low', + 'is_active' => true, + 'case_sensitive' => false, + 'applies_to' => ['posts'], + 'notes' => 'Email address masking for privacy', + ] + ); + + // URL shortener blocking + WordFilter::updateOrCreate( + ['pattern' => '/\\b(bit\\.ly|tinyurl\\.com|goo\\.gl|t\\.co)\\/\\w+/i'], + [ + 'replacement' => '[shortened URL]', + 'filter_type' => 'replace', + 'pattern_type' => 'regex', + 'severity' => 'medium', + 'is_active' => true, + 'case_sensitive' => false, + 'applies_to' => ['posts', 'private_messages'], + 'notes' => 'URL shorteners - potential security risk', + ] + ); + + // Inappropriate username filter + WordFilter::updateOrCreate( + ['pattern' => 'admin'], + [ + 'replacement' => null, + 'filter_type' => 'block', + 'pattern_type' => 'exact', + 'severity' => 'high', + 'is_active' => true, + 'case_sensitive' => false, + 'applies_to' => ['usernames'], + 'notes' => 'Prevent impersonation of administrators', + ] + ); + + WordFilter::updateOrCreate( + ['pattern' => '*moderator*'], + [ + 'replacement' => null, + 'filter_type' => 'block', + 'pattern_type' => 'wildcard', + 'severity' => 'high', + 'is_active' => true, + 'case_sensitive' => false, + 'applies_to' => ['usernames'], + 'notes' => 'Prevent impersonation of moderators', + ] + ); + + // Case-sensitive filter example + WordFilter::updateOrCreate( + ['pattern' => 'CEO'], + [ + 'replacement' => '[title]', + 'filter_type' => 'replace', + 'pattern_type' => 'exact', + 'severity' => 'low', + 'is_active' => true, + 'case_sensitive' => true, + 'applies_to' => ['posts'], + 'notes' => 'Replace CEO when in all caps only', + ] + ); + + // Multiple content type example + WordFilter::updateOrCreate( + ['pattern' => 'test123'], + [ + 'replacement' => '[test]', + 'filter_type' => 'replace', + 'pattern_type' => 'exact', + 'severity' => 'low', + 'is_active' => false, // Inactive example + 'case_sensitive' => false, + 'applies_to' => ['posts', 'private_messages', 'usernames', 'signatures', 'profile_fields'], + 'notes' => 'Test filter - currently disabled', + ] + ); + } + + /** + * Create random filters using the factory. + */ + protected function createRandomFilters(): void + { + // Create 10 random replace filters + WordFilter::factory() + ->count(10) + ->create([ + 'filter_type' => 'replace', + ]); + + // Create 5 random block filters + WordFilter::factory() + ->count(5) + ->create([ + 'filter_type' => 'block', + 'replacement' => null, + ]); + + // Create 5 random moderate filters + WordFilter::factory() + ->count(5) + ->create([ + 'filter_type' => 'moderate', + 'replacement' => null, + ]); + + // Create some high severity filters + WordFilter::factory() + ->count(5) + ->create([ + 'severity' => 'high', + 'is_active' => true, + ]); + + // Create some regex pattern filters + WordFilter::factory() + ->count(5) + ->create([ + 'pattern_type' => 'regex', + 'pattern' => $this->generateRandomRegexPattern(), + ]); + + // Create some wildcard filters for spam detection + WordFilter::factory() + ->count(5) + ->create([ + 'pattern_type' => 'wildcard', + 'pattern' => '*' . fake()->randomElement(['buy', 'cheap', 'free', 'click', 'download']) . '*', + 'filter_type' => 'replace', + 'replacement' => '[SPAM]', + 'applies_to' => ['posts', 'private_messages'], + ]); + + // Create inactive filters for testing + WordFilter::factory() + ->count(5) + ->create([ + 'is_active' => false, + ]); + } + + /** + * Generate a random regex pattern for testing. + */ + protected function generateRandomRegexPattern(): string + { + $patterns = [ + '/\\b\\d{3}-\\d{2}-\\d{4}\\b/', // SSN pattern + '/\\b[A-Z]{2}\\d{6}\\b/', // License plate pattern + '/\\b(https?:\\/\\/)?[\\w\\-]+(\\.[\\w\\-]+)+[/#?]?.*$/', // URL pattern + '/\\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,}\\b/i', // Email pattern + '/\\b\\d{4}[\\s-]?\\d{4}[\\s-]?\\d{4}[\\s-]?\\d{4}\\b/', // Credit card pattern + '/\\b(\\+?1[\\s-]?)?\\(?\\d{3}\\)?[\\s-]?\\d{3}[\\s-]?\\d{4}\\b/', // Phone pattern + '/\\$\\d+(\\.\\d{2})?\\b/', // Currency pattern + '/\\b[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}\\b/i', // UUID pattern + ]; + + return fake()->randomElement($patterns); } } diff --git a/docs/docs/api/emojis/aliases.md b/docs/docs/api/emojis/aliases.md index 1723061fc..c1a512b9a 100644 --- a/docs/docs/api/emojis/aliases.md +++ b/docs/docs/api/emojis/aliases.md @@ -1,5 +1,5 @@ --- -title: Emoji Aliases API +title: Emoji Aliases sidebar_position: 2 --- @@ -9,7 +9,7 @@ The Emoji Aliases API allows you to manage alternative shortcodes for emojis, en ## Base URL -```http +```text /api/emoji/aliases ``` @@ -19,7 +19,7 @@ The Emoji Aliases API allows you to manage alternative shortcodes for emojis, en Get a paginated list of emoji aliases with optional filtering and relationship loading. -```http +```text GET /api/emoji/aliases ``` @@ -124,7 +124,7 @@ curl -X GET "https://api.example.com/api/emoji/aliases?emoji_id=1&with_emoji=1" Search emoji aliases using Laravel Scout for full-text search capabilities. -```http +```text GET /api/emoji/aliases/search ``` @@ -187,7 +187,7 @@ curl -X GET "https://api.example.com/api/emoji/aliases/search?q=hap&limit=5&with Get a specific emoji alias by ID with full relationship data. -```http +```text GET /api/emoji/aliases/{id} ``` @@ -246,7 +246,7 @@ curl -X GET "https://api.example.com/api/emoji/aliases/1" \ Create a new alias for an existing emoji. -```http +```text POST /api/emoji/aliases ``` @@ -289,7 +289,7 @@ curl -X POST "https://api.example.com/api/emoji/aliases" \ Update an existing emoji alias. -```http +```text PUT /api/emoji/aliases/{id} PATCH /api/emoji/aliases/{id} ``` @@ -338,7 +338,7 @@ curl -X PATCH "https://api.example.com/api/emoji/aliases/3" \ Delete an emoji alias. The associated emoji remains unchanged. -```http +```text DELETE /api/emoji/aliases/{id} ``` diff --git a/docs/docs/api/emojis/categories.md b/docs/docs/api/emojis/categories.md index 268122a95..2b824a2c3 100644 --- a/docs/docs/api/emojis/categories.md +++ b/docs/docs/api/emojis/categories.md @@ -1,5 +1,5 @@ --- -title: Emoji Categories API +title: Emoji Categories sidebar_position: 3 --- @@ -9,7 +9,7 @@ The Emoji Categories API allows you to manage emoji categories used to organize ## Base URL -```http +```text /api/emoji/categories ``` @@ -19,7 +19,7 @@ The Emoji Categories API allows you to manage emoji categories used to organize Get a list of all emoji categories. -```http +```text GET /api/emoji/categories ``` @@ -68,7 +68,7 @@ curl -X GET "https://api.example.com/api/emoji/categories?with_emojis=1" \ Get a specific emoji category by ID. -```http +```text GET /api/emoji/categories/{id} ``` @@ -133,7 +133,7 @@ curl -X GET "https://api.example.com/api/emoji/categories/1?with_aliases=1" \ Create a new emoji category. -```http +```text POST /api/emoji/categories ``` @@ -178,7 +178,7 @@ curl -X POST "https://api.example.com/api/emoji/categories" \ Update an existing emoji category. -```http +```text PUT /api/emoji/categories/{id} PATCH /api/emoji/categories/{id} ``` @@ -229,7 +229,7 @@ curl -X PATCH "https://api.example.com/api/emoji/categories/3" \ Delete an emoji category. Associated emojis will have their category_id set to null. -```http +```text DELETE /api/emoji/categories/{id} ``` diff --git a/docs/docs/api/emojis/emojis.md b/docs/docs/api/emojis/emojis.md index 3350aae1c..888361dd5 100644 --- a/docs/docs/api/emojis/emojis.md +++ b/docs/docs/api/emojis/emojis.md @@ -1,5 +1,5 @@ --- -title: Emoji API +title: Emoji sidebar_position: 1 --- @@ -9,7 +9,7 @@ The Emoji API allows you to manage individual emojis, including Unicode emojis, ## Base URL -```http +```text /api/emoji/emojis ``` @@ -19,7 +19,7 @@ The Emoji API allows you to manage individual emojis, including Unicode emojis, Get a paginated list of emojis with optional filtering and relationship loading. -```http +```text GET /api/emoji/emojis ``` @@ -95,7 +95,7 @@ curl -X GET "https://api.example.com/api/emoji/emojis?category_id=1&with_aliases Search emojis using Laravel Scout for full-text search capabilities. -```http +```text GET /api/emoji/emojis/search ``` @@ -151,7 +151,7 @@ curl -X GET "https://api.example.com/api/emoji/emojis/search?q=smile&limit=10&wi Get a specific emoji by ID with full relationship data. -```http +```text GET /api/emoji/emojis/{id} ``` @@ -212,7 +212,7 @@ curl -X GET "https://api.example.com/api/emoji/emojis/1" \ Create a new emoji. Supports Unicode emojis, custom images, and CSS sprites. -```http +```text POST /api/emoji/emojis ``` @@ -316,7 +316,7 @@ curl -X POST "https://api.example.com/api/emoji/emojis" \ Update an existing emoji. -```http +```text PUT /api/emoji/emojis/{id} PATCH /api/emoji/emojis/{id} ``` @@ -371,7 +371,7 @@ curl -X PATCH "https://api.example.com/api/emoji/emojis/25" \ Delete an emoji. Associated aliases will be automatically deleted. -```http +```text DELETE /api/emoji/emojis/{id} ``` diff --git a/docs/docs/api/overview.md b/docs/docs/api/overview.md index 13f02fba8..a8a9ee959 100644 --- a/docs/docs/api/overview.md +++ b/docs/docs/api/overview.md @@ -11,7 +11,7 @@ Welcome to the API documentation. This section covers all available REST API end All API endpoints are prefixed with `/api/` and use the following base URL: -```http +```text https://your-domain.com/api/ ``` @@ -130,7 +130,7 @@ List endpoints support pagination using query parameters: Example: -```http +```text GET /api/emoji/emojis?page=2&per_page=25 ``` @@ -153,7 +153,7 @@ Use these parameters to include related data: Example: -```http +```text GET /api/emoji/emojis?search=smile&with_category=1&with_aliases=1 ``` @@ -161,7 +161,7 @@ GET /api/emoji/emojis?search=smile&with_category=1&with_aliases=1 The current API version is v1. Future versions will be available at: -```http +```text /api/v2/endpoint ``` diff --git a/docs/docs/api/word-filters/word-filters.md b/docs/docs/api/word-filters/word-filters.md index f538d1334..6115fb02f 100644 --- a/docs/docs/api/word-filters/word-filters.md +++ b/docs/docs/api/word-filters/word-filters.md @@ -1,3 +1,8 @@ +--- +title: Word Filters +sidebar_position: 2 +--- + # Word Filters API The Word Filters API allows you to manage content filtering rules for your forum. Word filters can automatically replace, block, or moderate content based on configurable patterns. diff --git a/tests/Feature/Api/EmojiAliasTest.php b/tests/Feature/Api/EmojiAliasTest.php index 31aff54a1..4c8ec0ce1 100644 --- a/tests/Feature/Api/EmojiAliasTest.php +++ b/tests/Feature/Api/EmojiAliasTest.php @@ -4,10 +4,6 @@ use App\Models\Emoji; use App\Models\EmojiAlias; use App\Models\EmojiCategory; -beforeEach(function () { - config(['scout.driver' => 'collection']); -}); - describe('Emoji Alias API Endpoints', function () { test('can list aliases', function () { $category = EmojiCategory::factory()->create(); @@ -300,7 +296,7 @@ describe('Emoji Alias Search API', function () { 'alias' => ':sad:', ]); - $response = $this->getJson('/api/emoji/aliases/search?q=happy'); + $response = $this->getJson('/api/emoji/aliases/search?q=happy&with_emoji=1'); $response->assertOk() ->assertJsonStructure([ diff --git a/tests/Feature/Api/EmojiTest.php b/tests/Feature/Api/EmojiTest.php index 49b82f3b0..739a28bcb 100644 --- a/tests/Feature/Api/EmojiTest.php +++ b/tests/Feature/Api/EmojiTest.php @@ -4,10 +4,6 @@ use App\Models\Emoji; use App\Models\EmojiAlias; use App\Models\EmojiCategory; -beforeEach(function () { - config(['scout.driver' => 'collection']); -}); - describe('Emoji API Endpoints', function () { test('can list emojis', function () { $category = EmojiCategory::factory()->create(); diff --git a/tests/Feature/Api/WordFilterTest.php b/tests/Feature/Api/WordFilterTest.php index bf577d19c..e72672368 100644 --- a/tests/Feature/Api/WordFilterTest.php +++ b/tests/Feature/Api/WordFilterTest.php @@ -6,10 +6,6 @@ use Illuminate\Foundation\Testing\RefreshDatabase; uses(RefreshDatabase::class); -beforeEach(function () { - config(['scout.driver' => 'collection']); -}); - describe('Word Filter API', function () { describe('Index', function () { test('can list word filters', function () {