Compare commits
34 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 57b0727788 | |||
| 58b047edd2 | |||
| a78b699a5e | |||
| e910ae9878 | |||
| 362c649d36 | |||
| 852c4d608d | |||
| aa59e55a41 | |||
| 348bad80e0 | |||
| f0d4686141 | |||
| a482ab3c7e | |||
| c580882ec0 | |||
| 2cf8379cbe | |||
| c39ec469df | |||
| 232fcc8845 | |||
| 9d5d4dd930 | |||
| c507dc6031 | |||
| 843f560710 | |||
| 7574d67d80 | |||
| 19931cb4d1 | |||
| 6458c1e71d | |||
| 63232585ab | |||
| 6d15591601 | |||
| 32c8378020 | |||
| d0cd8228cc | |||
| c45a3847f8 | |||
| c0b3878674 | |||
| f868219981 | |||
| 8ac0567e66 | |||
| 02f8f5970a | |||
| 0cd4e08716 | |||
| cf3d621587 | |||
| 6fc0a65b6f | |||
| a73ee21568 | |||
| ff54bcc2ef |
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"$schema": "/phpactor.schema.json",
|
||||
"language_server_phpstan.enabled": false
|
||||
}
|
||||
+2
-1
@@ -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
@@ -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"
|
||||
|
||||
+110
-110
File diff suppressed because one or more lines are too long
Vendored
-1
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+2
-2
@@ -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,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}/>
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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}"/>`
|
||||
|
||||
@@ -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 = ``;
|
||||
} 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(() => {
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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
|
||||
});
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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{
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}, []);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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]
|
||||
);
|
||||
|
||||
@@ -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.");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
@@ -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')),
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
};
|
||||
@@ -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');
|
||||
}
|
||||
};
|
||||
@@ -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
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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())) {
|
||||
|
||||
@@ -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,
|
||||
]
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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,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'),
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
@@ -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();
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace Lucent\Setup\Data;
|
||||
|
||||
enum SetupStepStatus: string
|
||||
{
|
||||
case SUCCESS = "success";
|
||||
case FAIL = "fail";
|
||||
}
|
||||
@@ -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),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace Lucent\Setup\Step;
|
||||
|
||||
use Lucent\Setup\Data\SetupStep;
|
||||
|
||||
interface IStep
|
||||
{
|
||||
public function __invoke(): SetupStep;
|
||||
|
||||
}
|
||||
@@ -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),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user