doc update
This commit is contained in:
+10
-2
@@ -165,9 +165,17 @@ Date and time selector. Stores as an ISO 8601 string.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### uuid
|
||||||
|
|
||||||
|
UUID text field. Stores an arbitrary UUID string. Typically used as a read-only external reference ID.
|
||||||
|
|
||||||
|
**Required:** `name`, `label`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
### json
|
### json
|
||||||
|
|
||||||
Raw JSON data field.
|
Raw JSON data field. Accepts either a JSON string or a plain array — both are stored as JSON.
|
||||||
|
|
||||||
**Required:** `name`, `label`
|
**Required:** `name`, `label`
|
||||||
|
|
||||||
@@ -175,7 +183,7 @@ Raw JSON data field.
|
|||||||
|
|
||||||
### file
|
### file
|
||||||
|
|
||||||
Upload or select files from a files schema.
|
Upload or select files from a files schema. Files can be uploaded directly or browsed from previously uploaded files. Image files (jpeg, png, webp, gif, tiff) automatically get a 300×300 thumbnail generated on upload.
|
||||||
|
|
||||||
**Required:** `name`, `label`, `collections`
|
**Required:** `name`, `label`, `collections`
|
||||||
|
|
||||||
|
|||||||
+18
-2
@@ -40,8 +40,6 @@ There are 2 types of schemas:
|
|||||||
| `label` | yes | Friendly display name |
|
| `label` | yes | Friendly display name |
|
||||||
| `type` | yes | Must be `"files"` |
|
| `type` | yes | Must be `"files"` |
|
||||||
| `fields` | yes | Array of field definitions. See [Fields](Fields.md) |
|
| `fields` | yes | Array of field definitions. See [Fields](Fields.md) |
|
||||||
| `disk` | no | Laravel disk name. Default: `"lucent"` |
|
|
||||||
| `path` | no | Subdirectory for uploads. Default: schema name |
|
|
||||||
| `groups` | no | Group IDs to split fields into tabs |
|
| `groups` | no | Group IDs to split fields into tabs |
|
||||||
| `isEntry` | no | Show in sidebar. Default: `false` |
|
| `isEntry` | no | Show in sidebar. Default: `false` |
|
||||||
| `sortBy` | no | Default sort in browser |
|
| `sortBy` | no | Default sort in browser |
|
||||||
@@ -51,6 +49,24 @@ There are 2 types of schemas:
|
|||||||
| `read` | no | Roles with read access |
|
| `read` | no | Roles with read access |
|
||||||
| `write` | no | Roles with write access |
|
| `write` | no | Roles with write access |
|
||||||
|
|
||||||
|
### File Metadata
|
||||||
|
|
||||||
|
Every uploaded file is automatically stored with the following metadata (stored in `lucent_files`):
|
||||||
|
|
||||||
|
| Field | Description |
|
||||||
|
|---|---|
|
||||||
|
| `id` | Unique file ID |
|
||||||
|
| `recordId` | ID of the record this file belongs to |
|
||||||
|
| `originalName` | Original filename as uploaded |
|
||||||
|
| `mime` | MIME type e.g. `image/webp` |
|
||||||
|
| `path` | Storage path e.g. `files/{recordId}/{filename}` |
|
||||||
|
| `size` | File size in bytes |
|
||||||
|
| `width` | Image width in pixels (0 for non-images) |
|
||||||
|
| `height` | Image height in pixels (0 for non-images) |
|
||||||
|
| `checksum` | SHA-1 hash of the file contents |
|
||||||
|
|
||||||
|
Image files (jpeg, png, webp, gif, tiff) also get a 300×300 thumbnail generated automatically at `thumbs/{path}`, plus any image filter presets configured in `lucent.imageFilters`.
|
||||||
|
|
||||||
|
|
||||||
## System Fields
|
## System Fields
|
||||||
|
|
||||||
|
|||||||
+84
-25
@@ -5,51 +5,94 @@ To generate the static content of the website, lucent provides a helper class.
|
|||||||
|
|
||||||
## Laravel Command
|
## Laravel Command
|
||||||
|
|
||||||
Just create a command and name it as you like. Make sure to inject the StaticGenerator class.
|
Create an Artisan command and inject `StaticGenerator`:
|
||||||
|
|
||||||
```php
|
```php
|
||||||
public function __construct(
|
class GenerateStatic extends Command
|
||||||
public StaticGenerator $staticGenerator,
|
{
|
||||||
) {
|
protected $signature = 'generate:static';
|
||||||
parent::__construct();
|
|
||||||
|
public function __construct(
|
||||||
|
public StaticGenerator $staticGenerator,
|
||||||
|
public Context $ctx,
|
||||||
|
) {
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handle(): void
|
||||||
|
{
|
||||||
|
$this->staticGenerator->run('generate:static', function ($writer) {
|
||||||
|
$writer->save("/", $this->ctx->render("homepage"));
|
||||||
|
$writer->save("/about", $this->ctx->render("about"));
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Redirect command
|
`run(string $signature, callable $callback)` — the first argument must match the command's artisan signature. This is used to stream live build logs in the Lucent UI. The callback receives a `Writer` instance.
|
||||||
|
|
||||||
There are cases which is useful to create a redirect like when you have a multilingual website:
|
|
||||||
|
## Writer: save
|
||||||
|
|
||||||
|
Writes an HTML file at the given path:
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$this->staticGenerator->run(function ($writer) {
|
$writer->save("/blog/my-post", $html);
|
||||||
$writer->createRedirect("/", "/el");
|
// writes to: storage/lucent/build/blog/my-post/index.html
|
||||||
});
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## The Writer save command
|
An optional third argument changes the file extension (default `"html"`).
|
||||||
|
|
||||||
In order to create an html file, you have to use the writer's save command
|
|
||||||
|
|
||||||
The first argument is the url and the second is the rendered HTML. That's where the page classes do come handy.
|
## Writer: createRedirect
|
||||||
|
|
||||||
|
Generates an HTML meta-refresh redirect. Useful for root-level locale redirects:
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$this->staticGenerator->run(function ($writer) {
|
$writer->createRedirect("/", "/el");
|
||||||
$writer->save("/", $this->ctx->render("homepage"));
|
$writer->createRedirect("/", "/el", "Redirecting", "Please wait...");
|
||||||
$writer->save("/about", $this->ctx->render("about"));
|
|
||||||
});
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Arguments: `from`, `to`, `title` (default `"Redirecting"`), `message` (default `"Redirecting Soon..."`).
|
||||||
|
|
||||||
|
|
||||||
|
## Writer: recordIterator
|
||||||
|
|
||||||
|
Iterates over all records in paginated batches — useful when there are too many records to load at once:
|
||||||
|
|
||||||
|
```php
|
||||||
|
$writer->recordIterator(
|
||||||
|
query: fn(int $limit, int $skip) => $query
|
||||||
|
->filter(["schema" => "blogPosts"])
|
||||||
|
->onlyPublished()
|
||||||
|
->limit($limit)
|
||||||
|
->skip($skip)
|
||||||
|
->tree(),
|
||||||
|
parser: function ($records) use ($writer) {
|
||||||
|
foreach ($records as $record) {
|
||||||
|
$writer->save(
|
||||||
|
"/blog/" . $record->data->slug,
|
||||||
|
$this->ctx->render("blogPost", $record),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
Signature: `recordIterator(callable $query, callable $parser, int $skip = 0, int $limit = 100): int`
|
||||||
|
|
||||||
|
The `$query` callable receives `($limit, $skip)` and must return a collection. Iteration stops automatically when a batch returns zero records.
|
||||||
|
|
||||||
|
|
||||||
## Storage and Permissions
|
## Storage and Permissions
|
||||||
|
|
||||||
All the generated html is stored on `storage/lucent/live`
|
All generated HTML is written to `storage/lucent/build` during the run, then atomically swapped to `storage/lucent/live` on completion. Create the public symlink with:
|
||||||
In order to make it accessible you have to create symlink:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
php artisan lucent:livelink
|
php artisan lucent:livelink
|
||||||
```
|
```
|
||||||
|
|
||||||
Now your static website is accessible at `http://localhost:8000/live`
|
The static site is then accessible at `http://localhost:8000/live`. To serve it without the `/live` prefix, add this to your nginx vhost:
|
||||||
|
|
||||||
But it would not be nice to have the **live** prefix everywhere. That's why you need to make the following tweak in the nginx vhost
|
|
||||||
|
|
||||||
```nginxconf
|
```nginxconf
|
||||||
location / {
|
location / {
|
||||||
@@ -57,14 +100,30 @@ location / {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Artisan Commands
|
||||||
|
|
||||||
|
| Command | Description |
|
||||||
|
|---|---|
|
||||||
|
| `lucent:setup-db` | Create all Lucent database tables |
|
||||||
|
| `lucent:schemas` | Compile schema JSON files |
|
||||||
|
| `lucent:livelink` | Create the `public/live` symlink |
|
||||||
|
| `lucent:rebuild:thumbnails` | Regenerate thumbnails for all uploaded images |
|
||||||
|
| `lucent:removeOrphanEdges` | Remove edges pointing to deleted records |
|
||||||
|
| `lucent:generate:collection {name}` | Scaffold a new collection schema JSON file |
|
||||||
|
|
||||||
|
|
||||||
## Build from the Lucent UI
|
## Build from the Lucent UI
|
||||||
|
|
||||||
In your lucent.php config file you can define your command that is used to generate the static files:
|
Register your generate command in `config/lucent.php` so admin users can trigger it from the UI:
|
||||||
|
|
||||||
```php
|
```php
|
||||||
"generateCommand" => "generate:static"
|
"commands" => [
|
||||||
|
"generate:static" => "Generate Static Site",
|
||||||
|
],
|
||||||
```
|
```
|
||||||
That way, the users will be able to initiate the build command through the user interface.
|
|
||||||
|
Only roles listed in `canBuild` can trigger commands from the UI.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -99,3 +99,113 @@ class Context
|
|||||||
```
|
```
|
||||||
|
|
||||||
Add the middleware inside the HTTP Kernel file.
|
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`.
|
||||||
Reference in New Issue
Block a user