6.8 KiB
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 namespace App\Lucent;
class Context
{
public string $website = "";
public string $title;
public string $description = "";
public string $url = "";
public string $mode = "static";
public string $locale = "el";
public array $data = [];
public array $locales = ["el","en"];
protected array $pages = [
"homepage" => 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
$this->app->singleton(Context::class);
Context Middleware
<?php namespace App\Http\Middleware;
class Context
{
public function __construct(public Context $context){}
public function handle(Request $request, Closure $next, string $mode = "static"): Response
{
$this->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) |
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:
"imageFilters" => [
"hero" => \App\ImageFilters\HeroFilter::class,
"thumb" => \App\ImageFilters\ThumbFilter::class,
],
A filter class must implement Intervention\Image\Filters\FilterInterface:
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
- User submits their email at
/lucent/login - A 32-character token is stored against the user with a timestamp
- Lucent emails a login link containing the token
- User clicks the link → token is validated (must be used within 1 hour)
- 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.