Compare commits

...

34 Commits

Author SHA1 Message Date
lexx 57b0727788 fix image limit 2025-07-03 15:28:52 +03:00
lexx 58b047edd2 Merge pull request 'Fix gte' (#24) from Fixing-date-&-datetime-compare-bugs into dev
Reviewed-on: #24
2025-06-16 15:48:36 +00:00
arvanitakis a78b699a5e Fix 2025-06-16 18:23:14 +03:00
lexx e910ae9878 login 2025-05-16 13:53:41 +03:00
lexx 362c649d36 Merge branch 'dev' of ssh://code.radical-elements.com:2727/lucent/lucent-laravel into dev 2025-05-16 13:27:48 +03:00
lexx 852c4d608d thumbnail limit 2025-05-16 13:27:25 +03:00
lexx aa59e55a41 meta htmx 2025-05-15 19:18:03 +03:00
lexx 348bad80e0 Merge pull request 'Fix' (#23) from Catching-thumbnail-rebuild-error into dev
Reviewed-on: #23
2025-05-06 10:46:58 +00:00
arvanitakis f0d4686141 Fix 2025-05-06 13:46:00 +03:00
lexx a482ab3c7e login urls 2025-03-20 21:06:40 +02:00
lexx c580882ec0 fix url 2025-03-20 20:57:47 +02:00
lexx 2cf8379cbe urls update 2025-03-20 20:53:51 +02:00
lexx c39ec469df files bug 2025-01-22 20:03:12 +02:00
lexx 232fcc8845 csv render title 2024-12-18 13:02:09 +02:00
lexx 9d5d4dd930 csv relations 2024-12-14 18:56:04 +02:00
lexx c507dc6031 upload fix 2024-10-23 19:34:41 +03:00
lexx 843f560710 new build 2024-09-27 17:42:49 +03:00
lexx 7574d67d80 some styling in tables 2024-09-27 16:48:05 +03:00
lexx 19931cb4d1 update files script 2024-09-27 16:27:37 +03:00
lexx 6458c1e71d rebuilding thumbnails command 2024-09-27 15:32:35 +03:00
lexx 63232585ab storage and image model 2024-09-27 14:28:20 +03:00
lexx 6d15591601 wip stograge 2024-09-20 13:39:45 +03:00
lexx 32c8378020 refactor files 2024-09-19 23:36:43 +03:00
lexx d0cd8228cc fix replacing config 2024-09-13 18:13:15 +03:00
lexx c45a3847f8 fix rich editor embed image original 2024-09-13 18:11:57 +03:00
lexx c0b3878674 file route for template generation 2024-09-13 17:16:04 +03:00
lexx f868219981 fixes and stuff 2024-09-11 16:21:51 +03:00
lexx 8ac0567e66 fix graph ignoring missing fields 2024-09-07 15:57:31 +03:00
lexx 02f8f5970a codemirror insert 2024-09-07 15:31:56 +03:00
lexx 0cd4e08716 fixing database connections 2024-09-07 13:22:58 +03:00
lexx cf3d621587 helper commands 2024-09-07 00:03:11 +03:00
lexx 6fc0a65b6f setup complete 2024-09-06 23:30:12 +03:00
lexx a73ee21568 wip setup guide 2024-09-06 21:00:15 +03:00
lexx ff54bcc2ef wip setup guide 2024-09-06 20:59:56 +03:00
83 changed files with 1204 additions and 664 deletions
+4
View File
@@ -0,0 +1,4 @@
{
"$schema": "/phpactor.schema.json",
"language_server_phpstan.enabled": false
}
+2 -1
View File
@@ -14,7 +14,8 @@
"intervention/image": "^2.7",
"phpoption/phpoption": "^1.9",
"spatie/image-optimizer": "^1.6",
"staudenmeir/laravel-cte": "^1.0"
"staudenmeir/laravel-cte": "^1.0",
"mustache/mustache": "^2.14"
},
"require-dev": {
Generated
+3 -3
View File
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "71cf4c1de3d614ce2f9607763bf5687f",
"content-hash": "e8fb1bee28ad339453d50110f7fea2f5",
"packages": [
{
"name": "brick/math",
@@ -5729,8 +5729,8 @@
"ext-zip": "*",
"ext-sqlite3": "*",
"ext-imagick": "*",
"php": "^8.3",
"ext-pdo": "*"
"ext-pdo": "*",
"php": "^8.3"
},
"platform-dev": [],
"plugin-api-version": "2.3.0"
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -1,11 +1,11 @@
{
"main.js": {
"file": "assets/main-D9joEh0I.js",
"file": "assets/main-BJyanQ7P.js",
"name": "main",
"src": "main.js",
"isEntry": true,
"css": [
"assets/main-BnJW41Dx.css"
"assets/main-Dk7njt4m.css"
]
}
}
+3 -1
View File
@@ -3,6 +3,7 @@
import Login from "./account/Login.svelte";
import Verify from "./account/Verify.svelte";
import Profile from "./account/Profile.svelte";
import SetupIndex from "./setup/Index.svelte";
import {setContext} from "svelte";
const components = {
@@ -10,6 +11,7 @@
login: Login,
verify: Verify,
profile: Profile,
setup: SetupIndex,
};
export let title;
@@ -22,7 +24,7 @@
setContext("user", user);
</script>
<div style="text-align: center;background: var(--p20);padding: 20px;color: var(--p90)">
<h1><a class="text-decoration-none" href="{channel.lucentUrl}">{channel.name}</a></h1>
<h1><a class="text-decoration-none" href="{channel.lucentUrl}">{channel.name ?? "Lucent Setup"}</a></h1>
</div>
<div>
<svelte:component this={components[view]} {title} {...data}/>
+7
View File
@@ -83,7 +83,11 @@
{#if record._file?.path}
<div class="file-table-row">
<Preview record={record} size={record._file?.width > 0 ? "medium" : "small"}/>
<div>
{#if record.status === "draft"}
<span style="text-transform: uppercase;font-size:10px">{record.status}</span>
{/if}
<a
href="{channel.lucentUrl}/records/{record.id}"
target={inModal ? "_blank" : "_self"}
@@ -109,6 +113,9 @@
href="{channel.lucentUrl}/records/{record.id}"
target={inModal ? "_blank" : "_self"}
>
{#if record.status === "draft"}
<span style="text-transform: uppercase;font-size:10px">{record.status}</span>
{/if}
{previewTitle(channel.schemas, record, graph)}
</a>
{/if}
@@ -18,7 +18,7 @@
<div class="references">
{#each recordEdges as recordEdge}
<span class="mr-3">
<span class="reference">
<PreviewCardSmall {schemas} {graph} record={recordEdge}/>
</span>
{/each}
+6 -9
View File
@@ -1,27 +1,24 @@
export function imgurl(channel,record) {
export function imgurl(channel, record) {
if (record._file.mime === "image/svg+xml") {
return fileurl(channel, record);
}
return channel.filesUrl + `/thumbs/${record._file.path}`;
return channel.disks[record._file.disk] + `/thumbs/${record._file.path}`;
}
export function fileurl(channel, record) {
return channel.filesUrl + `/${record._file.path}`;
return channel.disks[record._file.disk] + `/${record._file.path}`;
}
export function htmlurl(channel,record, preset) {
export function htmlurl(channel, record, preset) {
let html = "";
let url = fileurl(channel,record)
let url = fileurl(channel, record)
if (record._file.width > 0) {
let presetUrl = url;
if (preset) {
presetUrl = channel.filesUrl + `/templates/${preset}/${record._file.path}`;
presetUrl = channel.disks[record._file.disk] + `/templates/${preset}/${record._file.path}`;
}
html = `<img src="${presetUrl}" alt="${record._file.path}" />`
} else if (record._file.mime === "image/svg+xml") {
html = `<img src="${url}" alt="${record._file.path}"/>`
+26 -4
View File
@@ -1,10 +1,10 @@
<script>
// https://codesandbox.io/s/codemirror-remark-editor-4m4z9?file=/src/CodeEditor.js:374-387
import {onMount, onDestroy} from "svelte";
import {onDestroy, onMount} from "svelte";
import {basicSetup, EditorView} from "codemirror";
import { autocompletion, completionKeymap } from "@codemirror/autocomplete";
import {EditorState, Compartment} from "@codemirror/state";
import {autocompletion, completionKeymap} from "@codemirror/autocomplete";
import {Compartment, EditorState} from "@codemirror/state";
import {keymap} from "@codemirror/view";
import {indentWithTab} from "@codemirror/commands";
import {markdown} from "@codemirror/lang-markdown";
@@ -15,6 +15,29 @@
export let value;
export let editable = true;
export function insertMedia(info) {
let insertText = "";
if (info.record._file.width > 0) {
insertText = `![${info.record._file.originalName}](${info.url})`;
} else {
insertText = `[${info.record._file.originalName}](${info.originalUrl})`;
}
const cursor = codeMirrorView.state.selection.main.head;
const transaction = codeMirrorView.state.update({
changes: {
from: cursor,
insert: insertText,
},
// the next 2 lines will set the appropriate cursor position after inserting the new text.
selection: {anchor: cursor + 1},
scrollIntoView: true,
});
if (transaction) {
codeMirrorView.dispatch(transaction);
}
}
onMount(() => {
let language = new Compartment();
let tabSize = new Compartment();
@@ -51,7 +74,6 @@
});
});
onDestroy(() => {
+16 -1
View File
@@ -28,6 +28,15 @@
editor.addEventListener("trix-file-accept", (e) => {
e.preventDefault();
})
editor.addEventListener("trix-before-initialize", (e) => {
Trix.config.blockAttributes.heading1.tagName = 'h2';
const { toolbarElement } = e.target
const h1Button = toolbarElement.querySelector("[data-trix-attribute=heading1]")
h1Button.insertAdjacentHTML("afterend", `<button style="text-indent: initial;padding: 14px 10px !important;" type="button" class="trix-button trix-button--icon" data-trix-attribute="heading3" title="Heading 3" tabindex="-1" data-trix-active="">H3</button>`)
})
})
// onDestroy(() => {
// editor.removeEventListener("trix-before-initialize")
@@ -35,7 +44,13 @@
Trix.config.blockAttributes.default.breakOnReturn = false
console.log(Trix.config)
Trix.config.blockAttributes.heading3 = {
tagName: 'h3',
terminal: true,
breakOnReturn: true,
group: false
}
// console.log(Trix.config)
</script>
+10
View File
@@ -98,6 +98,16 @@
bind:graph
{record}
/>
{:else if field.info.name === "markdown"}
<Markdown
bind:value={data[field.name]}
{schema}
{field}
{validationErrors}
{isCreateMode}
bind:graph
{record}
/>
{:else}
<svelte:component
this={formElement}
+4 -1
View File
@@ -15,11 +15,14 @@
let backlinks = graph.parentEdges.map(edge => {
let schema = channel.schemas.find((s) => s.name === edge.sourceSchema);
let edgeField = findEdgeField(schema,edge.field);
if(!edgeField){
return null;
}
return {
field: edgeField.label,
record: graph.records.find( record => record.id === edge.source)
}
})
}).filter( edgeOrNull => !!edgeOrNull)
</script>
<div class="editor-field">
{#each backlinks as backlink}
@@ -13,11 +13,8 @@
{#if record?.data}
<a
href="{channel.lucentUrl}/records/{record.id}"
class="text-decoration-none rounded py-1 px-2 d-inline-block"
{title}
style="border:2px solid {!schema.color
? '#999'
: schema.color}!important;white-space: nowrap;"
class="reference"
>
{title}
</a>
@@ -1,21 +1,37 @@
<script>
import Codemirror from "../../libs/CodemirrorMarkdown.svelte";
import { getErrorMessage } from "./errorMessage";
import RichEditorFiles from "./RichEditorFiles.svelte";
export let value;
export let field;
export let graph;
export let record;
export let isCreateMode;
// export let id;
export let validationErrors;
$: errorMessage = getErrorMessage(validationErrors, field.name);
let editor;
function insertMedia(e){
editor.insertMedia(e.detail)
}
</script>
<div class="mb-3">
<Codemirror bind:value editable={!field.readonly || isCreateMode} />
<Codemirror bind:this={editor} bind:value editable={!field.readonly || isCreateMode} />
{#if field.collections.length > 0}
<RichEditorFiles
bind:graph
{record}
{field}
{validationErrors}
on:editor-insert={insertMedia}
>
</RichEditorFiles>
{/if}
{#if errorMessage}
<div class="invalid-feedback d-block">
{errorMessage}
@@ -1,6 +1,4 @@
<script>
import {sortByField} from "../../edges/sortEdges";
import Sortable from "../../libs/Sortable.svelte";
import PreviewFile from "../previews/PreviewFile.svelte";
import Dropdown from "../../common/Dropdown.svelte";
import Dialog from "../../dialog/Dialog.svelte";
@@ -70,7 +68,8 @@
{#each references as reference (reference.id)}
<!--This div helps the sorting thing-->
<div>
<PreviewFile record={reference} hasDelete={true} hasInsert={true} on:remove={removeReference} on:editor-insert></PreviewFile>
<PreviewFile record={reference} hasDelete={true} hasInsert={true} on:remove={removeReference}
on:editor-insert></PreviewFile>
</div>
{/each}
{/if}
@@ -26,9 +26,10 @@
function insert(e, preset) {
e.preventDefault();
let html = htmlurl(channel, record, preset)
let url = !preset ? `/${record._file.path}` : `/templates/${preset}/${record._file.path}`;
dispatch("editor-insert", {
html: html,
url: channel.filesUrl + `/templates/${preset}/${record._file.path}`,
url: channel.filesUrl + url,
originalUrl: channel.filesUrl + "/" + record._file.path,
record: record
});
+20
View File
@@ -0,0 +1,20 @@
<script>
import Step from "./Step.svelte"
export let steps;
export let allSuccess = false;
console.log(steps);
</script>
<div class="wrapper-tiny">
{#each steps as step}
<Step {step}></Step>
{/each}
<div style="text-align: center;margin-top: 30px;">
{#if allSuccess}
<a href="/lucent/register" class="bt">Create the first user</a>
{/if}
</div>
</div>
+67
View File
@@ -0,0 +1,67 @@
<script>
import Icon from "../common/Icon.svelte"
export let step;
</script>
<div class="step step-{step.status}">
<div class="step-icon">
{#if step.status === "success"}
<Icon icon="check"></Icon>
{:else}
<Icon icon="close"></Icon>
{/if}
</div>
<div style="width:100%">
<h4>{step.name}</h4>
<details>
<summary>Instuctions</summary>
<code class="instructions">{step.instructions}</code>
</details>
</div>
</div>
<style>
.step-success .step-icon{
background: var(--suc10);
color: var(--suc100);
}
.step-fail .step-icon{
background: var(--err10);
color: var(--err100);
}
.step-icon{
padding: 12px;
border-radius: 12px;
}
.step {
width: 100%;
display: flex;
align-items: start;
gap: 10px;
justify-content: space-between;
padding: 12px;
border-radius: 12px;
}
details {
width: 100%;
}
.instructions {
margin-top: 20px;
padding: 12px;
border-radius: 12px;
background: var(--p10);
white-space: break-spaces;
display: block;
}
</style>
+5
View File
@@ -7,11 +7,16 @@
.cm-content{
background-color: var(--p10);
color: var(--p100);
}
}
.cm-content{
background-color: var(--p20);
}
.ͼ4 .cm-line ::selection, .ͼ4 .cm-line::selection{
background: var(--p40) !important;
}
.cm-activeLine{
+43
View File
@@ -0,0 +1,43 @@
.flatpickr-wrapper {
display: block !important;
}
.editor-field {
.flatpickr-calendar {
border-radius: 12px !important;
}
.flatpickr-months .flatpickr-month {
background: var(--p30);
color: var(--text);
font-size: 12px;
}
.flatpickr-current-month .flatpickr-monthDropdown-months {
background: var(--p30);
}
.flatpickr-weekdays{
background: var(--p30);
color: var(--text);
}
.flatpickr-weekdaycontainer .flatpickr-weekday{
background: var(--p30);
color: var(--text);
}
.flatpickr-days{
background: var(--p10);
color: var(--text);
}
.flatpickr-time{
background: var(--p10);
color: var(--text);
}
}
+2 -2
View File
@@ -15,7 +15,7 @@ body:has(dialog[open]) {
dialog {
margin: 2vh auto;
background-color: #fff;
background-color: var(--p10);
padding: 34px;
border: none;
border-radius: 12px;
@@ -49,6 +49,6 @@ dialog::backdrop {
position: sticky;
top: -34px;
z-index: 999;
background: #fff;
background-color: var(--p10);
padding: 10px 0;
}
+12
View File
@@ -113,6 +113,18 @@
.field-ui-number {
text-align: right;
}
.references{
display: flex;
gap: 4px;
.reference{
font-size: 13px;
border-radius: 12px;
background: var(--p30);
padding: 1px 5px;
}
}
}
.file-table-row {
+5
View File
@@ -22,6 +22,11 @@
line-height: 30px;
}
h3{
font-size: 18px;
line-height: 28px;
}
ul {
padding: 0 0 0 16px;
list-style: none outside none;
+1 -4
View File
@@ -70,6 +70,7 @@
@import "./reference-tags";
@import "./members";
@import "./revisions";
@import "./datepicker";
body {
background-color: var(--p10);
@@ -104,7 +105,3 @@ a {
.lucent-component {
position: relative;
}
.flatpickr-wrapper {
display: block!important;
}
+1 -1
View File
@@ -9,7 +9,7 @@
<div class="form">
<h2 class="mb-5">Enter Lucent</h2>
<form hx-post="/lucent/login" >
<form hx-post="{{config("lucent.url")}}/lucent/login" >
@csrf
<p>Submit your email address and you will receive a <b>login link</b> to your email</p>
<p>Don't forget to check your spam folder</p>
+1 -1
View File
@@ -9,7 +9,7 @@
<div class="form">
<h2 class="mb-5">Welcome to Lucent</h2>
<form hx-post="/lucent/verify" hx-redirect="/lucent" hx-target-error=".form-errors" >
<form hx-post="{{config("lucent.url")}}/lucent/verify" hx-redirect="{{config("lucent.url")}}/lucent" hx-target-error=".form-errors" >
<input type="hidden" value="{{$email}}" name="email" />
<input type="hidden" value="{{$token}}" name="token" />
@csrf
+4 -4
View File
@@ -6,11 +6,11 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>@yield('title') - Lucent Data Platform</title>
<meta name="htmx-config" content='{"selfRequestsOnly": false}' />
@if(config("lucent.env") === "production")
<!-- if production -->
<link rel="stylesheet" href="/vendor/lucent/dist/{{ $manifest['main.js']["css"][0] }}"/>
<script type="module" src="/vendor/lucent/dist/{{ $manifest['main.js']["file"] }}"></script>
<link rel="stylesheet" href="{{url('vendor/lucent/dist/'.$manifest['main.js']["css"][0])}}"/>
<script type="module" src="{{url('vendor/lucent/dist/'.$manifest['main.js']["file"])}}"></script>
@else
<!-- if development -->
@php
@@ -20,7 +20,7 @@
@endif
{{-- <link rel="icon" type="image/x-icon" href="/favicon.ico"/>--}}
<link rel="icon" type="image/x-icon" href="{{url('favicon.ico')}}">
</head>
+5 -7
View File
@@ -8,8 +8,8 @@
<title>@yield('title') - Lucent Data Platform</title>
@if(config("lucent.env") == "production")
<!-- if production -->
<link rel="stylesheet" href="/vendor/lucent/dist/{{ $manifest['main.js']["css"][0] }}"/>
<script type="module" src="/vendor/lucent/dist/{{ $manifest['main.js']["file"] }}"></script>
<link rel="stylesheet" href="{{url('vendor/lucent/dist/'.$manifest['main.js']["css"][0])}}"/>
<script type="module" src="{{url('vendor/lucent/dist/'.$manifest['main.js']["file"])}}"></script>
@else
<!-- if development -->
@php
@@ -18,16 +18,14 @@
<script type="module" crossorigin src="http://127.0.0.1:5173/main.js"></script>
@endif
<link rel="icon" type="image/x-icon" href="/favicon.ico">
<link rel="icon" type="image/x-icon" href="{{url('favicon.ico')}}">
</head>
<body>
@yield('content')
</body>
</html>
+11 -11
View File
@@ -3,7 +3,7 @@
namespace Lucent\Account;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Lucent\Database\Database;
use Lucent\Primitive\Collection;
use PhpOption\Option;
@@ -12,7 +12,7 @@ class UserRepo
public function count(): int
{
return DB::table("users")->count();
return Database::make()->table("users")->count();
}
/**
@@ -20,7 +20,7 @@ class UserRepo
*/
public function all(): Collection
{
$usersData = DB::table("users")->get();
$usersData = Database::make()->table("users")->get();
$users = array_map(fn($userData) => $this->fromArray((array)$userData), $usersData->toArray());
return new Collection($users);
@@ -31,14 +31,14 @@ class UserRepo
{
$userData = toArray($user);
$userData["roles"] = json_encode($userData["roles"]);
DB::table("users")->insert($userData);
Database::make()->table("users")->insert($userData);
}
public function update(User $user): void
{
$userData = toArray($user);
$userData["roles"] = json_encode($userData["roles"]);
DB::table("users")->where("id", $user->id)->update($userData);
Database::make()->table("users")->where("id", $user->id)->update($userData);
}
@@ -46,7 +46,7 @@ class UserRepo
{
$newToken = Token::new(32);
DB::table("users")
Database::make()->table("users")
->where("id", $id)
->update([
'loggedInAt' => Carbon::now()->toJson(),
@@ -62,7 +62,7 @@ class UserRepo
*/
public function findByEmail(Email $email): Option
{
$user = DB::table("users")->where("email", $email->value())->first();
$user = Database::make()->table("users")->where("email", $email->value())->first();
if (empty($user)) {
return none();
@@ -76,7 +76,7 @@ class UserRepo
*/
public function findById(string $id): Option
{
$user = DB::table("users")->where("id", $id)->first();
$user = Database::make()->table("users")->where("id", $id)->first();
if (empty($user)) {
return none();
@@ -88,12 +88,12 @@ class UserRepo
public function updateName(string $userId, Name $name): void
{
DB::table("users")->where("id", $userId)->update(["name" => $name->value]);
Database::make()->table("users")->where("id", $userId)->update(["name" => $name->value]);
}
public function updateEmail(string $userId, Email $email): void
{
DB::table("users")->where("id", $userId)->update(["email" => $email->value()]);
Database::make()->table("users")->where("id", $userId)->update(["email" => $email->value()]);
}
public function fromArray(array $data): User
@@ -102,7 +102,7 @@ class UserRepo
id: $data["id"],
name: new Name($data["name"] ?? ""),
email: new Email($data["email"]),
roles: json_decode($data["roles"] ?? "[]",true),
roles: json_decode($data["roles"] ?? "[]", true),
createdAt: $data["createdAt"],
updatedAt: $data["updatedAt"],
loggedInAt: $data["loggedInAt"] ?? null,
+11
View File
@@ -4,12 +4,14 @@ namespace Lucent\Channel;
use Lucent\Channel\Data\UserCommand;
use Lucent\Primitive\Collection;
use Lucent\Schema\FilesSchema;
use Lucent\Schema\Schema;
final class Channel
{
public string $lucentUrl;
public string $filesUrl;
public array $disks;
public string $previewTargetUrl;
/**
@@ -28,6 +30,7 @@ final class Channel
{
$this->lucentUrl = $url . "/lucent";
$this->filesUrl = $this->makeFilesUrl();
$this->disks = $this->getDisksFromSchemas();
$this->previewTargetUrl = $url . "/" . $previewTarget;
}
@@ -37,8 +40,16 @@ final class Channel
return match (config("filesystems.disks.lucent.driver")) {
"s3" => config("filesystems.disks.lucent.endpoint") . "/" . config("filesystems.disks.lucent.bucket"),
"local" => $this->url . "/storage" . config("filesystems.disks.lucent.endpoint"),
default => ""
};
}
private function getDisksFromSchemas()
{
return $this->schemas->filter(fn(Schema $schema) => get_class($schema) === FilesSchema::class)->reduce(function (array $carry, Schema $schema) {
$carry[$schema->disk] = config("filesystems.disks." . $schema->disk . ".url");
return $carry;
}, []);
}
}
+5 -5
View File
@@ -2,15 +2,15 @@
namespace Lucent\Command;
use Illuminate\Support\Facades\DB;
use Lucent\Command\Data\CommandLogItem;
use Lucent\Database\Database;
class CommandRepo
{
public function findBySignature($signature): ?CommandLogItem
{
$row = DB::table("command_logs")->where("signature", $signature)->first();
$row = Database::make()->table("command_logs")->where("signature", $signature)->first();
if (empty($row)) {
return null;
}
@@ -22,16 +22,16 @@ class CommandRepo
{
$foundCommandLogItem = $this->findBySignature($commandLogItem->signature);
if (empty($foundCommandLogItem)) {
DB::table("command_logs")->insert(toArray($commandLogItem));
Database::make()->table("command_logs")->insert(toArray($commandLogItem));
return;
}
DB::table("command_logs")->where("signature", $commandLogItem->signature)->update(toArray($commandLogItem));
Database::make()->table("command_logs")->where("signature", $commandLogItem->signature)->update(toArray($commandLogItem));
}
public function appendToLogs(string $signature, string $line): void
{
$res = DB::update(
Database::make()->update(
'update command_logs set logs = logs || ? where signature = ?',
[$line, $signature]
);
+42
View File
@@ -0,0 +1,42 @@
<?php
namespace Lucent\Commands;
use Illuminate\Console\Command;
use Lucent\Primitive\Collection;
use Lucent\Schema\CollectionSchema;
class GenerateCollectionSchema extends Command
{
protected $signature = 'lucent:generate:collection {name}';
protected $description = 'Generate a lucent collection';
public function handle()
{
$name = $this->argument('name');
$schema = new CollectionSchema(
name: $name,
label: $name,
visible: [],
groups: [],
fields: Collection::make(),
);
$json = json_encode($schema, JSON_PRETTY_PRINT);
$configDir = base_path(config('lucent.schemas_path'));
$schemaPath = $configDir . "/" . $name . '.json';
if(file_exists($schemaPath)){
$this->error("The schema file already exists.");
return 0;
}
file_put_contents($schemaPath, $json);
$this->info("The schema file has been created.");
}
}
+43
View File
@@ -0,0 +1,43 @@
<?php
namespace Lucent\Commands;
use Illuminate\Console\Command;
use Lucent\Primitive\Collection;
use Lucent\Schema\FilesSchema;
class GenerateFileSchema extends Command
{
protected $signature = 'lucent:generate:file {name}';
protected $description = 'Generate a lucent file schema';
public function handle()
{
$name = $this->argument('name');
$schema = new FilesSchema(
name: $name,
label: $name,
fields: Collection::make(),
disk: "lucent",
path: $name,
groups: []
);
$json = json_encode($schema, JSON_PRETTY_PRINT);
$configDir = base_path(config('lucent.schemas_path'));
$schemaPath = $configDir . "/" . $name . '.json';
if (file_exists($schemaPath)) {
$this->error("The schema file already exists.");
return 0;
}
file_put_contents($schemaPath, $json);
$this->info("The schema file has been created.");
}
}
+16 -34
View File
@@ -7,6 +7,9 @@ use Exception;
use Illuminate\Console\Command;
use Intervention\Image\ImageManager;
use Lucent\Channel\ChannelService;
use Lucent\File\FileService;
use Lucent\Query\Query;
use Lucent\Schema\FilesSchema;
use Lucent\Schema\Schema;
use Lucent\Schema\Type;
@@ -18,7 +21,10 @@ class RebuildThumbnails extends Command
protected $description = 'Rebuilds thumbnails for path';
public function __construct(public ImageManager $imageManager)
public function __construct(
public Query $query,
public FileService $fileService,
)
{
parent::__construct();
}
@@ -27,49 +33,25 @@ class RebuildThumbnails extends Command
public function handle(ChannelService $channelService): int
{
$channelService->channel->schemas
->where("type", Type::FILES)->values()
->filter(fn(Schema $schema) => get_class($schema) === FilesSchema::class)
->map([$this, 'rebuildThumbnails']);
return 0;
}
public function rebuildThumbnails(Schema $schema): void
public function rebuildThumbnails(FilesSchema $schema): void
{
$filesDir = storage_path("app/public/" . $schema->path . "/");
$thumbDir = storage_path("app/public/thumbs/" . $schema->path . "/");
if (!file_exists($thumbDir)) {
make_dir_r($thumbDir);
}
$this->info("Rebuilding thumbnails for ". $schema->name);
$records = $this->query->limit(0)->filter(["schema" => $schema->name])->run()->records;
$disk = $this->fileService->loadDisk($schema->disk);
foreach ($records as $record) {
try{
if (!file_exists($filesDir)) {
make_dir_r($filesDir);
}
$filesDirIterator = new DirectoryIterator($filesDir);
foreach ($filesDirIterator as $file) {
if ($file->isDot()) {
continue;
}
try {
$image = $this->imageManager->make($filesDir . $file->getFilename());
$this->fileService->createTemplates($disk, $record->_file->path);
} catch (Exception $e) {
$this->error($e->getMessage());
continue;
echo "File ". $record->_file->originalName . " could not be rebuilt \n" ;
}
$image->fit(300, 300);
try {
$image->encode('webp', 75)->save($thumbDir . $file->getFilename());
} catch (Exception $e) {
$this->error($e->getMessage());
continue;
}
$this->info($file->getFilename());
}
}
+114
View File
@@ -0,0 +1,114 @@
<?php
namespace Lucent\Commands;
use Illuminate\Console\Command;
use Illuminate\Database\Schema\Blueprint;
use Lucent\Database\Database;
class SetupDatabase extends Command
{
protected $signature = 'lucent:setup-db';
protected $description = 'Run to setup a new database';
public function handle()
{
$dbConnection = config("lucent.database");
$databasePath = config("database.connections.$dbConnection.database");
if(file_exists($databasePath)){
$this->error("Database already exists.");
return 0;
}
touch($databasePath);
$this->tableUsers();
$this->tableRecords();
$this->tableRevisions();
$this->tableSessions();
$this->tableCommandLogs();
$this->info("Lucent Database Setup Completed");
}
private function tableUsers(): void
{
Database::make()->getSchemaBuilder()->create('users', function (Blueprint $table) {
$table->uuid("id")->primary();
$table->string('name')->nullable();
$table->string('email')->unique();
$table->jsonb('roles');
$table->string('createdAt');
$table->string('updatedAt');
$table->string('loggedInAt');
$table->string('mailToken')->nullable();
});
}
private function tableSessions(): void
{
Database::make()->getSchemaBuilder()->create('sessions', function (Blueprint $table) {
$table->string('id')->primary();
$table->foreignId('user_id')->nullable()->index();
$table->string('ip_address', 45)->nullable();
$table->text('user_agent')->nullable();
$table->longText('payload');
$table->integer('last_activity')->index();
});
}
private function tableRecords(): void
{
Database::make()->getSchemaBuilder()->create('records', function (Blueprint $table) {
$table->uuid('id')->primary();
$table->string('schema');
$table->string('status');
$table->jsonb('data');
$table->jsonb('_sys');
$table->jsonb('_file');
$table->text('search')->default("");
$table->index(['schema', '_sys->updatedAt', 'status']);
$table->index('search');
});
Database::make()->getSchemaBuilder()->create('edges', function (Blueprint $table) {
$table->uuid('source');
$table->uuid('target');
$table->string('sourceSchema');
$table->string('targetSchema');
$table->string('field');
$table->string('rank');
$table->unique(['source', 'target', "field"]);
});
}
private function tableRevisions(): void
{
Database::make()->getSchemaBuilder()->create('revisions', function (Blueprint $table) {
$table->uuid('id')->primary();
$table->uuid('recordId');
$table->string('schema');
$table->jsonb('data');
$table->jsonb('_sys');
$table->jsonb('_file');
$table->jsonb('_edges');
});
}
private function tableCommandLogs(): void
{
Database::make()->getSchemaBuilder()->create('command_logs', function (Blueprint $table) {
$table->uuid('id')->primary();
$table->string('signature');
$table->integer('pid')->nullable();
$table->text('logs');
});
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
namespace Lucent\Commands;
use Illuminate\Console\Command;
use Lucent\Database\Database;
class UpgradeFiles122 extends Command
{
protected $signature = 'lucent:upgrade:files_1_2_2 {schema} {disk}';
protected $description = 'Upgrade to the new filesystem';
public function handle()
{
$schema = $this->argument('schema');
$disk = $this->argument('disk');
$db = Database::make();
$records = $db->table("records")->where("schema", $schema)->get();
foreach ($records as $record) {
$array = json_decode($record->_file, true);
$array["disk"] = $disk;
$db->table("records")->where("id", $record->id)->update(["_file" => json_encode($array)]);
}
}
}
+1 -1
View File
@@ -2,7 +2,7 @@
return [
"env" => env("LUCENT_ENV", "production"),
"schemas_path" => env("LUCENT_SCHEMAS_PATH", "app/Lucent"),
"schemas_path" => env("LUCENT_SCHEMAS_PATH", "resources/lucent/schemas"),
"database" => env('LUCENT_DB_CONNECTION', env('DB_CONNECTION', "sqlite")),
"name" => env("LUCENT_NAME", "Lucent"),
"url" => env("LUCENT_URL", env('APP_URL')),
+17
View File
@@ -0,0 +1,17 @@
<?php
namespace Lucent\Database;
use Illuminate\Database\Connection;
use Illuminate\Support\Facades\DB;
class Database
{
public static function make(): Connection{
$dbConnection = config("lucent.database");
return DB::connection($dbConnection);
}
}
@@ -1,40 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
protected $connection = 'lucentdb';
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->uuid("id")->primary();
$table->string('name')->nullable();
$table->string('email')->unique();
$table->jsonb('roles');
$table->string('createdAt');
$table->string('updatedAt');
$table->string('loggedInAt');
$table->string('mailToken')->nullable();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('users');
}
};
@@ -1,37 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
protected $connection = 'lucentdb';
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('sessions', function (Blueprint $table) {
$table->string('id')->primary();
$table->foreignId('user_id')->nullable()->index();
$table->string('ip_address', 45)->nullable();
$table->text('user_agent')->nullable();
$table->longText('payload');
$table->integer('last_activity')->index();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('sessions');
}
};
-50
View File
@@ -1,50 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
protected $connection = 'lucentdb';
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('records', function (Blueprint $table) {
$table->uuid('id')->primary();
$table->string('schema');
$table->string('status');
$table->jsonb('data');
$table->jsonb('_sys');
$table->jsonb('_file');
$table->index(['schema', 'status']);
});
Schema::create('edges', function (Blueprint $table) {
$table->uuid('source');
$table->uuid('target');
$table->string('sourceSchema');
$table->string('targetSchema');
$table->string('field');
$table->string('rank');
$table->unique(['source', 'target', "field"]);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('records');
Schema::dropIfExists('edges');
}
};
@@ -1,37 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
protected $connection = 'lucentdb';
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('revisions', function (Blueprint $table) {
$table->uuid('id')->primary();
$table->uuid('recordId');
$table->string('schema');
$table->jsonb('data');
$table->jsonb('_sys');
$table->jsonb('_file');
$table->jsonb('_edges');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('revisions');
}
};
@@ -1,38 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
protected $connection = 'lucentdb';
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('records', function (Blueprint $table) {
$table->text('search')->default("");
$table->index('search');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('records', function (Blueprint $table) {
$table->dropColumn('search');
$table->dropIndex('search');
});
}
};
@@ -1,37 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
protected $connection = 'lucentdb';
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('records', function (Blueprint $table) {
$table->dropIndex(['schema', 'status']);
$table->index(['schema', '_sys->updatedAt', 'status']);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('records', function (Blueprint $table) {
$table->dropIndex(['schema', '_sys->updatedAt', 'status']);
$table->index(['schema', 'status']);
});
}
};
@@ -1,35 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
protected $connection = 'lucentdb';
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('command_logs', function (Blueprint $table) {
$table->uuid('id')->primary();
$table->string('signature');
$table->integer('pid')->nullable();
$table->text('logs');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('command_logs');
}
};
+9 -9
View File
@@ -1,6 +1,6 @@
<?php namespace Lucent\Edge;
use Illuminate\Support\Facades\DB;
use Lucent\Database\Database;
use Lucent\LucentException;
use PDOException;
use stdClass;
@@ -15,7 +15,7 @@ class EdgeRepo
public function insert(Edge $edge): void
{
try {
DB::table("edges")->insert($edge->toDB());
Database::make()->table("edges")->insert($edge->toDB());
} catch (PDOException $e) {
if ($e->getCode() == 23505) {
throw new LucentException("Edge already exists");
@@ -34,7 +34,7 @@ class EdgeRepo
{
$edgesDB = collect($edges)->map(fn($e) => $e->toDB())->toArray();
try {
DB::table("edges")->insert($edgesDB);
Database::make()->table("edges")->insert($edgesDB);
} catch (PDOException $e) {
if ($e->getCode() == 23505) {
throw new LucentException("Edge already exists");
@@ -52,8 +52,8 @@ class EdgeRepo
public function replaceForRecord(string $from, array $edges): void
{
$edgesDB = collect($edges)->map(fn($e) => $e->toDB())->toArray();
DB::table("edges")->where("source", $from)->delete();
DB::table("edges")->insert($edgesDB);
Database::make()->table("edges")->where("source", $from)->delete();
Database::make()->table("edges")->insert($edgesDB);
}
@@ -62,13 +62,13 @@ class EdgeRepo
*/
public function findAll(): array
{
$edges = DB::table("edges")->get();
$edges = Database::make()->table("edges")->get();
return $edges->map([$this, 'mapEdge'])->toArray();
}
public function findForSource(string $recordId): array
{
$edges = DB::table("edges")->where("source", $recordId)->get();
$edges = Database::make()->table("edges")->where("source", $recordId)->get();
return $edges->map([$this, 'mapEdge'])->toArray();
}
@@ -89,7 +89,7 @@ class EdgeRepo
public function remove(Edge $edge): void
{
DB::table("edges")
Database::make()->table("edges")
->where("source", $edge->source)
->where("target", $edge->target)
->where("sourceSchema", $edge->sourceSchema)
@@ -100,7 +100,7 @@ class EdgeRepo
public function findLastEdgeRank(string $source, string $field): string
{
$data = DB::table("edges")
$data = Database::make()->table("edges")
->where("source", $source)
->where("field", $field)
->orderBy("rank", "desc")
+28 -44
View File
@@ -2,25 +2,28 @@
namespace Lucent\File;
use Exception;
use Illuminate\Contracts\Filesystem\Filesystem;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\DB;
use Illuminate\Log\Logger;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Intervention\Image\ImageManagerStatic;
use Intervention\Image\ImageManager;
use Lucent\Channel\ChannelService;
use Lucent\Database\Database;
use Lucent\LucentException;
use Lucent\Record\FileData as RecordFile;
use Lucent\Record\QueryRecord;
use Lucent\Schema\FilesSchema;
use Lucent\Schema\Schema;
use Spatie\ImageOptimizer\OptimizerChainFactory;
class FileService
{
public function __construct(
public ChannelService $channelService
public ChannelService $channelService,
public ImageManager $imageManager,
public Logger $logger
)
{
}
@@ -64,8 +67,7 @@ class FileService
isDuplicate: true
);
}
$disk = $this->loadDisk();
$disk = $this->loadDisk($schema);
$path = $schema->path . "/" . $filename;
$res = $disk->put(
$path,
@@ -77,13 +79,17 @@ class FileService
throw new LucentException("File $filename not uploaded");
}
$this->createThumbnail($disk, $schema->path, $filename, $file);
if($this->isImage($mimetype)){
$this->createTemplates($disk, $path);
}
list($width, $height) = $this->isImage($mimetype) ? getimagesize($file) : [0, 0];
$recordFile = new RecordFile(
originalName: $originalFilename,
mime: $mimetype,
path: $path,
disk: $schema->disk,
size: $file->getSize(),
width: $width,
height: $height,
@@ -108,28 +114,16 @@ class FileService
return in_array($mimetype, $imageMimes);
}
public function loadDisk(): Filesystem
public function loadDisk(Schema|string $schema): Filesystem
{
return Storage::disk('lucent');
return Storage::build([
'driver' => 'lucent',
// 'key' => config("filesystems.disks.s3.key"),
// 'secret' => config("filesystems.disks.s3.secret"),
// 'region' => config("filesystems.disks.s3.region"),
// 'bucket' => config("filesystems.disks.s3.bucket"),
// // 'url' => $schema->objectStorageUrl,
// 'endpoint' => $schema->objectStorageEndpoint,
// 'use_path_style_endpoint' => false,
'visibility' => 'public', // now managed by aws policy
'root' => storage_path('app/public'),
'throw' => true,
]);
return Storage::disk($schema->disk ?? $schema);
}
private function checkDuplicate(string $schemaName, string $checksum, int $filesize): string
{
$record = DB::table("records")
$record = Database::make()->table("records")
->where("schema", $schemaName)
->where("_file->checksum", $checksum)
->where("_file->size", $filesize)
@@ -138,29 +132,19 @@ class FileService
return $record->id ?? "";
}
private function createThumbnail(Filesystem $disk, string $schemaPath, string $filename, UploadedFile $file): void
public function createTemplates(Filesystem $disk, string $path): void
{
$thumbDir = "thumbs/" . $schemaPath . "/";
// if (!file_exists($thumbDir)) {
// make_dir_r($thumbDir);
// }
try {
ImageManagerStatic::configure(['driver' => 'imagick']);
$image = ImageManagerStatic::make($file);
} catch (Exception $e) {
logger($e->getMessage());
return;
$originalImage = $this->imageManager->make($disk->get($path));
foreach (config("lucent.imageFilters") as $preset => $filterClass) {
$imageClone = clone $originalImage;
$image = $imageClone->filter(new $filterClass);
$templateUri = "/templates/" . $preset . "/" . $path;
$disk->put($templateUri, $image->encode('webp', 75));
}
$image->fit(300, 300);
try {
$this->loadDisk()->put($thumbDir . $filename, $image->encode('webp', 75));
// $image->encode('webp', 75)->save($thumbDir . $filename);
} catch (Exception $e) {
logger($e->getMessage());
}
$thumbDir = "thumbs/" . $path;
$image = $originalImage->fit(300, 300);
$disk->put($thumbDir, $image->encode('webp', 75));
}
}
-74
View File
@@ -1,74 +0,0 @@
<?php
namespace Lucent\File;
use Exception;
use Illuminate\Log\Logger;
use Intervention\Image\ImageManager;
use Lucent\Channel\ChannelService;
use Lucent\Record\QueryRecord;
class ImageService
{
private string $notFoundImage = "/not-found.jpg";
public function __construct(
public ImageManager $imageManager,
public FileService $fileService,
public ChannelService $channelService,
public Logger $logger
)
{
}
public function file(?QueryRecord $record, string $template = ""): string
{
if (empty($record)) {
return $this->notFoundImage;
}
$originalPath = $record->_file->path;
$templateUri = $this->findTemplate($originalPath, $template);
if ($templateUri === false) {
$templateUri = $this->createTemplate($originalPath, $template);
}
return $this->channelService->channel->filesUrl . "/" . $templateUri;
}
private function findTemplate(string $originalPath, string $template): string|false
{
$templateUri = "templates/" . $template . "/" . $originalPath;
$templateFilePath = public_path("storage/" . $templateUri);
if (file_exists($templateFilePath)) {
return $templateUri;
}
return false;
}
private function createTemplate(string $originalPath, string $template): string
{
try {
$image = $this->imageManager->make( $this->fileService->loadDisk()->get($originalPath));
} catch (Exception $e) {
$this->logger->error($e->getMessage());
return $originalPath;
}
$image = $image->filter(new $this->channelService->channel->imageFilters[$template]);
try {
$templateUri = "/templates/" . $template . "/" . $originalPath;
$this->fileService->loadDisk()->put($templateUri, $image->encode('webp', 75));
} catch (Exception $e) {
$this->logger->error($e->getMessage());
return $this->notFoundImage;
}
return $templateUri;
}
}
+16 -1
View File
@@ -7,6 +7,7 @@ use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
use Lucent\Channel\ChannelService;
use Lucent\File\FileService;
use Lucent\File\ImageService;
use Lucent\Query\Query;
use Lucent\Record\InputData\RecordInputData;
use Lucent\Record\RecordService;
@@ -27,6 +28,21 @@ class FileController extends Controller
{
}
public function fromDisk(Request $request, string $disk)
{
$imagePath = $request->route("any");
$disk = $this->fileService->loadDisk($disk);
return response()->file($disk->path($imagePath));
}
public function thumb(Request $request, string $disk)
{
$imagePath = "thumbs/".$request->route("any");
$disk = $this->fileService->loadDisk($disk);
return response()->file($disk->path($imagePath));
}
public function download(Request $request)
{
$disk = $this->fileService->loadDisk();
@@ -39,7 +55,6 @@ class FileController extends Controller
$validator = Validator::make($request->all(), [
'files.*' => 'required|file|max:100000',
]);
if ($validator->fails()) {
return fail($validator->errors()->first());
}
+38 -4
View File
@@ -17,8 +17,10 @@ use Lucent\Record\QueryRecord;
use Lucent\Record\RecordService;
use Lucent\Record\Status;
use Lucent\Schema\System;
use Lucent\Schema\Ui\Reference;
use Lucent\Schema\Validator\ValidatorException;
use Lucent\Svelte\Svelte;
use Lucent\ViewModel\ViewModel;
use function Lucent\Response\fail;
use function Lucent\Response\ok;
@@ -27,12 +29,12 @@ class RecordController extends Controller
public function __construct(
private readonly RecordService $recordService,
private readonly AccountService $accountService,
private readonly AuthService $authService,
private readonly ChannelService $channelService,
private readonly Svelte $svelte,
private readonly Query $query,
private readonly Manager $recordManager,
private readonly OperatorRegistry $operatorRegistry
private readonly OperatorRegistry $operatorRegistry,
private readonly ViewModel $viewModel,
)
{
}
@@ -61,10 +63,12 @@ class RecordController extends Controller
], $filter);
$skip = data_get($urlParams, "skip") ?? 0;
$limit = 30;
$records = [];
$graphArray = null;
$graph = $this->query
->filter($arguments)
->notLinked($request->input("notlinked") ?? "")
@@ -133,18 +137,22 @@ class RecordController extends Controller
->filter($arguments)
->limit(-1)
->status(explode(",", $arguments["status_in"]))
->childrenDepth(1)
// ->skip($skip)
->sort($sort)
->run()
->records;
->tree();
header('Content-Type: application/csv');
header('Content-Disposition: attachment; filename="' . $schemaName . '.csv";');
$handle = fopen('php://output', 'w');
$csvRow = ["id", ...array_keys($records[0]->data->toArray())];
$relationColumns = $this->makeCsvRelationColumns($schema);
$csvRow = ["id", ...array_keys($records[0]->data->toArray()),...$relationColumns];
fputcsv($handle, $csvRow, ',');
foreach ($records as $record) {
$csvRow = [$record->id, ...$record->data->toArray()];
$csvRow = array_merge($csvRow,$this->makeCsvRelationColumnValues($schema,$record->_children));
$csvRow = array_values($csvRow);
fputcsv($handle, $csvRow, ',');
}
@@ -153,6 +161,32 @@ class RecordController extends Controller
exit;
}
private function makeCsvRelationColumns($schema):array{
return $schema->fields->filter(fn($f) => get_class($f) === Reference::class)->reduce(function($c,$f){
$c[] = $f->name." id";
$c[] = $f->name." name";
return $c;
},[]);
}
private function makeCsvRelationColumnValues($schema, $children):array{
return $schema->fields->filter(fn($f) => get_class($f) === Reference::class)->reduce(function($c,$f) use($children){
$fieldRecords = data_get($children,$f->name);
if(empty($fieldRecords)){
$c[] = "";
$c[] = "";
}elseif (count($fieldRecords) === 1){
$c[] = data_get($fieldRecords,"0.id");
$c[] = $this->viewModel->getRecordName($fieldRecords[0]);
}else{
$c[] = collect($fieldRecords)->pluck("id")->join("::");
$c[] = collect($fieldRecords)->pluck("data.name")->join("::");
}
return $c;
},[]);
}
public function new(Request $request)
{
if (!in_array($request->input("schema"), $this->accountService->currentWritableSchemas())) {
+72
View File
@@ -0,0 +1,72 @@
<?php
namespace Lucent\Http\Controller;
use App\Http\Controllers\Controller;
use Illuminate\Contracts\View\View;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Lucent\Account\AccountService;
use Lucent\Channel\ChannelService;
use Lucent\LucentException;
use Lucent\Setup\Data\SetupStep;
use Lucent\Setup\Data\SetupStepStatus;
use Lucent\Setup\Setup;
use Lucent\Setup\Step\ComposerStep;
use Lucent\Setup\Step\DatabaseSetupStep;
use Lucent\Setup\Step\IStep;
use Lucent\Setup\Step\LaravelEnvStep;
use Lucent\Setup\Step\LucentConfigStep;
use Lucent\Setup\Step\StorageLinkSetupStep;
use Lucent\Setup\Step\StorageSetupStep;
use Lucent\Svelte\Svelte;
use function Lucent\Response\fail;
use function Lucent\Response\ok;
class SetupController
{
public function __construct(
private readonly AccountService $accountService,
private readonly ChannelService $channelService,
private readonly Svelte $svelte,
)
{
}
public function setup(Request $request): View|RedirectResponse
{
$steps = array_reduce([
new ComposerStep,
new LucentConfigStep,
new LaravelEnvStep,
new StorageSetupStep,
new StorageLinkSetupStep,
new DatabaseSetupStep,
], fn(array $carry, IStep $setupStep) => array_merge($carry, [$setupStep()]), []);
$allSuccess = array_reduce($steps, fn(bool $carry, SetupStep $step) => !$carry ? false : $step->status === SetupStepStatus::SUCCESS, true);
if($allSuccess){
if ($this->accountService->countUsers() > 0) {
return redirect($this->channelService->channel->lucentUrl . "/login");
}
}
return $this->svelte->render(
layout: "account",
view: "setup",
title: "Setup Lucent",
data: [
"steps" => $steps,
"allSuccess" => $allSuccess,
]
);
}
}
+8
View File
@@ -10,6 +10,11 @@ use Lucent\Http\Controller\HomeController;
use Lucent\Http\Controller\MemberController;
use Lucent\Http\Controller\RecordController;
use Lucent\Http\Controller\RevisionController;
use Lucent\Http\Controller\SetupController;
Route::get('/lucent/setup', [SetupController::class, 'setup']);
Route::get('/lfs-{disk}/{any}', [FileController::class, 'fromDisk'])->where('any', '.*');
Route::group([
@@ -17,8 +22,11 @@ Route::group([
'prefix' => "lucent"
], function () {
Route::middleware(['lucent.guest'])->group(function () {
Route::get('/', [AuthController::class, 'login']);
Route::get('/register', [AuthController::class, 'register']);
Route::post('/register', [AuthController::class, 'postRegister']);
Route::get('/login', [AuthController::class, 'login']);
+9 -4
View File
@@ -9,9 +9,13 @@ use Illuminate\Support\ServiceProvider;
use Intervention\Image\ImageManager;
use Lucent\Channel\ChannelService;
use Lucent\Commands\CompileSchemas;
use Lucent\Commands\GenerateCollectionSchema;
use Lucent\Commands\GenerateFileSchema;
use Lucent\Commands\LiveLink;
use Lucent\Commands\RebuildThumbnails;
use Lucent\Commands\RemoveOrphanEdges;
use Lucent\Commands\SetupDatabase;
use Lucent\Commands\UpgradeFiles122;
use Lucent\File\FileService;
use Lucent\File\ImageService;
use Lucent\Query\DatabaseGraph\DatabaseGraph;
@@ -67,25 +71,26 @@ class LucentServiceProvider extends ServiceProvider
$this->loadRoutesFrom(__DIR__ . '/Http/web.php');
$this->loadRoutesFrom(__DIR__ . '/Http/api.php');
$this->loadMigrationsFrom(__DIR__ . '/Database/migrations');
if ($this->app->runningInConsole()) {
$this->commands([
CompileSchemas::class,
RebuildThumbnails::class,
LiveLink::class,
RemoveOrphanEdges::class,
SetupDatabase::class,
GenerateCollectionSchema::class,
GenerateFileSchema::class,
UpgradeFiles122::class,
]);
}
View::share('manifest', $manifest);
View::share('image', app()->make(ImageService::class));
View::share('file', app()->make(FileService::class));
Blade::anonymousComponentPath(__DIR__ . '../front/views/components', "lucent");
$this->publishes([
__DIR__ . '/Config/main.php' => config_path('lucent.php'),
]);
],"lucent-config");
$this->publishes([
__DIR__ . '/../front/dist' => public_path('vendor/lucent/dist'),
+1 -1
View File
@@ -23,7 +23,7 @@ readonly class GreaterThan implements IBuilderConverter
return $builder->orWhere($this->argument->field, ">", $this->formatValue());
}
private function formatValue(): int|float
private function formatValue(): int|float|string
{
$value = trim($this->argument->value);
if (is_numeric($value)) {
@@ -23,7 +23,7 @@ readonly class GreaterThanEquals implements IBuilderConverter
return $builder->orWhere($this->argument->field, ">=", $this->formatValue());
}
private function formatValue(): int|float
private function formatValue(): int|float|string
{
$value = trim($this->argument->value);
if (is_numeric($value)) {
+1 -1
View File
@@ -23,7 +23,7 @@ readonly class LessThan implements IBuilderConverter
return $builder->orWhere($this->argument->field, "<", $this->formatValue());
}
private function formatValue(): int|float
private function formatValue(): int|float|string
{
$value = trim($this->argument->value);
if (is_numeric($value)) {
@@ -23,7 +23,7 @@ readonly class LessThanEquals implements IBuilderConverter
return $builder->orWhere($this->argument->field, "<=", $this->formatValue());
}
private function formatValue(): int|float
private function formatValue(): int|float|string
{
$value = trim($this->argument->value);
if (is_numeric($value)) {
@@ -3,6 +3,7 @@
namespace Lucent\Query\DatabaseGraph;
use Illuminate\Support\Facades\DB;
use Lucent\Database\Database;
use Lucent\Query\QueryOptions;
class PgsqlDatabaseGraph implements DatabaseGraph
@@ -13,7 +14,7 @@ class PgsqlDatabaseGraph implements DatabaseGraph
*/
public function getChildren(array $ids, QueryOptions $options): array
{
$subquery = DB::table('edges AS g')
$subquery = Database::make()->table('edges AS g')
->select(DB::raw('g.source,g.target,g.rank,"g"."sourceSchema","g"."targetSchema",g.field, 1 as depth '))
->whereIn('source', $ids);
@@ -23,14 +24,14 @@ class PgsqlDatabaseGraph implements DatabaseGraph
$subquery->limit($options->childrenLimit)
->unionAll(
DB::table(DB::raw("edges AS g, search_graph AS sg "))
Database::make()->table(DB::raw("edges AS g, search_graph AS sg "))
->selectRaw('g.source,g.target,g.rank,"g"."sourceSchema","g"."targetSchema",g.field,sg.depth + 1 as depth')
->whereRaw("g.source = sg.target")
->where("depth", "<", $options->childrenDepth)
->orderBy("rank")
);
return DB::table('search_graph')
return Database::make()->table('search_graph')
// ->select(DB::raw("*, 1 as depth "))
->withRecursiveExpression('search_graph', $subquery)
->get()->toArray();
@@ -41,19 +42,19 @@ class PgsqlDatabaseGraph implements DatabaseGraph
*/
public function getParents(array $ids, QueryOptions $options): array
{
$subquery = DB::table('edges AS g')
$subquery = Database::make()->table('edges AS g')
->select(DB::raw('g.source,g.target,g.rank,"g"."sourceSchema","g"."targetSchema",g.field, 1 as depth '))
->limit($options->parentsLimit)
->whereIn('g.target', $ids)
->unionAll(
DB::table(DB::raw("edges AS g, search_graph AS sg "))
Database::make()->table(DB::raw("edges AS g, search_graph AS sg "))
->selectRaw('g.source,g.target,g.rank,"g"."sourceSchema","g"."targetSchema",g.field,sg.depth + 1 as depth')
->whereRaw("g.target = sg.source")
->where("depth", "<", $options->parentsDepth)
->orderBy("rank")
);
return DB::table('search_graph')
return Database::make()->table('search_graph')
// ->select(DB::raw('sg.source,sg.target,sg.rank,sg."sourceSchema",sg."targetSchema",sg.field,sg.depth'))
->withRecursiveExpression('search_graph', $subquery)
->get()->toArray();
@@ -3,6 +3,7 @@
namespace Lucent\Query\DatabaseGraph;
use Illuminate\Support\Facades\DB;
use Lucent\Database\Database;
use Lucent\Query\QueryOptions;
class SqliteDatabaseGraph implements DatabaseGraph
@@ -12,7 +13,7 @@ class SqliteDatabaseGraph implements DatabaseGraph
*/
public function getChildren(array $ids, QueryOptions $options): array
{
$subquery = DB::table('edges AS g')
$subquery = Database::make()->table('edges AS g')
->select(DB::raw('g.source,g.target,g.rank,g.sourceSchema,g.targetSchema,g.field, 1 as depth '))
->whereIn('source', $ids);
@@ -22,14 +23,14 @@ class SqliteDatabaseGraph implements DatabaseGraph
$subquery->limit($options->childrenLimit)
->unionAll(
DB::table(DB::raw("edges AS g, search_graph AS sg "))
Database::make()->table(DB::raw("edges AS g, search_graph AS sg "))
->selectRaw('g.source,g.target,g.rank,"g"."sourceSchema","g"."targetSchema",g.field,sg.depth + 1 as depth')
->whereRaw("g.source = sg.target")
->where("depth", "<", $options->childrenDepth)
->orderBy("rank")
);
return DB::table('search_graph')
return Database::make()->table('search_graph')
// ->select(DB::raw("*, 1 as depth "))
->withRecursiveExpression('search_graph', $subquery)
->get()->toArray();
@@ -40,19 +41,19 @@ class SqliteDatabaseGraph implements DatabaseGraph
*/
public function getParents(array $ids, QueryOptions $options): array
{
$subquery = DB::table('edges AS g')
$subquery = Database::make()->table('edges AS g')
->select(DB::raw('g.source,g.target,g.rank,"g"."sourceSchema","g"."targetSchema",g.field, 1 as depth '))
->limit($options->parentsLimit)
->whereIn('g.target', $ids)
->unionAll(
DB::table(DB::raw("edges AS g, search_graph AS sg "))
Database::make()->table(DB::raw("edges AS g, search_graph AS sg "))
->selectRaw('g.source,g.target,g.rank,"g"."sourceSchema","g"."targetSchema",g.field,sg.depth + 1 as depth')
->whereRaw("g.target = sg.source")
->where("depth", "<", $options->parentsDepth)
->orderBy("rank")
);
return DB::table('search_graph')
return Database::make()->table('search_graph')
// ->select(DB::raw('sg.source,sg.target,sg.rank,sg."sourceSchema",sg."targetSchema",sg.field,sg.depth'))
->withRecursiveExpression('search_graph', $subquery)
->get()->toArray();
+2 -1
View File
@@ -5,6 +5,7 @@ namespace Lucent\Query;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Facades\DB;
use Lucent\Database\Database;
use Lucent\Query\BuilderConverter\BuilderConverter;
use Lucent\Query\Filter\AndFilter;
use Lucent\Query\Filter\Argument;
@@ -58,7 +59,7 @@ final class FilterParser
}
$targetIds = collect($graph->records)->pluck("id");
$sourceIds = DB::table("edges")->whereIn("target", $targetIds)->where("field", $k)->get()->pluck("source");
$sourceIds = Database::make()->table("edges")->whereIn("target", $targetIds)->where("field", $k)->get()->pluck("source");
return array_merge($c, $sourceIds->toArray());
}, []);
+7 -4
View File
@@ -3,7 +3,7 @@
namespace Lucent\Query;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Facades\DB;
use Lucent\Database\Database;
use Lucent\Edge\Edge;
use Lucent\Primitive\Collection;
use Lucent\Query\DatabaseGraph\DatabaseGraph;
@@ -67,7 +67,7 @@ final class Query
$edgesIds = collect($resultParentSourceTargetIds)->merge($resultChildrenEdgesTargetIds)->unique()->values()->toArray();
$edgeRecords = [];
if (!empty($edgesIds)) {
$edgeRecords = DB::table('records')
$edgeRecords = Database::make()->table('records')
->whereIn("id", $edgesIds)
->whereIn("status", $this->options->status)
->get()->toArray();
@@ -78,6 +78,7 @@ final class Query
// ->toArray();
$formattedRecords = $this->formatRecords($resultsRecordsUnique, $resultChildrenEdges, $resultParentEdges);
$this->reset();
return $formattedRecords;
@@ -94,7 +95,9 @@ final class Query
$queryRecords = collect($records)->map(function ($recordData) {
$record = Record::fromDB($recordData);
$record->data = $this->inputFormatter->fill($record->schema, $record->data);
$queryRecord = QueryRecord::fromRecord($record);
$queryRecord->isRoot = data_get($recordData, "isRoot") === true;
return $queryRecord;
@@ -142,7 +145,7 @@ final class Query
private function mainQuery(): array
{
$query = DB::table("records");
$query = Database::make()->table("records");
$query = $this->parseFilters($query);
$query = $this->findNotLinked($query);
@@ -189,7 +192,7 @@ final class Query
function runWithCount(): Graph
{
$query = DB::table("records");
$query = Database::make()->table("records");
$query = $this->parseFilters($query);
$query = $this->findNotLinked($query);
$graph = $this->run();
+2
View File
@@ -10,6 +10,7 @@ class FileData
public readonly string $originalName,
public readonly string $mime,
public readonly string $path,
public readonly string $disk,
public readonly int $size,
public readonly int $width,
public readonly int $height,
@@ -24,6 +25,7 @@ class FileData
originalName: data_get($data, "originalName"),
mime: data_get($data, "mime"),
path: data_get($data, "path"),
disk: data_get($data, "disk", "lucent"),
size: data_get($data, "size"),
width: data_get($data, "width"),
height: data_get($data, "height"),
+3
View File
@@ -16,8 +16,11 @@ class InputFormatter
public function fill(string $schemaName, RecordData $input): RecordData
{
$schema = $this->channelService->getSchema($schemaName)->get();
$data = $schema->fields->reduce(fn(array $carry, FieldInterface $field) => $field->format($input->toArray(), $carry), []);
return new RecordData($data);
}
+1 -1
View File
@@ -54,7 +54,7 @@ class Record implements JsonSerializable
$file = json_decode($data->_file, true);
if (!empty($file)) {
$file = new FileData(...$file);
$file = FileData::fromArray($file);
} else {
$file = null;
}
+9 -9
View File
@@ -2,7 +2,7 @@
namespace Lucent\Record;
use Illuminate\Support\Facades\DB;
use Lucent\Database\Database;
class RecordRepo
{
@@ -14,7 +14,7 @@ class RecordRepo
{
$recordToDB = $record->toDB();
DB::table("records")->insert($recordToDB);
Database::make()->table("records")->insert($recordToDB);
}
@@ -23,7 +23,7 @@ class RecordRepo
*/
public static function updateStatusBulk(Status $status, array $ids): void
{
DB::table("records")->whereIn("id", $ids)->update([
Database::make()->table("records")->whereIn("id", $ids)->update([
'status' => $status->value
]);
}
@@ -31,7 +31,7 @@ class RecordRepo
public static function update(Record $record): void
{
$recordToDB = $record->toDB();
DB::table("records")->where("id", $record->id)->update($recordToDB);
Database::make()->table("records")->where("id", $record->id)->update($recordToDB);
}
@@ -43,17 +43,17 @@ class RecordRepo
): void
{
DB::table("records")->whereIn("id", $ids)->delete();
DB::table("edges")->whereIn("source", $ids)->delete();
DB::table("edges")->whereIn("target", $ids)->delete();
DB::table("revisions")->whereIn("recordId", $ids)->delete();
Database::make()->table("records")->whereIn("id", $ids)->delete();
Database::make()->table("edges")->whereIn("source", $ids)->delete();
Database::make()->table("edges")->whereIn("target", $ids)->delete();
Database::make()->table("revisions")->whereIn("recordId", $ids)->delete();
}
public function deleteTrashedBySchema(
string $schemaName,
): void
{
$ids = DB::table("records")
$ids = Database::make()->table("records")
->where("schema", $schemaName)
->where("status", Status::TRASHED->value)
->get()->pluck("id")->toArray();
+7 -7
View File
@@ -2,7 +2,7 @@
namespace Lucent\Revision;
use Illuminate\Support\Facades\DB;
use Lucent\Database\Database;
use Lucent\Edge\Edge;
use Lucent\Primitive\Collection;
use Lucent\Record\FileData;
@@ -19,7 +19,7 @@ class RevisionRepo
public function create(Revision $revision): string
{
$revisionDB = $this->toDB($revision);
DB::table($this->table)->insert($revisionDB);
Database::make()->table($this->table)->insert($revisionDB);
return $revision->id;
}
@@ -29,7 +29,7 @@ class RevisionRepo
**/
public function getByRecordId(string $rid): Collection
{
$revisions = DB::table($this->table)
$revisions = Database::make()->table($this->table)
->where("recordId", $rid)
->get()
->map([$this, 'fromDB'])
@@ -41,7 +41,7 @@ class RevisionRepo
public function cleanupRecord(string $rid, int $numKeep): void
{
$revisionIds = DB::table($this->table)
$revisionIds = Database::make()->table($this->table)
->where("recordId", $rid)
->orderBy("_sys->version", "desc")
->limit(100)
@@ -49,7 +49,7 @@ class RevisionRepo
->get()
->pluck("id");
DB::table($this->table)
Database::make()->table($this->table)
->whereIn("id", $revisionIds)
->delete();
}
@@ -61,7 +61,7 @@ class RevisionRepo
public function getByRecordIdAndVersion(string $rid, int $version): Option
{
$res = DB::table($this->table)
$res = Database::make()->table($this->table)
->where("recordId", $rid)
->where('_sys->version', $version)->first();
@@ -90,7 +90,7 @@ class RevisionRepo
$file = json_decode($data->_file, true);
if (!empty($file)) {
$file = new FileData(...$file);
$file = FileData::fromArray($file);
} else {
$file = null;
}
+1
View File
@@ -17,6 +17,7 @@ class FilesSchema implements Schema
public string $name,
public string $label,
public Collection $fields,
public string $disk,
public string $path,
public array $groups,
public bool $isEntry = false,
+1
View File
@@ -36,6 +36,7 @@ class SchemaService
name: $schemaArr["name"],
label: $schemaArr["label"],
fields: (new Collection($schemaArr["fields"]))->map([$this, 'mapFields']),
disk: $schemaArr["disk"] ?? "lucent",
path: $schemaArr["path"] ?? $schemaArr["name"],
groups: $schemaArr["groups"] ?? [],
isEntry: $schemaArr["isEntry"] ?? false,
+1
View File
@@ -18,6 +18,7 @@ class Markdown implements FieldInterface, RequiredInterface
public bool $required = false,
public bool $nullable = false,
public string $default = "",
public array $collections = [],
public string $help = "",
public ?int $min = null,
public ?int $max = null,
+24
View File
@@ -0,0 +1,24 @@
<?php
namespace Lucent\Setup\Data;
class SetupStep
{
public function __construct(
public string $name,
public string $instructions,
public SetupStepStatus $status,
)
{
}
public static function makeSuccess(string $name, string $instructions): self
{
return new self($name, $instructions, SetupStepStatus::SUCCESS);
}
public static function makeFail(string $name, string $instructions): self
{
return new self($name, $instructions, SetupStepStatus::FAIL);
}
}
+9
View File
@@ -0,0 +1,9 @@
<?php
namespace Lucent\Setup\Data;
enum SetupStepStatus: string
{
case SUCCESS = "success";
case FAIL = "fail";
}
+41
View File
@@ -0,0 +1,41 @@
<?php
namespace Lucent\Setup\Step;
use Lucent\Setup\Data\SetupStep;
class ComposerStep implements IStep
{
public function __invoke(): SetupStep
{
$composerFile = json_decode(file_get_contents(base_path("composer.json")), true);
$postAutoloadDumpList = data_get($composerFile, "scripts.post-autoload-dump", []);
$name = "Composer File";
$instructions = <<<EOD
# Append this line in post-autoload-dump in your composer.json.:
"@php artisan vendor:publish --tag=lucent --force"
example:
{
"scripts": {
"post-autoload-dump": [
"@php artisan vendor:publish --tag=lucent --force"
]
}
}
EOD;
return match (in_array("@php artisan vendor:publish --tag=lucent --force", $postAutoloadDumpList)) {
true => SetupStep::makeSuccess($name, $instructions),
false => SetupStep::makeFail($name, $instructions),
};
}
}
+60
View File
@@ -0,0 +1,60 @@
<?php
namespace Lucent\Setup\Step;
use Illuminate\Database\QueryException;
use Lucent\Database\Database;
use Lucent\Setup\Data\SetupStep;
class DatabaseSetupStep implements IStep
{
public function __invoke(): SetupStep
{
$name = "Database Connection";
$databaseConnectionName = config("lucent.database");
$databaseConfig = config('database.connections.'.$databaseConnectionName);
if (empty($databaseConfig)) {
$instructions = <<<EOD
# You need to setup a database connection for $databaseConnectionName in database.php config. You can choose either sqlite or pgsql
# example:
'connections' => [
'$databaseConnectionName' => [
'driver' => 'sqlite',
'url' => env('LUCENT_DATABASE_URL'),
'database' => env('LUCENT_DB_DATABASE', database_path('database.sqlite')),
'prefix' => '',
'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true),
],
EOD;
return SetupStep::makeFail($name, $instructions);
}
if(!in_array($driver = $databaseConfig["driver"],["sqlite","pgsql"])){
$instructions = <<<EOD
You can choose either sqlite or pgsql. You current config is: $driver
EOD;
return SetupStep::makeFail($name, $instructions);
}
try {
Database::make()->table("records")->get();
} catch (QueryException $e) {
$instructions = <<<EOD
# Make sure you run:
php artisan lucent:setup-db
EOD;
return SetupStep::makeFail($name, $instructions);
}
return SetupStep::makeSuccess($name, "Database Connection successfully created");
}
}
+11
View File
@@ -0,0 +1,11 @@
<?php
namespace Lucent\Setup\Step;
use Lucent\Setup\Data\SetupStep;
interface IStep
{
public function __invoke(): SetupStep;
}
+23
View File
@@ -0,0 +1,23 @@
<?php
namespace Lucent\Setup\Step;
use Lucent\Setup\Data\SetupStep;
class LaravelEnvStep implements IStep
{
public function __invoke(): SetupStep
{
$name = "Laravel ENV";
$instructions = <<<EOD
# Make sure in your env that you APP_URL is the same as the one in your browser right now. Or the other way round.
EOD;
return match (config("lucent.url") === request()->getSchemeAndHttpHost()) {
true => SetupStep::makeSuccess($name, $instructions),
false => SetupStep::makeFail($name, $instructions),
};
}
}
+32
View File
@@ -0,0 +1,32 @@
<?php
namespace Lucent\Setup\Step;
use Lucent\Setup\Data\SetupStep;
class LucentConfigStep implements IStep
{
public function __invoke(): SetupStep
{
$lucentConfig = config("lucent.schemas_path");
$name = "Generate Lucent config";
$instructions = <<<EOD
# Run the following command to generate the configuration file:
php artisan vendor:publish --tag=lucent-config
# A lucent.php file will be created in your config folder
EOD;
return match (!empty($lucentConfig)) {
true => SetupStep::makeSuccess($name, $instructions),
false => SetupStep::makeFail($name, $instructions),
};
}
}
+39
View File
@@ -0,0 +1,39 @@
<?php
namespace Lucent\Setup\Step;
use Lucent\Setup\Data\SetupStep;
class StorageLinkSetupStep implements IStep
{
public function __invoke(): SetupStep
{
$storageDriver = config("filesystems.disks.lucent.driver");
$name = "Storage Link";
$instructions = <<<EOD
# If you have chosen to store your file locally then you have to generate a link to the public folder
# run:
php artisan storage:link
EOD;
if($storageDriver !== "local"){
return SetupStep::makeSuccess($name, $instructions);
}
$storageLinks = config("filesystems.links");
$allLinksExist = array_reduce(array_keys($storageLinks),fn(bool $carry, string $link) => !$carry ? false : is_link($link) , true);
return match ($allLinksExist) {
true => SetupStep::makeSuccess($name, $instructions),
false => SetupStep::makeFail($name, $instructions),
};
}
}
+42
View File
@@ -0,0 +1,42 @@
<?php
namespace Lucent\Setup\Step;
use Lucent\Setup\Data\SetupStep;
class StorageSetupStep implements IStep
{
public function __invoke(): SetupStep
{
$storage = config("filesystems.disks.lucent");
$name = "Storage setup";
$instructions = <<<EOD
# You can use your local filesystem or s3 compatible storage. Lucent expects you to have a valid configuration inside config/filesystems.php
# example:
return [
'disks' => [
'lucent' => [
'driver' => 'local',
'root' => storage_path('app/public'),
'url' => env('APP_URL').'/storage',
'visibility' => 'public',
'throw' => true,
],
],
];
EOD;
return match (!empty($storage)) {
true => SetupStep::makeSuccess($name, $instructions),
false => SetupStep::makeFail($name, $instructions),
};
}
}
+1 -1
View File
@@ -21,7 +21,7 @@ class ViewModel
public function getRecordName(QueryRecord $record): string
{
$schema = $this->channelService->getSchema($record->schema)->get();
if (empty($schema->titleTemplate)) {
if (empty($schema->cardTitle)) {
$title = match (get_class($schema)) {
CollectionSchema::class => $record->data[$schema->fields->filter(fn(FieldInterface $f) => $f->info->name === "text")->first()->name],
FilesSchema::class => $record->_file->path,
+18
View File
@@ -45,3 +45,21 @@ if (!function_exists('schemas_path')) {
return storage_path("lucent/lucent.schemas.json");
}
}
if (!function_exists('lucent_file')) {
function lucent_file(\Lucent\Record\QueryRecord $record): string
{
$path = $record->_file->path;
return app()->make(\Lucent\Channel\ChannelService::class)->channel->disks[$record->_file->disk] ."/". $path;
}
}
if (!function_exists('lucent_image')) {
function lucent_image(\Lucent\Record\QueryRecord $record, string $template): string
{
$path = $record->_file->path;
return app()->make(\Lucent\Channel\ChannelService::class)->channel->disks[$record->_file->disk] . "/templates/$template/$path";
}
}