boboko lulnar

This commit is contained in:
2026-04-20 21:07:35 +03:00
parent 57b0727788
commit 4a7eb217a1
32 changed files with 1350 additions and 858 deletions
+4 -7
View File
@@ -28,15 +28,15 @@
export let axios; export let axios;
export let readableSchemas; export let readableSchemas;
setContext("axios", axios); setContext("axios", axios);
setContext("channel", channel); setContext("channel", channel);
setContext("readableSchemas", channel.schemas.filter((s) => readableSchemas.includes(s.name))); setContext(
"readableSchemas",
channel.schemas.filter((s) => readableSchemas.includes(s.name)),
);
setContext("user", user); setContext("user", user);
</script> </script>
<div class="main-wrapper"> <div class="main-wrapper">
<div class="sidebar-content"> <div class="sidebar-content">
<Navbar schema={data.schema} /> <Navbar schema={data.schema} />
@@ -46,6 +46,3 @@
<svelte:component this={components[view]} {title} {...data} /> <svelte:component this={components[view]} {title} {...data} />
</div> </div>
</div> </div>
+1 -6
View File
@@ -1,7 +1,6 @@
<script> <script>
import { getContext, onMount } from "svelte"; import { getContext, onMount } from "svelte";
import RecordRow from "./RecordRow.svelte" import RecordRow from "./RecordRow.svelte";
const channel = getContext("channel"); const channel = getContext("channel");
let records = []; let records = [];
@@ -21,10 +20,8 @@
}); });
</script> </script>
<h3 class="header-small mb-4 mt-5">Latest Content changes</h3> <h3 class="header-small mb-4 mt-5">Latest Content changes</h3>
{#if records.length > 0} {#if records.length > 0}
<div class="table"> <div class="table">
<table class=""> <table class="">
<tbody> <tbody>
@@ -36,6 +33,4 @@
</tbody> </tbody>
</table> </table>
</div> </div>
{/if} {/if}
+4 -5
View File
@@ -5,10 +5,8 @@
const channel = getContext("channel"); const channel = getContext("channel");
const user = getContext("user"); const user = getContext("user");
console.log( channel.commands)
</script> </script>
<div class="top-nav"> <div class="top-nav">
<a class="top-nav-item" href="{channel.lucentUrl}/members">Members</a> <a class="top-nav-item" href="{channel.lucentUrl}/members">Members</a>
@@ -16,10 +14,12 @@
<Dropdown> <Dropdown>
<div slot="button">Actions</div> <div slot="button">Actions</div>
{#each channel.commands as command} {#each channel.commands as command}
<a href="{channel.lucentUrl}/command-report/{command.signature}" class="top-nav-item">{command.name}</a> <a
href="{channel.lucentUrl}/command-report/{command.signature}"
class="top-nav-item">{command.name}</a
>
{/each} {/each}
</Dropdown> </Dropdown>
{/if} {/if}
<!-- <div>--> <!-- <div>-->
<!-- <form method="GET">--> <!-- <form method="GET">-->
@@ -31,4 +31,3 @@
<Avatar side="28" name={user.name} /> <Avatar side="28" name={user.name} />
</a> </a>
</div> </div>
+2 -6
View File
@@ -1,11 +1,9 @@
<script> <script>
import Icon from "../common/Icon.svelte" import Icon from "../common/Icon.svelte";
export let step; export let step;
</script> </script>
<div class="step step-{step.status}"> <div class="step step-{step.status}">
<div class="step-icon"> <div class="step-icon">
{#if step.status === "success"} {#if step.status === "success"}
@@ -17,12 +15,10 @@
<div style="width:100%"> <div style="width:100%">
<h4>{step.name}</h4> <h4>{step.name}</h4>
<details> <details>
<summary>Instuctions</summary> <summary>Instructions</summary>
<code class="instructions">{step.instructions}</code> <code class="instructions">{step.instructions}</code>
</details> </details>
</div> </div>
</div> </div>
<style> <style>
+1 -9
View File
@@ -7,15 +7,11 @@ use Lucent\Primitive\Collection;
readonly class AccountService readonly class AccountService
{ {
public function __construct( public function __construct(
private AuthService $authService, private AuthService $authService,
private ChannelService $channelService, private ChannelService $channelService,
private UserRepo $userRepo, private UserRepo $userRepo,
) ) {}
{
}
/** /**
* @return Collection<User> * @return Collection<User>
@@ -25,13 +21,11 @@ readonly class AccountService
return $this->userRepo->all(); return $this->userRepo->all();
} }
public function countUsers(): int public function countUsers(): int
{ {
return $this->userRepo->count(); return $this->userRepo->count();
} }
/** /**
* @return Collection<UserProfile> * @return Collection<UserProfile>
*/ */
@@ -54,6 +48,4 @@ readonly class AccountService
$roles = $this->authService->currentUserRoles(); $roles = $this->authService->currentUserRoles();
return $this->channelService->schemasWritableByRoles($roles); return $this->channelService->schemasWritableByRoles($roles);
} }
} }
+15 -198
View File
@@ -2,235 +2,52 @@
namespace Lucent\Account; namespace Lucent\Account;
use Carbon\Carbon;
use Illuminate\Contracts\Session\Session;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Str;
use Lucent\Channel\ChannelService;
use Lucent\LucentException; use Lucent\LucentException;
use Lucent\Mail\LoginMail;
readonly class AuthService interface AuthService
{ {
public function currentUserId(): ?string;
public function __construct( public function currentUserRoles(): array;
private ChannelService $channelService, public function getCurrentUser(): User;
private UserRepo $userRepo,
public Session $session,
)
{
} public function isLoggedIn(): bool;
public function currentUserId(): ?string
{
if (app()->runningInConsole()) {
return config("lucent.systemUserId");
} elseif(request()->segment(1) !== "lucent") {
return config("lucent.systemUserId");
} else {
return $this->session->get("user.id");
}
}
public
function currentUserRoles(): array
{
return $this->session->get("user.roles") ?? [];
}
public
function isLoggedIn(): bool
{
return !empty($this->currentUserId());
}
/** /**
* @throws LucentException * @throws LucentException
*/ */
public public function login(string $email, string $token): void;
function login(string $email, string $token): void
{
$user = $this->userRepo->findByEmail(new Email($email)); public function refreshSession();
if ($user->isEmpty()) { public function create(string $name, string $email, array $roles): User;
throw new LucentException("Your account was not found");
}
if ($user->get()->isRemoved()) {
throw new LucentException("Your account is not active");
}
if ($user->get()->mailToken !== $token) {
throw new LucentException("Token has expired or is invalid");
}
if (Carbon::parse($user->get()->loggedInAt)->lte(Carbon::now()->subHours(1))) {
throw new LucentException("Token has expired.");
}
$newUser = $user->get();
$newUser->updatedAt = Carbon::now()->toJson();
$newUser->mailToken = null;
$this->userRepo->update($newUser);
$this->session->put(["user" => $user->get()->safe()]);
}
public
function refreshSession()
{
$user = $this->userRepo->findById($this->currentUserId());
if ($user->isEmpty()) {
throw new LucentException("Your account was not found");
}
if ($user->get()->isRemoved()) {
throw new LucentException("Your account is not active");
}
$newUser = $user->get();
$this->session->put(["user" => $user->get()->safe()]);
}
public
function create(string $name, string $email, array $roles): User
{
$user = new User(
id: (string)Str::uuid(),
name: new Name($name),
email: new Email($email),
roles: $this->validateRoles($roles),
createdAt: Carbon::now()->toJson(),
updatedAt: Carbon::now()->toJson(),
loggedInAt: Carbon::now()->toJson(),
mailToken: Token::new(32),
);
$this->userRepo->insert($user);
return $user;
}
public function sendLoginEmail(string $email): void
{
$emailAddress = (new Email($email));
$user = $this->userRepo->findByEmail($emailAddress);
if ($user->isEmpty()) {
return;
}
if ($user->get()->isRemoved()) {
return;
}
$newToken = $this->userRepo->updateLoginToken($user->get()->id);
Mail::to($email)->send(
new LoginMail(
$email,
$newToken,
$this->channelService->channel->lucentUrl
)
);
}
public function sendLoginEmail(string $email): void;
/** /**
* @throws LucentException * @throws LucentException
*/ */
public public function changeRoles(string $userId, array $roles): void;
function changeRoles(string $userId, array $roles): void
{
$user = $this->userRepo->findById($userId);
if ($user->isEmpty()) {
throw new LucentException("User not found");
}
$newUser = $user->get();
$newUser->roles = $this->validateRoles($roles);
$newUser->updatedAt = Carbon::now()->toJson();
$this->userRepo->update($newUser);
}
/** /**
* @throws LucentException * @throws LucentException
*/ */
public public function updateName(string $name): void;
function updateName(string $name): void
{
$name = (new Name($name));
$this->userRepo->updateName($this->currentUserId(), $name);
$user = $this->userRepo->findById($this->currentUserId());
$this->session->put(["user" => $user->get()->safe()]);
}
/** /**
* @throws LucentException * @throws LucentException
*/ */
public public function updateEmail(string $email): void;
function updateEmail(string $email): void
{
$email = (new Email($email));
$user = $this->userRepo->findByEmail($email);
if ($user->isDefined()) {
throw new LucentException("Email already assigned to user");
}
$this->userRepo->updateEmail($this->currentUserId(), $email);
$user = $this->userRepo->findById($this->currentUserId());
$this->session->put(["user" => $user->get()->safe()]);
}
/** /**
* @throws LucentException * @throws LucentException
*/ */
public public function invite(string $name, string $email, array $roles): User;
function invite(
string $name,
string $email,
array $roles
): User
{
$user = $this->create($name, $email, $roles);
$this->sendLoginEmail($user->email);
return $user;
}
/** /**
* @throws LucentException * @throws LucentException
*/ */
public public function registerAdmin(string $name, string $email): User;
function registerAdmin(
string $name,
string $email
): User
{
$user = $this->invite($name, $email, ["admin"]);
$this->sendLoginEmail($user->email);
return $user;
}
public
function validateRoles(array $roles): array
{
return collect($roles)
->filter(fn(string $role) => in_array($role, $this->channelService->channel->roles))
->unique()
->values()
->toArray();
}
public function validateRoles(array $roles): array;
} }
+224
View File
@@ -0,0 +1,224 @@
<?php
namespace Lucent\Account;
use Carbon\Carbon;
use Illuminate\Contracts\Session\Session;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Str;
use Lucent\Channel\ChannelService;
use Lucent\LucentException;
use Lucent\Mail\LoginMail;
use Filament\Facades\Filament;
readonly class AuthServiceLucent implements AuthService
{
public function __construct(
private ChannelService $channelService,
private UserRepo $userRepo,
public Session $session,
) {}
public function currentUserId(): ?string
{
if (app()->runningInConsole()) {
return config("lucent.systemUserId");
} elseif (request()->segment(1) !== "lucent") {
return config("lucent.systemUserId");
}
return $this->session->get("user.id");
}
public function currentUserRoles(): array
{
return $this->session->get("user.roles") ?? [];
}
public function getCurrentUser(): User
{
return new User(
id: $this->session->get("user.id"),
name: new Name($this->session->get("user.name")),
email: new Email($this->session->get("user.email")),
roles: $this->session->get("user.roles"),
createdAt: $this->session->get("user.createdAt"),
updatedAt: $this->session->get("user.updatedAt"),
loggedInAt: null,
mailToken: null,
);
}
public function isLoggedIn(): bool
{
return !empty($this->currentUserId());
}
/**
* @throws LucentException
*/
public function login(string $email, string $token): void
{
$user = $this->userRepo->findByEmail(new Email($email));
if ($user->isEmpty()) {
throw new LucentException("Your account was not found");
}
if ($user->get()->isRemoved()) {
throw new LucentException("Your account is not active");
}
if ($user->get()->mailToken !== $token) {
throw new LucentException("Token has expired or is invalid");
}
if (
Carbon::parse($user->get()->loggedInAt)->lte(
Carbon::now()->subHours(1),
)
) {
throw new LucentException("Token has expired.");
}
$newUser = $user->get();
$newUser->updatedAt = Carbon::now()->toJson();
$newUser->mailToken = null;
$this->userRepo->update($newUser);
$this->session->put(["user" => $user->get()->safe()]);
}
public function refreshSession()
{
$user = $this->userRepo->findById($this->currentUserId());
if ($user->isEmpty()) {
throw new LucentException("Your account was not found");
}
if ($user->get()->isRemoved()) {
throw new LucentException("Your account is not active");
}
$newUser = $user->get();
$this->session->put(["user" => $user->get()->safe()]);
}
public function create(string $name, string $email, array $roles): User
{
$user = new User(
id: (string) Str::uuid(),
name: new Name($name),
email: new Email($email),
roles: $this->validateRoles($roles),
createdAt: Carbon::now()->toJson(),
updatedAt: Carbon::now()->toJson(),
loggedInAt: Carbon::now()->toJson(),
mailToken: Token::new(32),
);
$this->userRepo->insert($user);
return $user;
}
public function sendLoginEmail(string $email): void
{
$emailAddress = new Email($email);
$user = $this->userRepo->findByEmail($emailAddress);
if ($user->isEmpty()) {
return;
}
if ($user->get()->isRemoved()) {
return;
}
$newToken = $this->userRepo->updateLoginToken($user->get()->id);
Mail::to($email)->send(
new LoginMail(
$email,
$newToken,
$this->channelService->channel->lucentUrl,
),
);
}
/**
* @throws LucentException
*/
public function changeRoles(string $userId, array $roles): void
{
$user = $this->userRepo->findById($userId);
if ($user->isEmpty()) {
throw new LucentException("User not found");
}
$newUser = $user->get();
$newUser->roles = $this->validateRoles($roles);
$newUser->updatedAt = Carbon::now()->toJson();
$this->userRepo->update($newUser);
}
/**
* @throws LucentException
*/
public function updateName(string $name): void
{
$name = new Name($name);
$this->userRepo->updateName($this->currentUserId(), $name);
$user = $this->userRepo->findById($this->currentUserId());
$this->session->put(["user" => $user->get()->safe()]);
}
/**
* @throws LucentException
*/
public function updateEmail(string $email): void
{
$email = new Email($email);
$user = $this->userRepo->findByEmail($email);
if ($user->isDefined()) {
throw new LucentException("Email already assigned to user");
}
$this->userRepo->updateEmail($this->currentUserId(), $email);
$user = $this->userRepo->findById($this->currentUserId());
$this->session->put(["user" => $user->get()->safe()]);
}
/**
* @throws LucentException
*/
public function invite(string $name, string $email, array $roles): User
{
$user = $this->create($name, $email, $roles);
$this->sendLoginEmail($user->email);
return $user;
}
/**
* @throws LucentException
*/
public function registerAdmin(string $name, string $email): User
{
$user = $this->invite($name, $email, ["admin"]);
$this->sendLoginEmail($user->email);
return $user;
}
public function validateRoles(array $roles): array
{
return collect($roles)
->filter(
fn(string $role) => in_array(
$role,
$this->channelService->channel->roles,
),
)
->unique()
->values()
->toArray();
}
}
+104
View File
@@ -0,0 +1,104 @@
<?php
namespace Lucent\Account;
use Illuminate\Contracts\Session\Session;
use Lucent\Channel\ChannelService;
use Lucent\LucentException;
use Filament\Facades\Filament;
readonly class AuthServiceLunar implements AuthService
{
public function __construct(
private ChannelService $channelService,
private UserRepo $userRepo,
public Session $session,
) {}
public function currentUserId(): ?string
{
if (app()->runningInConsole()) {
return config("lucent.systemUserId");
} elseif (request()->segment(1) !== "lucent") {
return config("lucent.systemUserId");
}
return Filament::auth()->user()->id;
}
public function getCurrentUser(): User
{
return new User(
id: Filament::auth()->user()->id,
name: new Name(
Filament::auth()->user()->first_name .
" " .
Filament::auth()->user()->last_name,
),
email: new Email(Filament::auth()->user()->email),
roles: [],
createdAt: Filament::auth()->user()->created_at,
updatedAt: Filament::auth()->user()->updated_at,
loggedInAt: null,
mailToken: null,
);
}
public function currentUserRoles(): array
{
return [];
}
public function isLoggedIn(): bool
{
return !empty($this->currentUserId());
}
/**
* @throws LucentException
*/
public function login(string $email, string $token): void {}
public function refreshSession() {}
public function create(string $name, string $email, array $roles): User {}
public function sendLoginEmail(string $email): void {}
/**
* @throws LucentException
*/
public function changeRoles(string $userId, array $roles): void {}
/**
* @throws LucentException
*/
public function updateName(string $name): void {}
/**
* @throws LucentException
*/
public function updateEmail(string $email): void {}
/**
* @throws LucentException
*/
public function invite(string $name, string $email, array $roles): User {}
/**
* @throws LucentException
*/
public function registerAdmin(string $name, string $email): User {}
public function validateRoles(array $roles): array
{
return collect($roles)
->filter(
fn(string $role) => in_array(
$role,
$this->channelService->channel->roles,
),
)
->unique()
->values()
->toArray();
}
}
+11 -85
View File
@@ -2,111 +2,37 @@
namespace Lucent\Account; namespace Lucent\Account;
use Carbon\Carbon;
use Lucent\Database\Database;
use Lucent\Primitive\Collection; use Lucent\Primitive\Collection;
use PhpOption\Option; use PhpOption\Option;
class UserRepo interface UserRepo
{ {
public function count(): int;
public function count(): int
{
return Database::make()->table("users")->count();
}
/** /**
* @return Collection<User> * @return Collection<User>
*/ */
public function all(): Collection public function all(): Collection;
{
$usersData = Database::make()->table("users")->get();
$users = array_map(fn($userData) => $this->fromArray((array)$userData), $usersData->toArray()); public static function insert(User $user): void;
return new Collection($users);
}
public function update(User $user): void;
public static function insert(User $user): void public function updateLoginToken(string $id): string;
{
$userData = toArray($user);
$userData["roles"] = json_encode($userData["roles"]);
Database::make()->table("users")->insert($userData);
}
public function update(User $user): void
{
$userData = toArray($user);
$userData["roles"] = json_encode($userData["roles"]);
Database::make()->table("users")->where("id", $user->id)->update($userData);
}
public function updateLoginToken(string $id): string
{
$newToken = Token::new(32);
Database::make()->table("users")
->where("id", $id)
->update([
'loggedInAt' => Carbon::now()->toJson(),
'mailToken' => $newToken,
]);
return $newToken;
}
/** /**
* @return Option<User> * @return Option<User>
*/ */
public function findByEmail(Email $email): Option public function findByEmail(Email $email): Option;
{
$user = Database::make()->table("users")->where("email", $email->value())->first();
if (empty($user)) {
return none();
}
return some($this->fromArray((array)$user));
}
/** /**
* @return Option<User> * @return Option<User>
*/ */
public function findById(string $id): Option public function findById(string $id): Option;
{
$user = Database::make()->table("users")->where("id", $id)->first();
if (empty($user)) { public function updateName(string $userId, Name $name): void;
return none();
}
return some($this->fromArray((array)$user)); public function updateEmail(string $userId, Email $email): void;
}
public function fromArray(array $data): User;
public function updateName(string $userId, Name $name): void
{
Database::make()->table("users")->where("id", $userId)->update(["name" => $name->value]);
}
public function updateEmail(string $userId, Email $email): void
{
Database::make()->table("users")->where("id", $userId)->update(["email" => $email->value()]);
}
public function fromArray(array $data): User
{
return new User(
id: $data["id"],
name: new Name($data["name"] ?? ""),
email: new Email($data["email"]),
roles: json_decode($data["roles"] ?? "[]", true),
createdAt: $data["createdAt"],
updatedAt: $data["updatedAt"],
loggedInAt: $data["loggedInAt"] ?? null,
mailToken: $data["mailToken"] ?? null,
);
}
} }
+128
View File
@@ -0,0 +1,128 @@
<?php
namespace Lucent\Account;
use Carbon\Carbon;
use Lucent\Database\Database;
use Lucent\Primitive\Collection;
use PhpOption\Option;
class UserRepoLucent implements UserRepo
{
private $tableName = "lucent_users";
public function count(): int
{
return Database::make()->table($this->tableName)->count();
}
/**
* @return Collection<User>
*/
public function all(): Collection
{
$usersData = Database::make()->table($this->tableName)->get();
$users = array_map(
fn($userData) => $this->fromArray((array) $userData),
$usersData->toArray(),
);
return new Collection($users);
}
public static function insert(User $user): void
{
$userData = toArray($user);
$userData["roles"] = json_encode($userData["roles"]);
Database::make()->table($this->tableName)->insert($userData);
}
public function update(User $user): void
{
$userData = toArray($user);
$userData["roles"] = json_encode($userData["roles"]);
Database::make()
->table($this->tableName)
->where("id", $user->id)
->update($userData);
}
public function updateLoginToken(string $id): string
{
$newToken = Token::new(32);
Database::make()
->table($this->tableName)
->where("id", $id)
->update([
"loggedInAt" => Carbon::now()->toJson(),
"mailToken" => $newToken,
]);
return $newToken;
}
/**
* @return Option<User>
*/
public function findByEmail(Email $email): Option
{
$user = Database::make()
->table($this->tableName)
->where("email", $email->value())
->first();
if (empty($user)) {
return none();
}
return some($this->fromArray((array) $user));
}
/**
* @return Option<User>
*/
public function findById(string $id): Option
{
$user = Database::make()
->table($this->tableName)
->where("id", $id)
->first();
if (empty($user)) {
return none();
}
return some($this->fromArray((array) $user));
}
public function updateName(string $userId, Name $name): void
{
Database::make()
->table($this->tableName)
->where("id", $userId)
->update(["name" => $name->value]);
}
public function updateEmail(string $userId, Email $email): void
{
Database::make()
->table($this->tableName)
->where("id", $userId)
->update(["email" => $email->value()]);
}
public function fromArray(array $data): User
{
return new User(
id: $data["id"],
name: new Name($data["name"] ?? ""),
email: new Email($data["email"]),
roles: json_decode($data["roles"] ?? "[]", true),
createdAt: $data["createdAt"],
updatedAt: $data["updatedAt"],
loggedInAt: $data["loggedInAt"] ?? null,
mailToken: $data["mailToken"] ?? null,
);
}
}
+130
View File
@@ -0,0 +1,130 @@
<?php
namespace Lucent\Account;
use Carbon\Carbon;
use Lucent\Database\Database;
use Lucent\Primitive\Collection;
use PhpOption\Option;
class UserRepoLunar implements UserRepo
{
private $tableName = "lunar_staff";
public function count(): int
{
return Database::make()->table($this->tableName)->count();
}
/**
* @return Collection<User>
*/
public function all(): Collection
{
$usersData = Database::make()->table($this->tableName)->get();
$users = array_map(
fn($userData) => $this->fromArray((array) $userData),
$usersData->toArray(),
);
return new Collection($users);
}
public static function insert(User $user): void
{
$userData = toArray($user);
$userData["roles"] = json_encode($userData["roles"]);
Database::make()->table($this->tableName)->insert($userData);
}
public function update(User $user): void
{
$userData = toArray($user);
$userData["roles"] = json_encode($userData["roles"]);
Database::make()
->table($this->tableName)
->where("id", $user->id)
->update($userData);
}
public function updateLoginToken(string $id): string
{
$newToken = Token::new(32);
Database::make()
->table($this->tableName)
->where("id", $id)
->update([
"loggedInAt" => Carbon::now()->toJson(),
"mailToken" => $newToken,
]);
return $newToken;
}
/**
* @return Option<User>
*/
public function findByEmail(Email $email): Option
{
$user = Database::make()
->table($this->tableName)
->where("email", $email->value())
->first();
if (empty($user)) {
return none();
}
return some($this->fromArray((array) $user));
}
/**
* @return Option<User>
*/
public function findById(string $id): Option
{
$user = Database::make()
->table($this->tableName)
->where("id", $id)
->first();
if (empty($user)) {
return none();
}
return some($this->fromArray((array) $user));
}
public function updateName(string $userId, Name $name): void
{
Database::make()
->table($this->tableName)
->where("id", $userId)
->update(["name" => $name->value]);
}
public function updateEmail(string $userId, Email $email): void
{
Database::make()
->table($this->tableName)
->where("id", $userId)
->update(["email" => $email->value()]);
}
public function fromArray(array $data): User
{
return new User(
id: $data["id"],
name: new Name(
$data["first_name"] . " " . $data["last_name"] ?? "",
),
email: new Email($data["email"]),
roles: [],
createdAt: $data["created_at"],
updatedAt: $data["updated_at"],
loggedInAt: null,
mailToken: null,
);
}
}
+49 -18
View File
@@ -2,7 +2,6 @@
namespace Lucent\Channel; namespace Lucent\Channel;
use Lucent\Channel\Data\UserCommand; use Lucent\Channel\Data\UserCommand;
use Lucent\Primitive\Collection; use Lucent\Primitive\Collection;
use Lucent\Schema\Schema; use Lucent\Schema\Schema;
@@ -13,12 +12,7 @@ final class ChannelService
{ {
public Channel $channel; public Channel $channel;
private function __construct( private function __construct(public SchemaService $schemaService) {}
public SchemaService $schemaService,
)
{
}
public static function fromConfig(): ChannelService public static function fromConfig(): ChannelService
{ {
@@ -28,7 +22,10 @@ final class ChannelService
$schemasArray = json_decode($schemasJson, true); $schemasArray = json_decode($schemasJson, true);
} }
$schemaService = new SchemaService(); $schemaService = new SchemaService();
$schemasCollection = (new Collection($schemasArray["schemas"] ?? []))->map([$schemaService, 'fromArray']); $schemasCollection = new Collection($schemasArray["schemas"] ?? []);
$schemasCollection = $schemasCollection->map(
$schemaService->fromArray(...),
);
$userCommands = []; $userCommands = [];
foreach (config("lucent.commands") ?? [] as $signature => $desc) { foreach (config("lucent.commands") ?? [] as $signature => $desc) {
@@ -42,7 +39,7 @@ final class ChannelService
commands: Collection::make($userCommands), commands: Collection::make($userCommands),
schemas: $schemasCollection, schemas: $schemasCollection,
imageFilters: config("lucent.imageFilters") ?? [], imageFilters: config("lucent.imageFilters") ?? [],
roles: $schemasArray["roles"] ?? [] roles: $schemasArray["roles"] ?? [],
); );
$channelService = new ChannelService($schemaService); $channelService = new ChannelService($schemaService);
@@ -69,11 +66,32 @@ final class ChannelService
*/ */
public function schemasReadableByRoles(array $roles): array public function schemasReadableByRoles(array $roles): array
{ {
$schemasAllRead = $this->channel->schemas->filter(fn(Schema $schema) => empty($schema->read))->values()->pluck("name"); $schemasAllRead = $this->channel->schemas
$schemasCanRead = $this->channel->schemas->filter(fn(Schema $schema) => count(array_intersect($schema->read ?? [], $roles)) > 0)->values()->pluck("name"); ->filter(fn(Schema $schema) => empty($schema->read))
$schemasCanWrite = $this->channel->schemas->filter(fn(Schema $schema) => count(array_intersect($schema->write ?? [], $roles)) > 0)->values()->pluck("name"); ->values()
return $schemasAllRead->merge($schemasCanRead)->merge($schemasCanWrite)->unique()->values()->toArray(); ->pluck("name");
$schemasCanRead = $this->channel->schemas
->filter(
fn(Schema $schema) => count(
array_intersect($schema->read ?? [], $roles),
) > 0,
)
->values()
->pluck("name");
$schemasCanWrite = $this->channel->schemas
->filter(
fn(Schema $schema) => count(
array_intersect($schema->write ?? [], $roles),
) > 0,
)
->values()
->pluck("name");
return $schemasAllRead
->merge($schemasCanRead)
->merge($schemasCanWrite)
->unique()
->values()
->toArray();
} }
/** /**
@@ -82,9 +100,22 @@ final class ChannelService
*/ */
public function schemasWritableByRoles(array $roles): array public function schemasWritableByRoles(array $roles): array
{ {
$schemasAllRead = $this->channel->schemas->filter(fn(Schema $schema) => empty($schema->write ?? []))->values()->pluck("name"); $schemasAllRead = $this->channel->schemas
$schemasCanWrite = $this->channel->schemas->filter(fn(Schema $schema) => count(array_intersect($schema->write ?? [], $roles)) > 0)->values()->pluck("name"); ->filter(fn(Schema $schema) => empty($schema->write ?? []))
return $schemasAllRead->merge($schemasCanWrite)->unique()->values()->toArray(); ->values()
->pluck("name");
$schemasCanWrite = $this->channel->schemas
->filter(
fn(Schema $schema) => count(
array_intersect($schema->write ?? [], $roles),
) > 0,
)
->values()
->pluck("name");
return $schemasAllRead
->merge($schemasCanWrite)
->unique()
->values()
->toArray();
} }
} }
+4 -4
View File
@@ -10,7 +10,7 @@ class CommandRepo
public function findBySignature($signature): ?CommandLogItem public function findBySignature($signature): ?CommandLogItem
{ {
$row = Database::make()->table("command_logs")->where("signature", $signature)->first(); $row = Database::make()->table("lucent_command_logs")->where("signature", $signature)->first();
if (empty($row)) { if (empty($row)) {
return null; return null;
} }
@@ -22,17 +22,17 @@ class CommandRepo
{ {
$foundCommandLogItem = $this->findBySignature($commandLogItem->signature); $foundCommandLogItem = $this->findBySignature($commandLogItem->signature);
if (empty($foundCommandLogItem)) { if (empty($foundCommandLogItem)) {
Database::make()->table("command_logs")->insert(toArray($commandLogItem)); Database::make()->table("lucent_command_logs")->insert(toArray($commandLogItem));
return; return;
} }
Database::make()->table("command_logs")->where("signature", $commandLogItem->signature)->update(toArray($commandLogItem)); Database::make()->table("lucent_command_logs")->where("signature", $commandLogItem->signature)->update(toArray($commandLogItem));
} }
public function appendToLogs(string $signature, string $line): void public function appendToLogs(string $signature, string $line): void
{ {
Database::make()->update( Database::make()->update(
'update command_logs set logs = logs || ? where signature = ?', 'update lucent_command_logs set logs = logs || ? where signature = ?',
[$line, $signature] [$line, $signature]
); );
+88 -61
View File
@@ -4,27 +4,18 @@ namespace Lucent\Commands;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Lucent\Database\Database; use Lucent\Database\Database;
class SetupDatabase extends Command class SetupDatabase extends Command
{ {
protected $signature = "lucent:setup-db";
protected $prefix = "lucent_";
protected $signature = 'lucent:setup-db'; protected $description = "Run to setup a new database";
protected $description = 'Run to setup a new database';
public function handle() 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->tableUsers();
$this->tableRecords(); $this->tableRecords();
$this->tableRevisions(); $this->tableRevisions();
@@ -36,79 +27,115 @@ class SetupDatabase extends Command
private function tableUsers(): void private function tableUsers(): void
{ {
Database::make()->getSchemaBuilder()->create('users', function (Blueprint $table) { $schema = Database::make()->getSchemaBuilder();
if ($schema->hasTable($this->prefix . "users")) {
return;
}
$schema->create($this->prefix . "users", function (Blueprint $table) {
$table->uuid("id")->primary(); $table->uuid("id")->primary();
$table->string('name')->nullable(); $table->string("name")->nullable();
$table->string('email')->unique(); $table->string("email")->unique();
$table->jsonb('roles'); $table->jsonb("roles");
$table->string('createdAt'); $table->string("createdAt");
$table->string('updatedAt'); $table->string("updatedAt");
$table->string('loggedInAt'); $table->string("loggedInAt");
$table->string('mailToken')->nullable(); $table->string("mailToken")->nullable();
}); });
} }
private function tableSessions(): void private function tableSessions(): void
{ {
Database::make()->getSchemaBuilder()->create('sessions', function (Blueprint $table) { $schema = Database::make()->getSchemaBuilder();
$table->string('id')->primary(); if ($schema->hasTable($this->prefix . "sessions")) {
$table->foreignId('user_id')->nullable()->index(); return;
$table->string('ip_address', 45)->nullable(); }
$table->text('user_agent')->nullable(); $schema->create($this->prefix . "sessions", function (
$table->longText('payload'); Blueprint $table,
$table->integer('last_activity')->index(); ) {
$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 private function tableRecords(): void
{ {
Database::make()->getSchemaBuilder()->create('records', function (Blueprint $table) { $schema = Database::make()->getSchemaBuilder();
$table->uuid('id')->primary(); if (!$schema->hasTable($this->prefix . "records")) {
$table->string('schema'); $schema->create($this->prefix . "records", function (
$table->string('status'); Blueprint $table,
$table->jsonb('data'); ) {
$table->jsonb('_sys'); $table->uuid("id")->primary();
$table->jsonb('_file'); $table->string("schema");
$table->text('search')->default(""); $table->string("status");
$table->jsonb("data");
$table->jsonb("_sys");
$table->jsonb("_file");
$table->text("search")->default("");
$table->index(['schema', '_sys->updatedAt', 'status']); // $table->index(["schema", "_sys->updatedAt", "status"]);
$table->index('search');
$table->index("search");
}); });
Database::make()->getSchemaBuilder()->create('edges', function (Blueprint $table) { DB::statement(
$table->uuid('source'); "CREATE INDEX ON " .
$table->uuid('target'); $this->prefix .
$table->string('sourceSchema'); 'records (schema, ((_sys->>\'updatedAt\')), status)',
$table->string('targetSchema'); );
$table->string('field'); }
$table->string('rank');
$table->unique(['source', 'target', "field"]); if (!$schema->hasTable($this->prefix . "edges")) {
$schema->create($this->prefix . "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 private function tableRevisions(): void
{ {
Database::make()->getSchemaBuilder()->create('revisions', function (Blueprint $table) { $schema = Database::make()->getSchemaBuilder();
$table->uuid('id')->primary(); if ($schema->hasTable($this->prefix . "revisions")) {
$table->uuid('recordId'); return;
$table->string('schema'); }
$table->jsonb('data'); $schema->create($this->prefix . "revisions", function (
$table->jsonb('_sys'); Blueprint $table,
$table->jsonb('_file'); ) {
$table->jsonb('_edges'); $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 private function tableCommandLogs(): void
{ {
Database::make()->getSchemaBuilder()->create('command_logs', function (Blueprint $table) { $schema = Database::make()->getSchemaBuilder();
$table->uuid('id')->primary(); if ($schema->hasTable($this->prefix . "command_logs")) {
$table->string('signature'); return;
$table->integer('pid')->nullable(); }
$table->text('logs'); $schema->create($this->prefix . "command_logs", function (
Blueprint $table,
) {
$table->uuid("id")->primary();
$table->string("signature");
$table->integer("pid")->nullable();
$table->text("logs");
}); });
} }
} }
+2 -2
View File
@@ -17,11 +17,11 @@ class UpgradeFiles122 extends Command
$schema = $this->argument('schema'); $schema = $this->argument('schema');
$disk = $this->argument('disk'); $disk = $this->argument('disk');
$db = Database::make(); $db = Database::make();
$records = $db->table("records")->where("schema", $schema)->get(); $records = $db->table("lucent_records")->where("schema", $schema)->get();
foreach ($records as $record) { foreach ($records as $record) {
$array = json_decode($record->_file, true); $array = json_decode($record->_file, true);
$array["disk"] = $disk; $array["disk"] = $disk;
$db->table("records")->where("id", $record->id)->update(["_file" => json_encode($array)]); $db->table("lucent_records")->where("id", $record->id)->update(["_file" => json_encode($array)]);
} }
} }
} }
+4 -5
View File
@@ -7,11 +7,10 @@ use Illuminate\Support\Facades\DB;
class Database class Database
{ {
public static function make(): Connection{ public static function make(): Connection
$dbConnection = config("lucent.database"); {
// $dbConnection = config("lucent.database");
return DB::connection($dbConnection); return DB::connection();
} }
} }
+8 -8
View File
@@ -15,7 +15,7 @@ class EdgeRepo
public function insert(Edge $edge): void public function insert(Edge $edge): void
{ {
try { try {
Database::make()->table("edges")->insert($edge->toDB()); Database::make()->table("lucent_edges")->insert($edge->toDB());
} catch (PDOException $e) { } catch (PDOException $e) {
if ($e->getCode() == 23505) { if ($e->getCode() == 23505) {
throw new LucentException("Edge already exists"); throw new LucentException("Edge already exists");
@@ -34,7 +34,7 @@ class EdgeRepo
{ {
$edgesDB = collect($edges)->map(fn($e) => $e->toDB())->toArray(); $edgesDB = collect($edges)->map(fn($e) => $e->toDB())->toArray();
try { try {
Database::make()->table("edges")->insert($edgesDB); Database::make()->table("lucent_edges")->insert($edgesDB);
} catch (PDOException $e) { } catch (PDOException $e) {
if ($e->getCode() == 23505) { if ($e->getCode() == 23505) {
throw new LucentException("Edge already exists"); throw new LucentException("Edge already exists");
@@ -52,8 +52,8 @@ class EdgeRepo
public function replaceForRecord(string $from, array $edges): void public function replaceForRecord(string $from, array $edges): void
{ {
$edgesDB = collect($edges)->map(fn($e) => $e->toDB())->toArray(); $edgesDB = collect($edges)->map(fn($e) => $e->toDB())->toArray();
Database::make()->table("edges")->where("source", $from)->delete(); Database::make()->table("lucent_edges")->where("source", $from)->delete();
Database::make()->table("edges")->insert($edgesDB); Database::make()->table("lucent_edges")->insert($edgesDB);
} }
@@ -62,13 +62,13 @@ class EdgeRepo
*/ */
public function findAll(): array public function findAll(): array
{ {
$edges = Database::make()->table("edges")->get(); $edges = Database::make()->table("lucent_edges")->get();
return $edges->map([$this, 'mapEdge'])->toArray(); return $edges->map([$this, 'mapEdge'])->toArray();
} }
public function findForSource(string $recordId): array public function findForSource(string $recordId): array
{ {
$edges = Database::make()->table("edges")->where("source", $recordId)->get(); $edges = Database::make()->table("lucent_edges")->where("source", $recordId)->get();
return $edges->map([$this, 'mapEdge'])->toArray(); return $edges->map([$this, 'mapEdge'])->toArray();
} }
@@ -89,7 +89,7 @@ class EdgeRepo
public function remove(Edge $edge): void public function remove(Edge $edge): void
{ {
Database::make()->table("edges") Database::make()->table("lucent_edges")
->where("source", $edge->source) ->where("source", $edge->source)
->where("target", $edge->target) ->where("target", $edge->target)
->where("sourceSchema", $edge->sourceSchema) ->where("sourceSchema", $edge->sourceSchema)
@@ -100,7 +100,7 @@ class EdgeRepo
public function findLastEdgeRank(string $source, string $field): string public function findLastEdgeRank(string $source, string $field): string
{ {
$data = Database::make()->table("edges") $data = Database::make()->table("lucent_edges")
->where("source", $source) ->where("source", $source)
->where("field", $field) ->where("field", $field)
->orderBy("rank", "desc") ->orderBy("rank", "desc")
+1 -1
View File
@@ -123,7 +123,7 @@ class FileService
private function checkDuplicate(string $schemaName, string $checksum, int $filesize): string private function checkDuplicate(string $schemaName, string $checksum, int $filesize): string
{ {
$record = Database::make()->table("records") $record = Database::make()->table("lucent_records")
->where("schema", $schemaName) ->where("schema", $schemaName)
->where("_file->checksum", $checksum) ->where("_file->checksum", $checksum)
->where("_file->size", $filesize) ->where("_file->size", $filesize)
+11 -18
View File
@@ -2,7 +2,6 @@
namespace Lucent\Http\Controller; namespace Lucent\Http\Controller;
use App\Http\Controllers\Controller;
use Illuminate\Contracts\Session\Session; use Illuminate\Contracts\Session\Session;
use Illuminate\Contracts\View\View; use Illuminate\Contracts\View\View;
use Illuminate\Http\RedirectResponse; use Illuminate\Http\RedirectResponse;
@@ -13,12 +12,10 @@ use Lucent\Account\AuthService;
use Lucent\Channel\ChannelService; use Lucent\Channel\ChannelService;
use Lucent\LucentException; use Lucent\LucentException;
use Lucent\Svelte\Svelte; use Lucent\Svelte\Svelte;
use Lucent\Util\Form\FormException;
use Lucent\Util\Form\ResponseFormError; use Lucent\Util\Form\ResponseFormError;
use function Lucent\Response\fail; use function Lucent\Response\fail;
use function Lucent\Response\ok; use function Lucent\Response\ok;
class AuthController class AuthController
{ {
public function __construct( public function __construct(
@@ -27,30 +24,25 @@ class AuthController
private readonly ChannelService $channelService, private readonly ChannelService $channelService,
private readonly Session $session, private readonly Session $session,
private readonly Svelte $svelte, private readonly Svelte $svelte,
) ) {}
{
}
public function register(Request $request): View|RedirectResponse public function register(Request $request): View|RedirectResponse
{ {
if ($this->accountService->countUsers() > 0) { if ($this->accountService->countUsers() > 0) {
return redirect($this->channelService->channel->lucentUrl . "/login"); return redirect(
$this->channelService->channel->lucentUrl . "/login",
);
} }
return $this->svelte->render( return $this->svelte->render(
layout: "account", layout: "account",
view: "register", view: "register",
title: "Create an account", title: "Create an account",
); );
} }
public function postRegister(Request $request): Response public function postRegister(Request $request): Response
{ {
if ($this->accountService->countUsers() > 0) { if ($this->accountService->countUsers() > 0) {
abort(400); abort(400);
} }
@@ -70,7 +62,9 @@ class AuthController
public function login() public function login()
{ {
if ($this->accountService->countUsers() == 0) { if ($this->accountService->countUsers() == 0) {
return redirect($this->channelService->channel->lucentUrl . "/register"); return redirect(
$this->channelService->channel->lucentUrl . "/register",
);
} }
return view("lucent::auth.login"); return view("lucent::auth.login");
@@ -89,25 +83,24 @@ class AuthController
"email" => $request->input("email"), "email" => $request->input("email"),
"token" => $request->input("token"), "token" => $request->input("token"),
]); ]);
} }
public function postVerify(Request $request) public function postVerify(Request $request)
{ {
try { try {
$this->authService->login($request->input("email"), $request->input("token")); $this->authService->login(
$request->input("email"),
$request->input("token"),
);
} catch (LucentException $th) { } catch (LucentException $th) {
return ResponseFormError::fromException($th); return ResponseFormError::fromException($th);
} }
return []; return [];
} }
public function logout(): RedirectResponse public function logout(): RedirectResponse
{ {
$this->session->flush(); $this->session->flush();
return redirect($this->channelService->channel->lucentUrl . "/login"); return redirect($this->channelService->channel->lucentUrl . "/login");
} }
} }
+7 -7
View File
@@ -17,13 +17,10 @@ class HomeController extends Controller
private readonly Svelte $svelte, private readonly Svelte $svelte,
private readonly AccountService $accountService, private readonly AccountService $accountService,
private readonly Query $query, private readonly Query $query,
) ) {}
{
}
public function home(): View public function home(): View
{ {
return $this->svelte->render( return $this->svelte->render(
layout: "channel", layout: "channel",
view: "homeIndex", view: "homeIndex",
@@ -38,10 +35,13 @@ class HomeController extends Controller
$sort = data_get($urlParams, "sort") ?? "-_sys.updatedAt"; $sort = data_get($urlParams, "sort") ?? "-_sys.updatedAt";
$filter = data_get($urlParams, "filter") ?? []; $filter = data_get($urlParams, "filter") ?? [];
$arguments = array_merge([ $arguments = array_merge(
[
"schema_in" => $this->accountService->currentReadableSchemas(), "schema_in" => $this->accountService->currentReadableSchemas(),
"status_in" => ["draft", "published"] "status_in" => ["draft", "published"],
], $filter); ],
$filter,
);
$limit = 20; $limit = 20;
+138 -63
View File
@@ -35,15 +35,18 @@ class RecordController extends Controller
private readonly Manager $recordManager, private readonly Manager $recordManager,
private readonly OperatorRegistry $operatorRegistry, private readonly OperatorRegistry $operatorRegistry,
private readonly ViewModel $viewModel, private readonly ViewModel $viewModel,
) ) {}
{
}
public function index(Request $request) public function index(Request $request)
{ {
$schemaName = $request->route("schemaName"); $schemaName = $request->route("schemaName");
if (!in_array($schemaName, $this->accountService->currentReadableSchemas())) { if (
!in_array(
$schemaName,
$this->accountService->currentReadableSchemas(),
)
) {
return $this->svelte->render( return $this->svelte->render(
layout: "channel", layout: "channel",
view: "recordNotFound", view: "recordNotFound",
@@ -53,16 +56,18 @@ class RecordController extends Controller
$users = $this->accountService->all(); $users = $this->accountService->all();
$schema = $this->channelService->getSchema($schemaName)->get(); $schema = $this->channelService->getSchema($schemaName)->get();
$urlParams = $request->all(); $urlParams = $request->all();
$sort = data_get($urlParams, "sort") ?? $schema->sortBy; $sort = data_get($urlParams, "sort") ?? $schema->sortBy;
$filter = data_get($urlParams, "filter") ?? []; $filter = data_get($urlParams, "filter") ?? [];
$arguments = array_merge([ $arguments = array_merge(
[
"schema" => $schema->name, "schema" => $schema->name,
"status_in" => "draft,published", "status_in" => "draft,published",
], $filter); ],
$filter,
);
$skip = data_get($urlParams, "skip") ?? 0; $skip = data_get($urlParams, "skip") ?? 0;
$limit = 30; $limit = 30;
@@ -81,7 +86,6 @@ class RecordController extends Controller
->parentsDepth(0) ->parentsDepth(0)
->runWithCount(); ->runWithCount();
$records = $graph->getRootRecords()->toArray(); $records = $graph->getRootRecords()->toArray();
$data = [ $data = [
@@ -93,45 +97,64 @@ class RecordController extends Controller
"systemFields" => array_values(System::list()), "systemFields" => array_values(System::list()),
"operators" => $this->operatorRegistry->all(), "operators" => $this->operatorRegistry->all(),
"sortParam" => $sort, "sortParam" => $sort,
"sortField" => $schema->fields->merge(array_values(System::list()))->firstWhere(fn($field) => $field->name === $sort || "-" . $field->name === $sort || "data." . $field->name === $sort || "-data." . $field->name === $sort), "sortField" => $schema->fields
->merge(array_values(System::list()))
->firstWhere(
fn($field) => $field->name === $sort ||
"-" . $field->name === $sort ||
"data." . $field->name === $sort ||
"-data." . $field->name === $sort,
),
"limit" => $limit, "limit" => $limit,
"skip" => $skip, "skip" => $skip,
"total" => $graph->total ?? 0, "total" => $graph->total ?? 0,
"filter" => $request->input("filter") ?? [], "filter" => $request->input("filter") ?? [],
"inModal" => true, "inModal" => true,
"isWritable" => in_array($schemaName, $this->accountService->currentWritableSchemas()) "isWritable" => in_array(
$schemaName,
$this->accountService->currentWritableSchemas(),
),
]; ];
if ($request->ajax()) { if ($request->ajax()) {
$data["modalUrl"] = $request->fullUrl(); $data["modalUrl"] = $request->fullUrl();
if (str_starts_with(config("lucent.url"), "https")) { if (str_starts_with(config("lucent.url"), "https")) {
$data["modalUrl"] = str_replace("http://", "https://", $request->fullUrl()); $data["modalUrl"] = str_replace(
"http://",
"https://",
$request->fullUrl(),
);
} }
return $data; return $data;
} }
$data["inModal"] = false; $data["inModal"] = false;
return $this->svelte->render( return $this->svelte->render(
layout: "channel", layout: "channel",
view: "contentIndex", view: "contentIndex",
title: "Records", title: "Records",
data: $data data: $data,
); );
} }
public function exportCSV(Request $request) public function exportCSV(Request $request)
{ {
$schemaName = $request->route("schemaName"); $schemaName = $request->route("schemaName");
$schema = $this->channelService->channel->schemas->where("name", $schemaName)->first(); $schema = $this->channelService->channel->schemas
->where("name", $schemaName)
->first();
$urlParams = $request->all(); $urlParams = $request->all();
$sort = data_get($urlParams, "sort") ?? "-_sys.updatedAt"; $sort = data_get($urlParams, "sort") ?? "-_sys.updatedAt";
$filter = data_get($urlParams, "filter") ?? []; $filter = data_get($urlParams, "filter") ?? [];
$arguments = array_merge([ $arguments = array_merge(
[
"schema" => $schema->name, "schema" => $schema->name,
"status_in" => "draft,published", "status_in" => "draft,published",
], $filter); ],
$filter,
);
$records = $this->query $records = $this->query
->filter($arguments) ->filter($arguments)
@@ -143,35 +166,50 @@ class RecordController extends Controller
->run() ->run()
->tree(); ->tree();
header('Content-Type: application/csv'); header("Content-Type: application/csv");
header('Content-Disposition: attachment; filename="' . $schemaName . '.csv";'); header(
$handle = fopen('php://output', 'w'); 'Content-Disposition: attachment; filename="' .
$schemaName .
'.csv";',
);
$handle = fopen("php://output", "w");
$relationColumns = $this->makeCsvRelationColumns($schema); $relationColumns = $this->makeCsvRelationColumns($schema);
$csvRow = ["id", ...array_keys($records[0]->data->toArray()),...$relationColumns]; $csvRow = [
fputcsv($handle, $csvRow, ','); "id",
...array_keys($records[0]->data->toArray()),
...$relationColumns,
];
fputcsv($handle, $csvRow, ",");
foreach ($records as $record) { foreach ($records as $record) {
$csvRow = [$record->id, ...$record->data->toArray()]; $csvRow = [$record->id, ...$record->data->toArray()];
$csvRow = array_merge($csvRow,$this->makeCsvRelationColumnValues($schema,$record->_children)); $csvRow = array_merge(
$csvRow,
$this->makeCsvRelationColumnValues($schema, $record->_children),
);
$csvRow = array_values($csvRow); $csvRow = array_values($csvRow);
fputcsv($handle, $csvRow, ','); fputcsv($handle, $csvRow, ",");
} }
fclose($handle); fclose($handle);
echo $handle; echo $handle;
exit; exit();
} }
private function makeCsvRelationColumns($schema):array{ private function makeCsvRelationColumns($schema): array
return $schema->fields->filter(fn($f) => get_class($f) === Reference::class)->reduce(function($c,$f){ {
return $schema->fields
->filter(fn($f) => get_class($f) === Reference::class)
->reduce(function ($c, $f) {
$c[] = $f->name . " id"; $c[] = $f->name . " id";
$c[] = $f->name . " name"; $c[] = $f->name . " name";
return $c; return $c;
}, []); }, []);
} }
private function makeCsvRelationColumnValues($schema, $children):array{ private function makeCsvRelationColumnValues($schema, $children): array
return $schema->fields->filter(fn($f) => get_class($f) === Reference::class)->reduce(function($c,$f) use($children){ {
return $schema->fields
->filter(fn($f) => get_class($f) === Reference::class)
->reduce(function ($c, $f) use ($children) {
$fieldRecords = data_get($children, $f->name); $fieldRecords = data_get($children, $f->name);
if (empty($fieldRecords)) { if (empty($fieldRecords)) {
$c[] = ""; $c[] = "";
@@ -181,7 +219,9 @@ class RecordController extends Controller
$c[] = $this->viewModel->getRecordName($fieldRecords[0]); $c[] = $this->viewModel->getRecordName($fieldRecords[0]);
} else { } else {
$c[] = collect($fieldRecords)->pluck("id")->join("::"); $c[] = collect($fieldRecords)->pluck("id")->join("::");
$c[] = collect($fieldRecords)->pluck("data.name")->join("::"); $c[] = collect($fieldRecords)
->pluck("data.name")
->join("::");
} }
return $c; return $c;
}, []); }, []);
@@ -189,7 +229,12 @@ class RecordController extends Controller
public function new(Request $request) public function new(Request $request)
{ {
if (!in_array($request->input("schema"), $this->accountService->currentWritableSchemas())) { if (
!in_array(
$request->input("schema"),
$this->accountService->currentWritableSchemas(),
)
) {
return $this->svelte->render( return $this->svelte->render(
layout: "channel", layout: "channel",
view: "recordNotFound", view: "recordNotFound",
@@ -197,8 +242,12 @@ class RecordController extends Controller
); );
} }
$schema = $this->channelService->channel->schemas->where("name", $request->input("schema"))->first(); $schema = $this->channelService->channel->schemas
$recordHistory = $this->recordManager->fromSession($request->session())->getRecords(); ->where("name", $request->input("schema"))
->first();
$recordHistory = $this->recordManager
->fromSession($request->session())
->getRecords();
$record = $this->recordService->createEmpty($schema); $record = $this->recordService->createEmpty($schema);
$queryRecord = QueryRecord::fromRecord($record); $queryRecord = QueryRecord::fromRecord($record);
return $this->svelte->render( return $this->svelte->render(
@@ -210,15 +259,22 @@ class RecordController extends Controller
"record" => $queryRecord, "record" => $queryRecord,
"recordHistory" => $recordHistory, "recordHistory" => $recordHistory,
"isCreateMode" => true, "isCreateMode" => true,
"isWritable" => in_array($record->schema, $this->accountService->currentWritableSchemas()) "isWritable" => in_array(
] $record->schema,
$this->accountService->currentWritableSchemas(),
),
],
); );
} }
public function newInline(Request $request) public function newInline(Request $request)
{ {
if (!in_array($request->input("schema"), $this->accountService->currentWritableSchemas())) { if (
!in_array(
$request->input("schema"),
$this->accountService->currentWritableSchemas(),
)
) {
return $this->svelte->render( return $this->svelte->render(
layout: "channel", layout: "channel",
view: "recordNotFound", view: "recordNotFound",
@@ -226,7 +282,9 @@ class RecordController extends Controller
); );
} }
$schema = $this->channelService->getSchema($request->input("schema"))->get(); $schema = $this->channelService
->getSchema($request->input("schema"))
->get();
$record = $this->recordService->createEmpty($schema); $record = $this->recordService->createEmpty($schema);
$queryRecord = QueryRecord::fromRecord($record); $queryRecord = QueryRecord::fromRecord($record);
@@ -234,7 +292,10 @@ class RecordController extends Controller
"schema" => $schema, "schema" => $schema,
"record" => $queryRecord, "record" => $queryRecord,
"isCreateMode" => true, "isCreateMode" => true,
"isWritable" => in_array($record->schema, $this->accountService->currentWritableSchemas()) "isWritable" => in_array(
$record->schema,
$this->accountService->currentWritableSchemas(),
),
]; ];
} }
@@ -262,7 +323,12 @@ class RecordController extends Controller
$record = $graph->records->first(); $record = $graph->records->first();
if (!in_array($record->schema, $this->accountService->currentReadableSchemas())) { if (
!in_array(
$record->schema,
$this->accountService->currentReadableSchemas(),
)
) {
return $this->svelte->render( return $this->svelte->render(
layout: "channel", layout: "channel",
view: "recordNotFound", view: "recordNotFound",
@@ -271,7 +337,10 @@ class RecordController extends Controller
} }
$schema = $this->channelService->getSchema($record->schema)->get(); $schema = $this->channelService->getSchema($record->schema)->get();
$recordHistory = $this->recordManager->fromSession($request->session())->push($rid)->getRecords($rid); $recordHistory = $this->recordManager
->fromSession($request->session())
->push($rid)
->getRecords($rid);
return $this->svelte->render( return $this->svelte->render(
layout: "channel", layout: "channel",
view: "recordEdit", view: "recordEdit",
@@ -282,8 +351,11 @@ class RecordController extends Controller
"record" => toArray($record), "record" => toArray($record),
"users" => $this->accountService->all(), "users" => $this->accountService->all(),
"recordHistory" => $recordHistory, "recordHistory" => $recordHistory,
"isWritable" => in_array($record->schema, $this->accountService->currentWritableSchemas()) "isWritable" => in_array(
] $record->schema,
$this->accountService->currentWritableSchemas(),
),
],
); );
} }
@@ -295,19 +367,20 @@ class RecordController extends Controller
if ($request->input("value")) { if ($request->input("value")) {
if (in_array($request->input("ui"), ["text", "date"])) { if (in_array($request->input("ui"), ["text", "date"])) {
$arguments["data." . $request->input("field") . "_regex"] = $request->input("value"); $arguments[
"data." . $request->input("field") . "_regex"
] = $request->input("value");
} elseif ($request->input("ui") == "number") { } elseif ($request->input("ui") == "number") {
$arguments["data." . $request->input("field") . "_eqnum"] = floatval($request->input("value")); $arguments[
"data." . $request->input("field") . "_eqnum"
] = floatval($request->input("value"));
} elseif ($request->input("ui") == "date") { } elseif ($request->input("ui") == "date") {
} elseif ($request->input("ui") == "search") { } elseif ($request->input("ui") == "search") {
$arguments["search_regex"] = $request->input("value"); $arguments["search_regex"] = $request->input("value");
} }
} }
$records = $this->query $records = $this->query->filter($arguments)->limit(10)->tree();
->filter($arguments)
->limit(10)
->tree();
if ($records->isEmpty()) { if ($records->isEmpty()) {
return ok([]); return ok([]);
@@ -320,7 +393,6 @@ class RecordController extends Controller
{ {
$recordId = $request->input("record.id"); $recordId = $request->input("record.id");
try { try {
if ($request->input("isCreateMode")) { if ($request->input("isCreateMode")) {
$recordId = $this->recordService->create( $recordId = $this->recordService->create(
data: new RecordInputData( data: new RecordInputData(
@@ -329,15 +401,20 @@ class RecordController extends Controller
$request->input("record.data"), $request->input("record.data"),
Status::from($request->input("record.status")), Status::from($request->input("record.status")),
), ),
edges: array_map(EdgeInputData::fromArray(...), $request->input("edges") ?? []) edges: array_map(
EdgeInputData::fromArray(...),
$request->input("edges") ?? [],
),
); );
} else { } else {
$this->recordService->updateWithEdges( $this->recordService->updateWithEdges(
id: $request->input("record.id"), id: $request->input("record.id"),
data: $request->input("record.data"), data: $request->input("record.data"),
status: Status::from($request->input("record.status")), status: Status::from($request->input("record.status")),
edges: array_map(EdgeInputData::fromArray(...), $request->input("edges") ?? []), edges: array_map(
EdgeInputData::fromArray(...),
$request->input("edges") ?? [],
),
); );
} }
@@ -347,7 +424,6 @@ class RecordController extends Controller
->childrenDepth(1) ->childrenDepth(1)
->parentsDepth(1) ->parentsDepth(1)
->run(); ->run();
} catch (ValidatorException $th) { } catch (ValidatorException $th) {
return fail($th->getValidatorErrors()); return fail($th->getValidatorErrors());
} catch (LucentException $th) { } catch (LucentException $th) {
@@ -356,11 +432,9 @@ class RecordController extends Controller
return ok(toArray($newGraph)); return ok(toArray($newGraph));
} }
public function clone(Request $request) public function clone(Request $request)
{ {
try { try {
$newRecordId = $this->recordService->clone( $newRecordId = $this->recordService->clone(
recordId: $request->route("rid"), recordId: $request->route("rid"),
); );
@@ -374,10 +448,8 @@ class RecordController extends Controller
public function status(Request $request) public function status(Request $request)
{ {
$ids = array_map(fn($rec) => $rec["id"], $request->input("records")); $ids = array_map(fn($rec) => $rec["id"], $request->input("records"));
$this->recordService->changeStatusBulk( $this->recordService->changeStatusBulk(
status: $request->route("status"), status: $request->route("status"),
recordsIds: $ids, recordsIds: $ids,
@@ -387,9 +459,12 @@ class RecordController extends Controller
public function emptyTrash(Request $request) public function emptyTrash(Request $request)
{ {
$this->recordService->emptyTrash($request->route("schemaName")); $this->recordService->emptyTrash($request->route("schemaName"));
return redirect($this->channelService->channel->lucentUrl . "/content/" . $request->route("schemaName")); return redirect(
$this->channelService->channel->lucentUrl .
"/content/" .
$request->route("schemaName"),
);
} }
public function delete(Request $request) public function delete(Request $request)
@@ -409,7 +484,7 @@ class RecordController extends Controller
try { try {
$this->recordService->rollback( $this->recordService->rollback(
recordId: $request->route("rid"), recordId: $request->route("rid"),
version: (int)$request->route("version") version: (int) $request->route("version"),
); );
} catch (ValidatorException $th) { } catch (ValidatorException $th) {
return fail($th->getFirstValidatorError()); return fail($th->getFirstValidatorError());
+15 -7
View File
@@ -16,20 +16,28 @@ readonly class AuthMiddleware
private AuthService $authService, private AuthService $authService,
private AccountService $accountService, private AccountService $accountService,
private ChannelService $channelService, private ChannelService $channelService,
private ViewModel $viewModel private ViewModel $viewModel,
) ) {}
{
}
public function handle(Request $request, Closure $next) public function handle(Request $request, Closure $next)
{ {
if (!$this->authService->isLoggedIn()) { if (!$this->authService->isLoggedIn()) {
return redirect($this->channelService->channel->lucentUrl . "/login"); return redirect(
$this->channelService->channel->lucentUrl . "/login",
);
} }
$this->authService->refreshSession(); $this->authService->refreshSession();
View::share("channel", $this->channelService->channel); View::share("channel", $this->channelService->channel);
View::share("user",session("user")); View::share("user", toArray($this->authService->getCurrentUser()));
View::share("schemas",$this->channelService->channel->schemas->whereIn("name",$this->accountService->currentReadableSchemas())->values()); View::share(
"schemas",
$this->channelService->channel->schemas
->whereIn(
"name",
$this->accountService->currentReadableSchemas(),
)
->values(),
);
View::share("viewModel", $this->viewModel); View::share("viewModel", $this->viewModel);
return $next($request); return $next($request);
+51 -32
View File
@@ -7,6 +7,10 @@ use Illuminate\Support\Facades\Blade;
use Illuminate\Support\Facades\View; use Illuminate\Support\Facades\View;
use Illuminate\Support\ServiceProvider; use Illuminate\Support\ServiceProvider;
use Intervention\Image\ImageManager; use Intervention\Image\ImageManager;
use Lucent\Account\AuthService;
use Lucent\Account\AuthServiceLunar;
use Lucent\Account\UserRepo;
use Lucent\Account\UserRepoLunar;
use Lucent\Channel\ChannelService; use Lucent\Channel\ChannelService;
use Lucent\Commands\CompileSchemas; use Lucent\Commands\CompileSchemas;
use Lucent\Commands\GenerateCollectionSchema; use Lucent\Commands\GenerateCollectionSchema;
@@ -17,10 +21,8 @@ use Lucent\Commands\RemoveOrphanEdges;
use Lucent\Commands\SetupDatabase; use Lucent\Commands\SetupDatabase;
use Lucent\Commands\UpgradeFiles122; use Lucent\Commands\UpgradeFiles122;
use Lucent\File\FileService; use Lucent\File\FileService;
use Lucent\File\ImageService;
use Lucent\Query\DatabaseGraph\DatabaseGraph; use Lucent\Query\DatabaseGraph\DatabaseGraph;
use Lucent\Query\DatabaseGraph\PgsqlDatabaseGraph; use Lucent\Query\DatabaseGraph\PgsqlDatabaseGraph;
use Lucent\Query\DatabaseGraph\SqliteDatabaseGraph;
class LucentServiceProvider extends ServiceProvider class LucentServiceProvider extends ServiceProvider
{ {
@@ -34,21 +36,20 @@ class LucentServiceProvider extends ServiceProvider
}); });
$this->app->bind(ImageManager::class, function () { $this->app->bind(ImageManager::class, function () {
return new ImageManager(['driver' => 'imagick']); return new ImageManager(["driver" => "imagick"]);
}); });
$this->app->bind(DatabaseGraph::class, function () { $this->app->bind(DatabaseGraph::class, function () {
$dbConnection = config("lucent.database"); return new PgsqlDatabaseGraph();
return match (config("database.connections.$dbConnection.driver")) {
"sqlite" => new SqliteDatabaseGraph(),
"pgsql" => new PgsqlDatabaseGraph(),
};
}); });
$this->app->bind(UserRepo::class, function () {
return new UserRepoLunar();
});
$this->app->bind(AuthService::class, function ($app) {
return $app->make(AuthServiceLunar::class);
});
} }
/** /**
@@ -56,20 +57,29 @@ class LucentServiceProvider extends ServiceProvider
*/ */
public function boot(Router $router): void public function boot(Router $router): void
{ {
$manifestPath = public_path("vendor/lucent/dist/manifest.json");
$manifestPath = public_path('vendor/lucent/dist/manifest.json');
$manifest = null; $manifest = null;
if (file_exists($manifestPath)) { if (file_exists($manifestPath)) {
$manifest = json_decode(file_get_contents(public_path('vendor/lucent/dist/manifest.json')), true); $manifest = json_decode(
file_get_contents(
public_path("vendor/lucent/dist/manifest.json"),
),
true,
);
} }
$router->aliasMiddleware(
"lucent.auth",
\Lucent\Http\Middleware\AuthMiddleware::class,
);
$router->aliasMiddleware(
"lucent.guest",
\Lucent\Http\Middleware\GuestMiddleware::class,
);
$router->aliasMiddleware('lucent.auth', \Lucent\Http\Middleware\AuthMiddleware::class); $this->loadViewsFrom(__DIR__ . "/../front/views", "lucent");
$router->aliasMiddleware('lucent.guest', \Lucent\Http\Middleware\GuestMiddleware::class); $this->loadRoutesFrom(__DIR__ . "/Http/web.php");
$this->loadRoutesFrom(__DIR__ . "/Http/api.php");
$this->loadViewsFrom(__DIR__ . '/../front/views', 'lucent');
$this->loadRoutesFrom(__DIR__ . '/Http/web.php');
$this->loadRoutesFrom(__DIR__ . '/Http/api.php');
if ($this->app->runningInConsole()) { if ($this->app->runningInConsole()) {
$this->commands([ $this->commands([
@@ -84,19 +94,28 @@ class LucentServiceProvider extends ServiceProvider
]); ]);
} }
View::share('manifest', $manifest); View::share("manifest", $manifest);
View::share('file', app()->make(FileService::class)); View::share("file", app()->make(FileService::class));
Blade::anonymousComponentPath(__DIR__ . '../front/views/components', "lucent"); Blade::anonymousComponentPath(
__DIR__ . "../front/views/components",
$this->publishes([ "lucent",
__DIR__ . '/Config/main.php' => config_path('lucent.php'), );
],"lucent-config");
$this->publishes([
__DIR__ . '/../front/dist' => public_path('vendor/lucent/dist'),
__DIR__ . '/../front/public' => public_path('vendor/lucent/public'),
], 'lucent');
$this->publishes(
[
__DIR__ . "/Config/main.php" => config_path("lucent.php"),
],
"lucent-config",
);
$this->publishes(
[
__DIR__ . "/../front/dist" => public_path("vendor/lucent/dist"),
__DIR__ . "/../front/public" => public_path(
"vendor/lucent/public",
),
],
"lucent",
);
} }
} }
@@ -14,7 +14,7 @@ class PgsqlDatabaseGraph implements DatabaseGraph
*/ */
public function getChildren(array $ids, QueryOptions $options): array public function getChildren(array $ids, QueryOptions $options): array
{ {
$subquery = Database::make()->table('edges AS g') $subquery = Database::make()->table('lucent_edges AS g')
->select(DB::raw('g.source,g.target,g.rank,"g"."sourceSchema","g"."targetSchema",g.field, 1 as depth ')) ->select(DB::raw('g.source,g.target,g.rank,"g"."sourceSchema","g"."targetSchema",g.field, 1 as depth '))
->whereIn('source', $ids); ->whereIn('source', $ids);
@@ -24,7 +24,7 @@ class PgsqlDatabaseGraph implements DatabaseGraph
$subquery->limit($options->childrenLimit) $subquery->limit($options->childrenLimit)
->unionAll( ->unionAll(
Database::make()->table(DB::raw("edges AS g, search_graph AS sg ")) Database::make()->table(DB::raw("lucent_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') ->selectRaw('g.source,g.target,g.rank,"g"."sourceSchema","g"."targetSchema",g.field,sg.depth + 1 as depth')
->whereRaw("g.source = sg.target") ->whereRaw("g.source = sg.target")
->where("depth", "<", $options->childrenDepth) ->where("depth", "<", $options->childrenDepth)
@@ -42,12 +42,12 @@ class PgsqlDatabaseGraph implements DatabaseGraph
*/ */
public function getParents(array $ids, QueryOptions $options): array public function getParents(array $ids, QueryOptions $options): array
{ {
$subquery = Database::make()->table('edges AS g') $subquery = Database::make()->table('lucent_edges AS g')
->select(DB::raw('g.source,g.target,g.rank,"g"."sourceSchema","g"."targetSchema",g.field, 1 as depth ')) ->select(DB::raw('g.source,g.target,g.rank,"g"."sourceSchema","g"."targetSchema",g.field, 1 as depth '))
->limit($options->parentsLimit) ->limit($options->parentsLimit)
->whereIn('g.target', $ids) ->whereIn('g.target', $ids)
->unionAll( ->unionAll(
Database::make()->table(DB::raw("edges AS g, search_graph AS sg ")) Database::make()->table(DB::raw("lucent_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') ->selectRaw('g.source,g.target,g.rank,"g"."sourceSchema","g"."targetSchema",g.field,sg.depth + 1 as depth')
->whereRaw("g.target = sg.source") ->whereRaw("g.target = sg.source")
->where("depth", "<", $options->parentsDepth) ->where("depth", "<", $options->parentsDepth)
@@ -13,7 +13,7 @@ class SqliteDatabaseGraph implements DatabaseGraph
*/ */
public function getChildren(array $ids, QueryOptions $options): array public function getChildren(array $ids, QueryOptions $options): array
{ {
$subquery = Database::make()->table('edges AS g') $subquery = Database::make()->table('lucent_edges AS g')
->select(DB::raw('g.source,g.target,g.rank,g.sourceSchema,g.targetSchema,g.field, 1 as depth ')) ->select(DB::raw('g.source,g.target,g.rank,g.sourceSchema,g.targetSchema,g.field, 1 as depth '))
->whereIn('source', $ids); ->whereIn('source', $ids);
@@ -23,7 +23,7 @@ class SqliteDatabaseGraph implements DatabaseGraph
$subquery->limit($options->childrenLimit) $subquery->limit($options->childrenLimit)
->unionAll( ->unionAll(
Database::make()->table(DB::raw("edges AS g, search_graph AS sg ")) Database::make()->table(DB::raw("lucent_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') ->selectRaw('g.source,g.target,g.rank,"g"."sourceSchema","g"."targetSchema",g.field,sg.depth + 1 as depth')
->whereRaw("g.source = sg.target") ->whereRaw("g.source = sg.target")
->where("depth", "<", $options->childrenDepth) ->where("depth", "<", $options->childrenDepth)
@@ -41,12 +41,12 @@ class SqliteDatabaseGraph implements DatabaseGraph
*/ */
public function getParents(array $ids, QueryOptions $options): array public function getParents(array $ids, QueryOptions $options): array
{ {
$subquery = Database::make()->table('edges AS g') $subquery = Database::make()->table('lucent_edges AS g')
->select(DB::raw('g.source,g.target,g.rank,"g"."sourceSchema","g"."targetSchema",g.field, 1 as depth ')) ->select(DB::raw('g.source,g.target,g.rank,"g"."sourceSchema","g"."targetSchema",g.field, 1 as depth '))
->limit($options->parentsLimit) ->limit($options->parentsLimit)
->whereIn('g.target', $ids) ->whereIn('g.target', $ids)
->unionAll( ->unionAll(
Database::make()->table(DB::raw("edges AS g, search_graph AS sg ")) Database::make()->table(DB::raw("lucent_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') ->selectRaw('g.source,g.target,g.rank,"g"."sourceSchema","g"."targetSchema",g.field,sg.depth + 1 as depth')
->whereRaw("g.target = sg.source") ->whereRaw("g.target = sg.source")
->where("depth", "<", $options->parentsDepth) ->where("depth", "<", $options->parentsDepth)
+1 -1
View File
@@ -59,7 +59,7 @@ final class FilterParser
} }
$targetIds = collect($graph->records)->pluck("id"); $targetIds = collect($graph->records)->pluck("id");
$sourceIds = Database::make()->table("edges")->whereIn("target", $targetIds)->where("field", $k)->get()->pluck("source"); $sourceIds = Database::make()->table("lucent_edges")->whereIn("target", $targetIds)->where("field", $k)->get()->pluck("source");
return array_merge($c, $sourceIds->toArray()); return array_merge($c, $sourceIds->toArray());
}, []); }, []);
+76 -54
View File
@@ -15,7 +15,6 @@ use Lucent\Record\Record;
final class Query final class Query
{ {
/** /**
* @var array<AndFilter> $filters * @var array<AndFilter> $filters
*/ */
@@ -26,8 +25,7 @@ final class Query
public readonly FilterParser $filterParser, public readonly FilterParser $filterParser,
public readonly InputFormatter $inputFormatter, public readonly InputFormatter $inputFormatter,
public readonly DatabaseGraph $databaseGraph, public readonly DatabaseGraph $databaseGraph,
) ) {
{
$this->options = new QueryOptions(); $this->options = new QueryOptions();
} }
@@ -43,7 +41,6 @@ final class Query
return $this; return $this;
} }
public function run(): Graph public function run(): Graph
{ {
$resultsRecords = $this->mainQuery(); $resultsRecords = $this->mainQuery();
@@ -55,33 +52,53 @@ final class Query
$resultChildrenEdges = []; $resultChildrenEdges = [];
if ($this->options->childrenDepth > 0 && !empty($ids)) { if ($this->options->childrenDepth > 0 && !empty($ids)) {
$resultChildrenEdges = $this->getChildren($ids); $resultChildrenEdges = $this->getChildren($ids);
$resultChildrenEdgesTargetIds = array_map(fn($e) => $e->target, $resultChildrenEdges); $resultChildrenEdgesTargetIds = array_map(
fn($e) => $e->target,
$resultChildrenEdges,
);
} }
$resultParentSourceTargetIds = []; $resultParentSourceTargetIds = [];
$resultParentEdges = []; $resultParentEdges = [];
if ($this->options->parentsDepth > 0 && !empty($ids)) { if ($this->options->parentsDepth > 0 && !empty($ids)) {
$resultParentEdges = $this->getParents($ids); $resultParentEdges = $this->getParents($ids);
$resultParentSourceTargetIds = array_map(fn($e) => $e->source, $resultParentEdges); $resultParentSourceTargetIds = array_map(
fn($e) => $e->source,
$resultParentEdges,
);
} }
$edgesIds = collect($resultParentSourceTargetIds)->merge($resultChildrenEdgesTargetIds)->unique()->values()->toArray(); $edgesIds = collect($resultParentSourceTargetIds)
->merge($resultChildrenEdgesTargetIds)
->unique()
->values()
->toArray();
$edgeRecords = []; $edgeRecords = [];
if (!empty($edgesIds)) { if (!empty($edgesIds)) {
$edgeRecords = Database::make()->table('records') $edgeRecords = Database::make()
->table("lucent_records")
->whereIn("id", $edgesIds) ->whereIn("id", $edgesIds)
->whereIn("status", $this->options->status) ->whereIn("status", $this->options->status)
->get()->toArray(); ->get()
->toArray();
} }
$resultsRecordsUnique = collect(array_merge($resultsRecords, $edgeRecords))->unique("id")->values()->toArray(); $resultsRecordsUnique = collect(
array_merge($resultsRecords, $edgeRecords),
)
->unique("id")
->values()
->toArray();
// $resultEdges = collect(array_merge($resultChildrenEdges, $resultParentEdges)) // $resultEdges = collect(array_merge($resultChildrenEdges, $resultParentEdges))
// ->unique(fn($edge) => $edge->source . $edge->target . $edge->field) // ->unique(fn($edge) => $edge->source . $edge->target . $edge->field)
// ->toArray(); // ->toArray();
$formattedRecords = $this->formatRecords($resultsRecordsUnique, $resultChildrenEdges, $resultParentEdges); $formattedRecords = $this->formatRecords(
$resultsRecordsUnique,
$resultChildrenEdges,
$resultParentEdges,
);
$this->reset(); $this->reset();
return $formattedRecords; return $formattedRecords;
} }
private function reset() private function reset()
@@ -90,33 +107,41 @@ final class Query
$this->filters = []; $this->filters = [];
} }
private function formatRecords(array $records, array $edges, array $parentEdges): Graph private function formatRecords(
{ array $records,
$queryRecords = collect($records)->map(function ($recordData) { array $edges,
array $parentEdges,
): Graph {
$queryRecords = collect($records)
->map(function ($recordData) {
$record = Record::fromDB($recordData); $record = Record::fromDB($recordData);
$record->data = $this->inputFormatter->fill($record->schema, $record->data); $record->data = $this->inputFormatter->fill(
$record->schema,
$record->data,
);
$queryRecord = QueryRecord::fromRecord($record); $queryRecord = QueryRecord::fromRecord($record);
$queryRecord->isRoot = data_get($recordData, "isRoot") === true; $queryRecord->isRoot = data_get($recordData, "isRoot") === true;
return $queryRecord; return $queryRecord;
})->toArray(); })
->toArray();
$queryEdges = collect($edges)->map(function ($edgeData) {
$queryEdges = collect($edges)
->map(function ($edgeData) {
return Edge::fromArray((array) $edgeData); return Edge::fromArray((array) $edgeData);
})
->sortBy("rank")
->values()
->toArray();
})->sortBy("rank")->values()->toArray(); $queryParentEdges = collect($parentEdges)
->map(function ($edgeData) {
$queryParentEdges = collect($parentEdges)->map(function ($edgeData) {
return Edge::fromArray((array) $edgeData); return Edge::fromArray((array) $edgeData);
})
})->sortBy("rank")->values()->toArray(); ->sortBy("rank")
->values()
->toArray();
return new Graph( return new Graph(
new Collection($queryRecords), new Collection($queryRecords),
@@ -126,13 +151,11 @@ final class Query
); );
} }
public function tree(): Collection public function tree(): Collection
{ {
return $this->run()->tree(); return $this->run()->tree();
} }
private function parseFilters(Builder $query): Builder private function parseFilters(Builder $query): Builder
{ {
foreach ($this->filters as $filter) { foreach ($this->filters as $filter) {
@@ -142,10 +165,9 @@ final class Query
return $query; return $query;
} }
private function mainQuery(): array private function mainQuery(): array
{ {
$query = Database::make()->table("records"); $query = Database::make()->table("lucent_records");
$query = $this->parseFilters($query); $query = $this->parseFilters($query);
$query = $this->findNotLinked($query); $query = $this->findNotLinked($query);
@@ -155,13 +177,15 @@ final class Query
} }
$query = $this->orderByQuery($query); $query = $this->orderByQuery($query);
return $query->get()->map(function ($r) { return $query
->get()
->map(function ($r) {
$r->isRoot = true; $r->isRoot = true;
return $r; return $r;
})->toArray(); })
->toArray();
} }
private function findNotLinked(Builder $query): Builder private function findNotLinked(Builder $query): Builder
{ {
if (empty($this->options->notLinked)) { if (empty($this->options->notLinked)) {
@@ -170,14 +194,19 @@ final class Query
// This returns only records that have no parents // This returns only records that have no parents
$query $query
->select("records.*") ->select("lucent_records.*")
->join('edges', 'records.id', '=', 'edges.target', 'left outer') ->join(
->whereNull("edges.target"); "lucent_edges",
"lucent_records.id",
"=",
"lucent_edges.target",
"left outer",
)
->whereNull("lucent_edges.target");
return $query; return $query;
} }
private private function getChildren(array $ids): array
function getChildren(array $ids): array
{ {
return $this->databaseGraph->getChildren($ids, $this->options); return $this->databaseGraph->getChildren($ids, $this->options);
} }
@@ -187,12 +216,9 @@ final class Query
return $this->databaseGraph->getParents($ids, $this->options); return $this->databaseGraph->getParents($ids, $this->options);
} }
public function runWithCount(): Graph
public
function runWithCount(): Graph
{ {
$query = Database::make()->table("lucent_records");
$query = Database::make()->table("records");
$query = $this->parseFilters($query); $query = $this->parseFilters($query);
$query = $this->findNotLinked($query); $query = $this->findNotLinked($query);
$graph = $this->run(); $graph = $this->run();
@@ -200,15 +226,13 @@ final class Query
return $graph; return $graph;
} }
public function limit(int $limit): Query public function limit(int $limit): Query
{ {
$this->options->limit = $limit; $this->options->limit = $limit;
return $this; return $this;
} }
public public function skip(int $skip): Query
function skip(int $skip): Query
{ {
$this->options->skip = $skip; $this->options->skip = $skip;
return $this; return $this;
@@ -250,7 +274,6 @@ final class Query
return $this; return $this;
} }
public function sort(string $sort): Query public function sort(string $sort): Query
{ {
$this->options->sort[] = $sort; $this->options->sort[] = $sort;
@@ -269,12 +292,11 @@ final class Query
return $this; return $this;
} }
public public function orderByQuery(Builder $query): Builder
function orderByQuery(Builder $query): Builder
{ {
foreach ($this->options->sort as $item) { foreach ($this->options->sort as $item) {
$field = str_replace(".", "->", ltrim($item, '-')); $field = str_replace(".", "->", ltrim($item, "-"));
$dir = str_starts_with($item, '-') ? "desc" : "asc"; $dir = str_starts_with($item, "-") ? "desc" : "asc";
if ($field) { if ($field) {
$query->orderBy($field, $dir); $query->orderBy($field, $dir);
} }
+34 -22
View File
@@ -6,16 +6,13 @@ use Lucent\Database\Database;
class RecordRepo class RecordRepo
{ {
public function __construct() public function __construct() {}
{
}
public static function create(Record $record): void public static function create(Record $record): void
{ {
$recordToDB = $record->toDB(); $recordToDB = $record->toDB();
Database::make()->table("records")->insert($recordToDB); Database::make()->table("lucent_records")->insert($recordToDB);
} }
/** /**
@@ -23,40 +20,55 @@ class RecordRepo
*/ */
public static function updateStatusBulk(Status $status, array $ids): void public static function updateStatusBulk(Status $status, array $ids): void
{ {
Database::make()->table("records")->whereIn("id", $ids)->update([ Database::make()
'status' => $status->value ->table("lucent_records")
->whereIn("id", $ids)
->update([
"status" => $status->value,
]); ]);
} }
public static function update(Record $record): void public static function update(Record $record): void
{ {
$recordToDB = $record->toDB(); $recordToDB = $record->toDB();
Database::make()->table("records")->where("id", $record->id)->update($recordToDB); Database::make()
->table("lucent_records")
->where("id", $record->id)
->update($recordToDB);
} }
/** /**
* @param string[] $ids * @param string[] $ids
*/ */
public function deleteMany( public function deleteMany(array $ids): void
array $ids,
): void
{ {
Database::make()
Database::make()->table("records")->whereIn("id", $ids)->delete(); ->table("lucent_records")
Database::make()->table("edges")->whereIn("source", $ids)->delete(); ->whereIn("id", $ids)
Database::make()->table("edges")->whereIn("target", $ids)->delete(); ->delete();
Database::make()->table("revisions")->whereIn("recordId", $ids)->delete(); Database::make()
->table("lucent_edges")
->whereIn("source", $ids)
->delete();
Database::make()
->table("lucent_edges")
->whereIn("target", $ids)
->delete();
Database::make()
->table("lucent_revisions")
->whereIn("recordId", $ids)
->delete();
} }
public function deleteTrashedBySchema( public function deleteTrashedBySchema(string $schemaName): void
string $schemaName,
): void
{ {
$ids = Database::make()->table("records") $ids = Database::make()
->table("lucent_records")
->where("schema", $schemaName) ->where("schema", $schemaName)
->where("status", Status::TRASHED->value) ->where("status", Status::TRASHED->value)
->get()->pluck("id")->toArray(); ->get()
->pluck("id")
->toArray();
$this->deleteMany($ids); $this->deleteMany($ids);
} }
+95 -77
View File
@@ -23,7 +23,6 @@ use Lucent\Schema\Validator\ValidatorException;
readonly class RecordService readonly class RecordService
{ {
public function __construct( public function __construct(
private AuthService $authService, private AuthService $authService,
private RevisionService $revisionService, private RevisionService $revisionService,
@@ -34,9 +33,7 @@ readonly class RecordService
private RecordRepo $recordRepo, private RecordRepo $recordRepo,
private EdgeService $edgeService, private EdgeService $edgeService,
private FileService $fileService, private FileService $fileService,
) ) {}
{
}
/** /**
* @param string $url * @param string $url
@@ -49,12 +46,13 @@ readonly class RecordService
public function createFromUrl( public function createFromUrl(
string $url, string $url,
RecordInputData $data, RecordInputData $data,
array $edges array $edges,
): string ): string {
{
$schema = $this->channelService->getSchema($data->schemaName)->get(); $schema = $this->channelService->getSchema($data->schemaName)->get();
if ($schema->type !== Type::FILES) { if ($schema->type !== Type::FILES) {
throw new LucentException("You can't upload a file to a regular record"); throw new LucentException(
"You can't upload a file to a regular record",
);
} }
$fileData = $this->fileService->createFromUrl($schema, $url); $fileData = $this->fileService->createFromUrl($schema, $url);
if ($fileData->isDuplicate) { if ($fileData->isDuplicate) {
@@ -66,12 +64,13 @@ readonly class RecordService
public function createFromUploadedFile( public function createFromUploadedFile(
UploadedFile $uploadedFile, UploadedFile $uploadedFile,
RecordInputData $data, RecordInputData $data,
array $edges array $edges,
): string ): string {
{
$schema = $this->channelService->getSchema($data->schemaName)->get(); $schema = $this->channelService->getSchema($data->schemaName)->get();
if ($schema->type !== Type::FILES) { if ($schema->type !== Type::FILES) {
throw new LucentException("You can't upload a file to a regular record"); throw new LucentException(
"You can't upload a file to a regular record",
);
} }
$fileData = $this->fileService->upload($schema, $uploadedFile); $fileData = $this->fileService->upload($schema, $uploadedFile);
if ($fileData->isDuplicate) { if ($fileData->isDuplicate) {
@@ -83,12 +82,13 @@ readonly class RecordService
public function createFromFileData( public function createFromFileData(
FileData $fileData, FileData $fileData,
RecordInputData $data, RecordInputData $data,
array $edges array $edges,
): string ): string {
{
$schema = $this->channelService->getSchema($data->schemaName)->get(); $schema = $this->channelService->getSchema($data->schemaName)->get();
if ($schema->type !== Type::FILES) { if ($schema->type !== Type::FILES) {
throw new LucentException("You can't upload a file to a regular record"); throw new LucentException(
"You can't upload a file to a regular record",
);
} }
return $this->create($data, $fileData, $edges); return $this->create($data, $fileData, $edges);
} }
@@ -103,11 +103,12 @@ readonly class RecordService
public function create( public function create(
RecordInputData $data, RecordInputData $data,
?FileData $file = null, ?FileData $file = null,
array $edges = [] array $edges = [],
): string ): string {
{ $formattedData = $this->inputFormatter->fill(
$data->schemaName,
$formattedData = $this->inputFormatter->fill($data->schemaName, new RecordData($data->data)); new RecordData($data->data),
);
$newRecordId = empty($data->id) ? Id::new() : $data->id; $newRecordId = empty($data->id) ? Id::new() : $data->id;
$record = new Record( $record = new Record(
@@ -120,38 +121,49 @@ readonly class RecordService
); );
if ($data->status === Status::PUBLISHED) { if ($data->status === Status::PUBLISHED) {
$errors = $this->recordValidator->check($data->schemaName, $record->data); $errors = $this->recordValidator->check(
$data->schemaName,
$record->data,
);
if ($errors->isNotEmpty()) { if ($errors->isNotEmpty()) {
$this->recordValidator->throwException($errors); $this->recordValidator->throwException($errors);
} }
} }
RecordRepo::create($record); RecordRepo::create($record);
$newEdges = $this->edgeService->createManyForRecord($record->id, $record->schema, $edges); $newEdges = $this->edgeService->createManyForRecord(
$record->id,
$record->schema,
$edges,
);
$this->revisionService->create($record, $newEdges); $this->revisionService->create($record, $newEdges);
return $record->id; return $record->id;
} }
/** /**
* @throws LucentException * @throws LucentException
* @throws ValidatorException * @throws ValidatorException
*/ */
public function update( public function update(string $id, array $data, Status $status): void
string $id,
array $data,
Status $status,
): void
{ {
$record = $this->query->filter(["id" => $id])->run()->records->first(); $record = $this->query
->filter(["id" => $id])
->run()
->records->first();
if (empty($record)) { if (empty($record)) {
throw new LucentException("Record id is missing"); throw new LucentException("Record id is missing");
} }
$formattedData = $this->inputFormatter->fill($record->schema, new RecordData($data)); $formattedData = $this->inputFormatter->fill(
$record->schema,
new RecordData($data),
);
if ($status === Status::PUBLISHED) { if ($status === Status::PUBLISHED) {
$errors = $this->recordValidator->check($record->schema, $formattedData); $errors = $this->recordValidator->check(
$record->schema,
$formattedData,
);
if ($errors->isNotEmpty()) { if ($errors->isNotEmpty()) {
$this->recordValidator->throwException($errors); $this->recordValidator->throwException($errors);
} }
@@ -169,7 +181,6 @@ readonly class RecordService
RecordRepo::update($newRecord); RecordRepo::update($newRecord);
$newEdges = $this->edgeService->findForSource($record->id); $newEdges = $this->edgeService->findForSource($record->id);
$this->revisionService->create($newRecord, $newEdges); $this->revisionService->create($newRecord, $newEdges);
} }
/** /**
@@ -180,18 +191,26 @@ readonly class RecordService
string $id, string $id,
array $data, array $data,
Status $status, Status $status,
array $edges array $edges,
): void ): void {
{ $record = $this->query
$record = $this->query->filter(["id" => $id])->run()->records->first(); ->filter(["id" => $id])
->run()
->records->first();
if (empty($record)) { if (empty($record)) {
throw new LucentException("Record id is missing"); throw new LucentException("Record id is missing");
} }
$formattedData = $this->inputFormatter->fill($record->schema, new RecordData($data)); $formattedData = $this->inputFormatter->fill(
$record->schema,
new RecordData($data),
);
if ($status === Status::PUBLISHED) { if ($status === Status::PUBLISHED) {
$errors = $this->recordValidator->check($record->schema, $formattedData); $errors = $this->recordValidator->check(
$record->schema,
$formattedData,
);
if ($errors->isNotEmpty()) { if ($errors->isNotEmpty()) {
$this->recordValidator->throwException($errors); $this->recordValidator->throwException($errors);
} }
@@ -207,15 +226,15 @@ readonly class RecordService
); );
RecordRepo::update($newRecord); RecordRepo::update($newRecord);
$newEdges = $this->edgeService->replaceManyForRecord($record->id, $record->schema, $edges); $newEdges = $this->edgeService->replaceManyForRecord(
$record->id,
$record->schema,
$edges,
);
$this->revisionService->create($newRecord, $newEdges); $this->revisionService->create($newRecord, $newEdges);
} }
public function changeStatusBulk( public function changeStatusBulk(string $status, array $recordsIds): void
string $status,
array $recordsIds,
): void
{ {
RecordRepo::updateStatusBulk(Status::from($status), $recordsIds); RecordRepo::updateStatusBulk(Status::from($status), $recordsIds);
} }
@@ -224,10 +243,7 @@ readonly class RecordService
* @throws LucentException * @throws LucentException
* @throws ValidatorException * @throws ValidatorException
*/ */
public public function clone(string $recordId): string
function clone(
string $recordId,
): string
{ {
$graph = $this->query $graph = $this->query
->filter(["id" => $recordId]) ->filter(["id" => $recordId])
@@ -244,11 +260,14 @@ readonly class RecordService
$newEdgesData = $graph->edges $newEdgesData = $graph->edges
->filter(fn(Edge $edge) => $edge->source == $recordId) ->filter(fn(Edge $edge) => $edge->source == $recordId)
->values() ->values()
->map(fn(Edge $edge) => new EdgeInputData( ->map(
fn(Edge $edge) => new EdgeInputData(
target: $edge->target, target: $edge->target,
targetSchema: $edge->targetSchema, targetSchema: $edge->targetSchema,
field: $edge->field, field: $edge->field,
))->toArray(); ),
)
->toArray();
$record->id = $newRecordId; $record->id = $newRecordId;
@@ -257,62 +276,62 @@ readonly class RecordService
schemaName: $record->schema, schemaName: $record->schema,
id: $record->id, id: $record->id,
data: $record->data->toArray(), data: $record->data->toArray(),
status: Status::DRAFT status: Status::DRAFT,
), ),
file: $record->_file, file: $record->_file,
edges: $newEdgesData edges: $newEdgesData,
); );
} }
public function deleteMany(array $recordsIds): void
public function deleteMany(
array $recordsIds,
): void
{ {
$this->recordRepo->deleteMany($recordsIds); $this->recordRepo->deleteMany($recordsIds);
} }
public function emptyTrash( public function emptyTrash(string $schemaName): void
string $schemaName,
): void
{ {
$schema = $this->channelService->getSchema($schemaName)->get(); $schema = $this->channelService->getSchema($schemaName)->get();
$this->recordRepo->deleteTrashedBySchema($schemaName); $this->recordRepo->deleteTrashedBySchema($schemaName);
} }
/** /**
* @throws LucentException * @throws LucentException
* @throws ValidatorException * @throws ValidatorException
*/ */
public function rollback( public function rollback(string $recordId, int $version): void
string $recordId,
int $version,
): void
{ {
$revision = $this->revisionService->getByRecordIdAndVersion($recordId, $version)->get(); $revision = $this->revisionService
->getByRecordIdAndVersion($recordId, $version)
->get();
$this->updateWithEdges( $this->updateWithEdges(
id: $revision->recordId, id: $revision->recordId,
data: $revision->data->toArray(), data: $revision->data->toArray(),
status: Status::DRAFT, status: Status::DRAFT,
edges: array_map(fn(Edge $edge) => new EdgeInputData($edge->target, $edge->targetSchema, $edge->field), $revision->_edges), edges: array_map(
fn(Edge $edge) => new EdgeInputData(
$edge->target,
$edge->targetSchema,
$edge->field,
),
$revision->_edges,
),
); );
} }
public function createEmpty(Schema $schema): Record
public function createEmpty(
Schema $schema,
): Record
{ {
$defaultValues = $schema->fields->reduce(function (
$defaultValues = $schema->fields->reduce(function ($carry, FieldInterface $f) { $carry,
FieldInterface $f,
) {
$carry[$f->name] = $f->default ?? null; $carry[$f->name] = $f->default ?? null;
return $carry; return $carry;
}, []); }, []);
$formattedData = $this->inputFormatter->fill($schema->name, new RecordData($defaultValues)); $formattedData = $this->inputFormatter->fill(
$schema->name,
new RecordData($defaultValues),
);
return new Record( return new Record(
id: Id::new(), id: Id::new(),
@@ -322,6 +341,5 @@ readonly class RecordService
data: $formattedData, data: $formattedData,
_file: null, _file: null,
); );
} }
} }
+1 -1
View File
@@ -14,7 +14,7 @@ use stdClass;
class RevisionRepo class RevisionRepo
{ {
private string $table = "revisions"; private string $table = "lucent_revisions";
public function create(Revision $revision): string public function create(Revision $revision): string
{ {
+5 -35
View File
@@ -8,45 +8,12 @@ use Lucent\Setup\Data\SetupStep;
class DatabaseSetupStep implements IStep class DatabaseSetupStep implements IStep
{ {
public function __invoke(): SetupStep public function __invoke(): SetupStep
{ {
$name = "Database Connection"; $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 { try {
Database::make()->table("records")->get(); Database::make()->table("lucent_records")->get();
} catch (QueryException $e) { } catch (QueryException $e) {
$instructions = <<<EOD $instructions = <<<EOD
# Make sure you run: # Make sure you run:
@@ -55,6 +22,9 @@ EOD;
return SetupStep::makeFail($name, $instructions); return SetupStep::makeFail($name, $instructions);
} }
return SetupStep::makeSuccess($name, "Database Connection successfully created"); return SetupStep::makeSuccess(
$name,
"Database Connection successfully created",
);
} }
} }
+30 -20
View File
@@ -5,44 +5,54 @@ namespace Lucent\Svelte;
use Illuminate\Contracts\View\Factory; use Illuminate\Contracts\View\Factory;
use Illuminate\Contracts\View\View; use Illuminate\Contracts\View\View;
use Lucent\Account\AccountService; use Lucent\Account\AccountService;
use Lucent\Account\AuthService;
use Lucent\Channel\ChannelService; use Lucent\Channel\ChannelService;
class Svelte class Svelte
{ {
public function __construct( public function __construct(
public ChannelService $channelService, public ChannelService $channelService,
public AccountService $accountService public AccountService $accountService,
) public AuthService $authService,
{ ) {}
}
function render(string $layout, string $view, string $title = "", mixed $data = []): View|Factory
{
function render(
string $layout,
string $view,
string $title = "",
mixed $data = [],
): View|Factory {
$context = []; $context = [];
$context["user"] = session('user'); $context["user"] = toArray($this->authService->getCurrentUser());
$context["view"] = $view; $context["view"] = $view;
$context["layout"] = $layout; $context["layout"] = $layout;
$context["title"] = $title; $context["title"] = $title;
$context["data"] = $data; $context["data"] = $data;
$context["channel"] = $this->channelService->channel; $context["channel"] = $this->channelService->channel;
$context["readableSchemas"] = $this->accountService->currentReadableSchemas(); $context[
"readableSchemas"
] = $this->accountService->currentReadableSchemas();
$json = json_encode($context); $json = json_encode($context);
$divTag = sprintf('<div class="lucent-component" data-layout="%s"></div>', $layout); $divTag = sprintf(
$jsonTag = sprintf('<script type="application/json" id="json-%s">%s</script>', $layout, $json); '<div class="lucent-component" data-layout="%s"></div>',
$layout,
);
$jsonTag = sprintf(
'<script type="application/json" id="json-%s">%s</script>',
$layout,
$json,
);
$svelte = $divTag . $jsonTag; $svelte = $divTag . $jsonTag;
return view('lucent::svelte', [ return view("lucent::svelte", [
'svelte' => $svelte, "svelte" => $svelte,
'view' => $view, "view" => $view,
'data' => $data, "data" => $data,
'title' => $title, "title" => $title,
'layout' => $layout, "layout" => $layout,
'channel' => $this->channelService->channel, "channel" => $this->channelService->channel,
]); ]);
} }
} }