Architecture

CMS Page Structure

Content editors create pages in Filament Admin (/admin ) using the Fabricator Page Builder. Pages are stored in the pages` ` table with a blocks JSON column. Fabricator automatically routes pages based on their slugs—for example, the slug de/faq` ` is routed to /de/faq .

Pages support hierarchical parent-child relationships viaparent_id (self-referential foreign key with cascade delete).

Existing Page Blocks

Block Class Fields
rich-text
RichTextBlock
heading
,content
image
ImageBlock
image
,alt
,caption
,align
  • RichTextBlock: Heading (optional), Content (RichEditor with file attachments). Output is filtered by an HTML sanitizer (Symfony HtmlSanitizer) with a strict allowlist — scripts, event handlers, anddata: URIs are removed; links automatically receiverel="noopener noreferrer" .
  • ImageBlock: Image (JPEG/PNG/GIF/WEBP, max 10 MB), alt text, caption (optional), alignment (left/center/right) with XSS validation.

Add a new page block

  1. Create a class inapp/Filament/Fabricator/PageBlocks/ (extendsPageBlock )

  2. Define form fields ingetBlockSchema()

  3. Optionally overridemutateData() for post-processing

  4. Create a Blade view inresources/views/components/filament-fabricator/page-blocks/

  5. Fabricator automatically registers the block

Multilingual Support

  • URL-based routing: Pages with/de or/en prefix
  • Client-side locale detection in the Blade layout: Extracts language from URL segment
  • Dynamic language switch button with label dictionaries (18+ keys per language)
  • Seeder: Creates parallel de/en page trees with localized content (~15 pages per language)
  • Note: No Laravel translation files — labels are currently hardcoded in the Blade template

Registration Counter

API endpoint at/api/regcount (rate-limited: 30 requests/min), which retrieves the registration count from the external SpringFurCon registration page:

  • Primary: Regex pattern matching on “Reg-Counter”
  • Fallback: DOM-based XPath search
  • Caching: 5 minutes with distributed lock (Thundering Herd protection)
  • Graceful Degradation: In case of error, the last cached value is returned
  • Pages integrate the counter via the<span data-regcount> placeholder

Crawl Route

GET /crawl/{path?} serves static HTML files from acrawl/ directory:

  • Path traversal protection withrealpath() and prefix validation
  • Directories are automatically resolved toindex.html

-Cache-Control: no-cache, no-store, must-revalidate

  • Purpose: Hosting of externally crawled/cached content (images, static pages from an external CMS)

Custom Artisan Commands

  • **admin:create **: Creates or promotes users to admin
    • Email-based search
    • Interactive password prompt with auto-generation (16 characters)
    • Validation: Email format, min. 8-character password
    • Usesis_admin Boolean flag

Routing

-GET / — Redirect to/de

-GET /crawl/{path?} — Static HTML fromcrawl/

-GET /api/regcount — Registration counter API

  • All other pages via Fabricator slug routing
  • Admin panel at/admin (Filament, session auth)

Directory structure

app/
├── Console/Commands/AdminCreateCommand.php     Admin-Erstellung
├── Filament/Fabricator/PageBlocks/             Block-Definitionen mit Sanitizer
├── Filament/Fabricator/Layouts/                Layout-Definitionen
├── Http/Controllers/RegcountController.php     Registrierungs-Counter API
└── Providers/Filament/AdminPanelProvider.php   Filament-Konfiguration (Emerald Theme)
resources/views/
├── components/filament-fabricator/             Blade-Views der Blocks + Layouts
└── *.blade.php                                Statische Templates
database/
├── migrations/                                Users, Pages, Cache, Jobs
└── seeders/FabricatorPageSeeder.php           Bilingualer Content (~1600 Zeilen)

Uploads

Uploaded files are located atstorage/app/public/ :

-page-images/ — Images from ImageBlock -page-attachments/ — File attachments from RichTextBlock

The storage symlink (php artisan storage:link ) must be created before the first upload.

Tests

11 test files:

  • RichTextBlockTest (13 tests): HTML sanitization, XSS prevention, allowed tags
  • ImageBlockTest (6 tests): Alignment validation, data mutation
  • ImageBlockRenderTest (6 tests): Page rendering with images, captions, alignment classes
  • RegcountExtractionTest (8 tests): Regex and DOM extraction, edge cases

Test configuration: SQLite:memory: , array cache, sync queue.