# Website Setup This is an opinionated website setup. It's irrelevant to both Lucent and Laravel. It is just the way I like to organize everything. ## Summary - Create a Lucent folder with Schemas, Image Filters and Pages - Pages are similar to controllers, but will also get used to statically generate the website - A Context singleton class that acts as a container for global state - A middleware to initiate the Context class and set the application state for the request - Some helper functions to make our life easier ## Context Class ```php Homepage::class, ]; public function __construct( public Application $app, public Query $query ){} public function render(string $pageName, ...$args): string { $page = $this->app->make($this->pages[$pageName]); return $page->render(...$args); } public function getTitle() { return empty($this->title) ? $this->website : $this->title . " | " . $this->website; } public function getFullUrl(string $path = "") { return config("app.url") . $this->generateLocaleUrl($path, $this->locale); } public function localeUrl(string $path = ""): string { return $this->generateLocaleUrl($path, $this->locale); } public function switchLocale(string $locale, $path = ""): string { return $this->generateLocaleUrl($path, $locale); } private function generateLocaleUrl(string $path, string $locale): string { if ($this->mode == "preview") { return config("app.url") . "/preview/" . $locale . "/" . trim($path, "/"); } return config("app.url") . "/" . $locale . "/" . trim($path, "/"); } } ``` Add this class to `AppServiceProvider` as a singleton ```php $this->app->singleton(Context::class); ``` ## Context Middleware ```php context->locale = $request->route("locale"); App::setLocale($this->meta->locale); $this->context->mode = $mode; $this->context->loadData(); return $next($request); } } ``` Add the middleware inside the HTTP Kernel file. ## Configuration After publishing the config (`php artisan vendor:publish --tag=lucent-config`), configure `config/lucent.php` via `.env`: | Key | Env var | Default | Description | |---|---|---|---| | `env` | `LUCENT_ENV` | `production` | `production` or `development` | | `auth` | `LUCENT_AUTH` | `lucent` | Auth driver: `lucent` or `lunar` | | `disk` | `LUCENT_DISK` | `public` | Laravel filesystem disk for uploads | | `schemas_path` | `LUCENT_SCHEMAS_PATH` | `resources/lucent/schemas` | Where schema JSON files live | | `database` | `LUCENT_DB_CONNECTION` | `DB_CONNECTION` | Database connection name | | `name` | `LUCENT_NAME` | `Lucent` | CMS display name | | `url` | `LUCENT_URL` | `APP_URL` | Base URL of the CMS | | `previewTarget` | `LUCENT_PREVIEW_TARGET` | `previewTarget` | Preview route parameter | | `commands` | — | `[]` | Artisan commands exposed to admin users (see [Static Generator](Static%20Generator.md)) | | `imageFilters` | — | `[]` | Image filter presets applied to uploads (see below) | | `canInvite` | — | `["admin"]` | Roles that can invite new users | | `canBuild` | — | `["admin"]` | Roles that can trigger static builds | | `systemUserId` | — | `""` | User ID used for console-initiated record writes | ## Image Filters Image filters are Intervention Image filter classes applied to every uploaded image, producing additional versions (e.g. a cropped or watermarked variant). Each preset is stored at `templates/{name}/{original-path}` on the configured disk. Define filters in `config/lucent.php`: ```php "imageFilters" => [ "hero" => \App\ImageFilters\HeroFilter::class, "thumb" => \App\ImageFilters\ThumbFilter::class, ], ``` A filter class must implement `Intervention\Image\Filters\FilterInterface`: ```php namespace App\ImageFilters; use Intervention\Image\Filters\FilterInterface; use Intervention\Image\Image; class HeroFilter implements FilterInterface { public function applyFilter(Image $image): Image { return $image->fit(1200, 600)->encode('webp', 80); } } ``` Every uploaded image automatically gets a 300×300 WebP thumbnail at `thumbs/{path}` in addition to any configured presets. ## Authentication Lucent uses **email-link (magic link) login** — no passwords. Users receive a time-limited link by email. ### Auth modes Set `LUCENT_AUTH` to choose how users are stored: | Value | Description | |---|---| | `lucent` | Users stored in the `lucent_users` table. Supports roles. | | `lunar` | Delegates to Lunar's `lunar_staff` table. Roles not supported. | ### Login flow 1. User submits their email at `/lucent/login` 2. A 32-character token is stored against the user with a timestamp 3. Lucent emails a login link containing the token 4. User clicks the link → token is validated (must be used within **1 hour**) 5. Session is established; token is cleared ### First-time setup The `/lucent/register` route is only available when **no users exist** in the system. Once the first admin registers, the route returns a redirect to `/lucent/login`. Registration automatically assigns the `admin` role and sends a login link. ### Middleware Two middleware aliases are available for your routes: | Alias | Description | |---|---| | `lucent.auth` | Requires an active Lucent session | | `lucent.guest` | Redirects authenticated users away | ### Roles Roles are defined in your channel config. Only roles listed there are valid — anything else is silently stripped on assignment. The `canInvite` and `canBuild` config keys control which roles can invite users and trigger builds respectively. On console commands and non-`/lucent` routes, `currentUserId()` returns `config("lucent.systemUserId")` instead of the session user. ## Database Tables All Lucent-managed tables use the `lucent_` prefix: | Table | Description | |---|---| | `lucent_records` | All collection and file schema records | | `lucent_files` | Uploaded file metadata | | `lucent_edges` | Relationships between records | | `lucent_revisions` | Record revision history | | `lucent_users` | Users (when using `lucent` auth mode) | | `lucent_command_logs` | Background command execution logs | These are created automatically by running `php artisan lucent:setup`.