From 4a7eb217a1f081ed200750b550a842687dc46c87 Mon Sep 17 00:00:00 2001 From: lexx Date: Mon, 20 Apr 2026 21:07:35 +0300 Subject: [PATCH] boboko lulnar --- front/js/svelte/Channel.svelte | 27 +- front/js/svelte/home/Index.svelte | 19 +- front/js/svelte/layout/Header.svelte | 15 +- front/js/svelte/setup/Step.svelte | 16 +- src/Account/AccountService.php | 14 +- src/Account/AuthService.php | 213 ++------------- src/Account/AuthServiceLucent.php | 224 +++++++++++++++ src/Account/AuthServiceLunar.php | 104 +++++++ src/Account/UserRepo.php | 96 +------ src/Account/UserRepoLucent.php | 128 +++++++++ src/Account/UserRepoLunar.php | 130 +++++++++ src/Channel/ChannelService.php | 67 +++-- src/Command/CommandRepo.php | 8 +- src/Commands/SetupDatabase.php | 153 ++++++----- src/Commands/UpgradeFiles122.php | 4 +- src/Database/Database.php | 11 +- src/Edge/EdgeRepo.php | 16 +- src/File/FileService.php | 2 +- src/Http/Controller/AuthController.php | 35 +-- src/Http/Controller/HomeController.php | 20 +- src/Http/Controller/RecordController.php | 255 +++++++++++------- src/Http/Middleware/AuthMiddleware.php | 26 +- src/LucentServiceProvider.php | 83 +++--- .../DatabaseGraph/PgsqlDatabaseGraph.php | 8 +- .../DatabaseGraph/SqliteDatabaseGraph.php | 8 +- src/Query/FilterParser.php | 2 +- src/Query/Query.php | 158 ++++++----- src/Record/RecordRepo.php | 58 ++-- src/Record/RecordService.php | 206 +++++++------- src/Revision/RevisionRepo.php | 2 +- src/Setup/Step/DatabaseSetupStep.php | 48 +--- src/Svelte/Svelte.php | 52 ++-- 32 files changed, 1350 insertions(+), 858 deletions(-) create mode 100644 src/Account/AuthServiceLucent.php create mode 100644 src/Account/AuthServiceLunar.php create mode 100644 src/Account/UserRepoLucent.php create mode 100644 src/Account/UserRepoLunar.php diff --git a/front/js/svelte/Channel.svelte b/front/js/svelte/Channel.svelte index fa0216b..0d55838 100644 --- a/front/js/svelte/Channel.svelte +++ b/front/js/svelte/Channel.svelte @@ -3,7 +3,7 @@ import RecordNotFound from "./records/NotFound.svelte"; import RecordEdit from "./records/Edit.svelte"; import ContentIndex from "./content/Index.svelte"; - import {setContext} from "svelte"; + import { setContext } from "svelte"; import Navbar from "./layout/Navbar.svelte"; import HomeIndex from "./home/Index.svelte"; import BuildReport from "./build/Report.svelte"; @@ -28,24 +28,21 @@ export let axios; export let readableSchemas; - setContext("axios", axios); 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); - -
- -
-
- -
-
- - - diff --git a/front/js/svelte/home/Index.svelte b/front/js/svelte/home/Index.svelte index f781144..ec445ff 100644 --- a/front/js/svelte/home/Index.svelte +++ b/front/js/svelte/home/Index.svelte @@ -1,7 +1,6 @@ -

Latest Content changes

{#if records.length > 0} -
- {#each records as record (record.id)} - - - - {/each} + {#each records as record (record.id)} + + + + {/each}
- {/if} - diff --git a/front/js/svelte/layout/Header.svelte b/front/js/svelte/layout/Header.svelte index 04ec6c0..8513fc1 100644 --- a/front/js/svelte/layout/Header.svelte +++ b/front/js/svelte/layout/Header.svelte @@ -1,25 +1,25 @@ - -
+
Members {#if channel.commands.length > 0}
Actions
{#each channel.commands as command} - {command.name} + {command.name} {/each}
- {/if} @@ -28,7 +28,6 @@ - +
- diff --git a/front/js/svelte/setup/Step.svelte b/front/js/svelte/setup/Step.svelte index 955fb7e..606f3f9 100644 --- a/front/js/svelte/setup/Step.svelte +++ b/front/js/svelte/setup/Step.svelte @@ -1,11 +1,9 @@ -
{#if step.status === "success"} @@ -17,26 +15,24 @@

{step.name}

- Instuctions + Instructions {step.instructions}
- -
\ No newline at end of file + diff --git a/src/Account/AccountService.php b/src/Account/AccountService.php index 82fae53..908bc5d 100644 --- a/src/Account/AccountService.php +++ b/src/Account/AccountService.php @@ -7,15 +7,11 @@ use Lucent\Primitive\Collection; readonly class AccountService { - public function __construct( - private AuthService $authService, + private AuthService $authService, private ChannelService $channelService, - private UserRepo $userRepo, - ) - { - - } + private UserRepo $userRepo, + ) {} /** * @return Collection @@ -25,13 +21,11 @@ readonly class AccountService return $this->userRepo->all(); } - public function countUsers(): int { return $this->userRepo->count(); } - /** * @return Collection */ @@ -54,6 +48,4 @@ readonly class AccountService $roles = $this->authService->currentUserRoles(); return $this->channelService->schemasWritableByRoles($roles); } - - } diff --git a/src/Account/AuthService.php b/src/Account/AuthService.php index 0098832..f3a604d 100644 --- a/src/Account/AuthService.php +++ b/src/Account/AuthService.php @@ -2,235 +2,52 @@ 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; -readonly class AuthService +interface AuthService { + public function currentUserId(): ?string; - public function __construct( - private ChannelService $channelService, - private UserRepo $userRepo, - public Session $session, - ) - { + public function currentUserRoles(): array; + public function getCurrentUser(): User; - } - - - 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()); - } + public function isLoggedIn(): bool; /** * @throws LucentException */ - public - function login(string $email, string $token): void - { + public function login(string $email, string $token): void; - $user = $this->userRepo->findByEmail(new Email($email)); + public function refreshSession(); - 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 - ) - ); - } + 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 - { - $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); - } + public function changeRoles(string $userId, array $roles): void; /** * @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()]); - } + public function updateName(string $name): void; /** * @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()]); - } - + public function updateEmail(string $email): void; /** * @throws LucentException */ - public - function invite( - string $name, - string $email, - array $roles - ): User - { - $user = $this->create($name, $email, $roles); - $this->sendLoginEmail($user->email); - return $user; - } + public function invite(string $name, string $email, array $roles): 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(); - } - + public function registerAdmin(string $name, string $email): User; + public function validateRoles(array $roles): array; } diff --git a/src/Account/AuthServiceLucent.php b/src/Account/AuthServiceLucent.php new file mode 100644 index 0000000..1a30c68 --- /dev/null +++ b/src/Account/AuthServiceLucent.php @@ -0,0 +1,224 @@ +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(); + } +} diff --git a/src/Account/AuthServiceLunar.php b/src/Account/AuthServiceLunar.php new file mode 100644 index 0000000..5fcf4f8 --- /dev/null +++ b/src/Account/AuthServiceLunar.php @@ -0,0 +1,104 @@ +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(); + } +} diff --git a/src/Account/UserRepo.php b/src/Account/UserRepo.php index 84d638f..3c08a2c 100644 --- a/src/Account/UserRepo.php +++ b/src/Account/UserRepo.php @@ -2,111 +2,37 @@ namespace Lucent\Account; -use Carbon\Carbon; -use Lucent\Database\Database; use Lucent\Primitive\Collection; use PhpOption\Option; -class UserRepo +interface UserRepo { - - public function count(): int - { - return Database::make()->table("users")->count(); - } + public function count(): int; /** * @return Collection */ - public function all(): Collection - { - $usersData = Database::make()->table("users")->get(); + public function all(): Collection; - $users = array_map(fn($userData) => $this->fromArray((array)$userData), $usersData->toArray()); - return new Collection($users); - } + public static function insert(User $user): void; + public function update(User $user): void; - public static function insert(User $user): void - { - $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; - } - + public function updateLoginToken(string $id): string; /** * @return 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)); - } + public function findByEmail(Email $email): Option; /** * @return Option */ - public function findById(string $id): Option - { - $user = Database::make()->table("users")->where("id", $id)->first(); + public function findById(string $id): Option; - if (empty($user)) { - return none(); - } + public function updateName(string $userId, Name $name): void; - return some($this->fromArray((array)$user)); - } + public function updateEmail(string $userId, Email $email): void; - - 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, - ); - } + public function fromArray(array $data): User; } diff --git a/src/Account/UserRepoLucent.php b/src/Account/UserRepoLucent.php new file mode 100644 index 0000000..7f9bdfe --- /dev/null +++ b/src/Account/UserRepoLucent.php @@ -0,0 +1,128 @@ +table($this->tableName)->count(); + } + + /** + * @return Collection + */ + 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 + */ + 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 + */ + 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, + ); + } +} diff --git a/src/Account/UserRepoLunar.php b/src/Account/UserRepoLunar.php new file mode 100644 index 0000000..9b7a787 --- /dev/null +++ b/src/Account/UserRepoLunar.php @@ -0,0 +1,130 @@ +table($this->tableName)->count(); + } + + /** + * @return Collection + */ + 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 + */ + 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 + */ + 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, + ); + } +} diff --git a/src/Channel/ChannelService.php b/src/Channel/ChannelService.php index 75fa258..942c834 100644 --- a/src/Channel/ChannelService.php +++ b/src/Channel/ChannelService.php @@ -2,7 +2,6 @@ namespace Lucent\Channel; - use Lucent\Channel\Data\UserCommand; use Lucent\Primitive\Collection; use Lucent\Schema\Schema; @@ -13,12 +12,7 @@ final class ChannelService { public Channel $channel; - private function __construct( - public SchemaService $schemaService, - ) - { - - } + private function __construct(public SchemaService $schemaService) {} public static function fromConfig(): ChannelService { @@ -28,7 +22,10 @@ final class ChannelService $schemasArray = json_decode($schemasJson, true); } $schemaService = new SchemaService(); - $schemasCollection = (new Collection($schemasArray["schemas"] ?? []))->map([$schemaService, 'fromArray']); + $schemasCollection = new Collection($schemasArray["schemas"] ?? []); + $schemasCollection = $schemasCollection->map( + $schemaService->fromArray(...), + ); $userCommands = []; foreach (config("lucent.commands") ?? [] as $signature => $desc) { @@ -42,7 +39,7 @@ final class ChannelService commands: Collection::make($userCommands), schemas: $schemasCollection, imageFilters: config("lucent.imageFilters") ?? [], - roles: $schemasArray["roles"] ?? [] + roles: $schemasArray["roles"] ?? [], ); $channelService = new ChannelService($schemaService); @@ -69,11 +66,32 @@ final class ChannelService */ public function schemasReadableByRoles(array $roles): array { - $schemasAllRead = $this->channel->schemas->filter(fn(Schema $schema) => empty($schema->read))->values()->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(); - + $schemasAllRead = $this->channel->schemas + ->filter(fn(Schema $schema) => empty($schema->read)) + ->values() + ->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 { - $schemasAllRead = $this->channel->schemas->filter(fn(Schema $schema) => empty($schema->write ?? []))->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(); + $schemasAllRead = $this->channel->schemas + ->filter(fn(Schema $schema) => empty($schema->write ?? [])) + ->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(); } - } diff --git a/src/Command/CommandRepo.php b/src/Command/CommandRepo.php index 4e84fd6..fa94be9 100644 --- a/src/Command/CommandRepo.php +++ b/src/Command/CommandRepo.php @@ -10,7 +10,7 @@ class CommandRepo 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)) { return null; } @@ -22,17 +22,17 @@ class CommandRepo { $foundCommandLogItem = $this->findBySignature($commandLogItem->signature); if (empty($foundCommandLogItem)) { - Database::make()->table("command_logs")->insert(toArray($commandLogItem)); + Database::make()->table("lucent_command_logs")->insert(toArray($commandLogItem)); 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 { Database::make()->update( - 'update command_logs set logs = logs || ? where signature = ?', + 'update lucent_command_logs set logs = logs || ? where signature = ?', [$line, $signature] ); diff --git a/src/Commands/SetupDatabase.php b/src/Commands/SetupDatabase.php index 8f7bdad..01cb383 100644 --- a/src/Commands/SetupDatabase.php +++ b/src/Commands/SetupDatabase.php @@ -4,27 +4,18 @@ namespace Lucent\Commands; use Illuminate\Console\Command; use Illuminate\Database\Schema\Blueprint; +use Illuminate\Support\Facades\DB; use Lucent\Database\Database; 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() { - - $dbConnection = config("lucent.database"); - $databasePath = config("database.connections.$dbConnection.database"); - - if(file_exists($databasePath)){ - $this->error("Database already exists."); - return 0; - } - touch($databasePath); $this->tableUsers(); $this->tableRecords(); $this->tableRevisions(); @@ -36,79 +27,115 @@ class SetupDatabase extends Command 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->string('name')->nullable(); - $table->string('email')->unique(); - $table->jsonb('roles'); - $table->string('createdAt'); - $table->string('updatedAt'); - $table->string('loggedInAt'); - $table->string('mailToken')->nullable(); - + $table->string("name")->nullable(); + $table->string("email")->unique(); + $table->jsonb("roles"); + $table->string("createdAt"); + $table->string("updatedAt"); + $table->string("loggedInAt"); + $table->string("mailToken")->nullable(); }); } private function tableSessions(): void { - Database::make()->getSchemaBuilder()->create('sessions', function (Blueprint $table) { - $table->string('id')->primary(); - $table->foreignId('user_id')->nullable()->index(); - $table->string('ip_address', 45)->nullable(); - $table->text('user_agent')->nullable(); - $table->longText('payload'); - $table->integer('last_activity')->index(); + $schema = Database::make()->getSchemaBuilder(); + if ($schema->hasTable($this->prefix . "sessions")) { + return; + } + $schema->create($this->prefix . "sessions", function ( + Blueprint $table, + ) { + $table->string("id")->primary(); + $table->foreignId("user_id")->nullable()->index(); + $table->string("ip_address", 45)->nullable(); + $table->text("user_agent")->nullable(); + $table->longText("payload"); + $table->integer("last_activity")->index(); }); } private function tableRecords(): void { - Database::make()->getSchemaBuilder()->create('records', function (Blueprint $table) { - $table->uuid('id')->primary(); - $table->string('schema'); - $table->string('status'); - $table->jsonb('data'); - $table->jsonb('_sys'); - $table->jsonb('_file'); - $table->text('search')->default(""); + $schema = Database::make()->getSchemaBuilder(); + if (!$schema->hasTable($this->prefix . "records")) { + $schema->create($this->prefix . "records", function ( + Blueprint $table, + ) { + $table->uuid("id")->primary(); + $table->string("schema"); + $table->string("status"); + $table->jsonb("data"); + $table->jsonb("_sys"); + $table->jsonb("_file"); + $table->text("search")->default(""); - $table->index(['schema', '_sys->updatedAt', 'status']); - $table->index('search'); - }); + // $table->index(["schema", "_sys->updatedAt", "status"]); - Database::make()->getSchemaBuilder()->create('edges', function (Blueprint $table) { - $table->uuid('source'); - $table->uuid('target'); - $table->string('sourceSchema'); - $table->string('targetSchema'); - $table->string('field'); - $table->string('rank'); + $table->index("search"); + }); - $table->unique(['source', 'target', "field"]); - }); + DB::statement( + "CREATE INDEX ON " . + $this->prefix . + 'records (schema, ((_sys->>\'updatedAt\')), status)', + ); + } + + 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 { - Database::make()->getSchemaBuilder()->create('revisions', function (Blueprint $table) { - $table->uuid('id')->primary(); - $table->uuid('recordId'); - $table->string('schema'); - $table->jsonb('data'); - $table->jsonb('_sys'); - $table->jsonb('_file'); - $table->jsonb('_edges'); + $schema = Database::make()->getSchemaBuilder(); + if ($schema->hasTable($this->prefix . "revisions")) { + return; + } + $schema->create($this->prefix . "revisions", function ( + Blueprint $table, + ) { + $table->uuid("id")->primary(); + $table->uuid("recordId"); + $table->string("schema"); + $table->jsonb("data"); + $table->jsonb("_sys"); + $table->jsonb("_file"); + $table->jsonb("_edges"); }); } private function tableCommandLogs(): void { - Database::make()->getSchemaBuilder()->create('command_logs', function (Blueprint $table) { - $table->uuid('id')->primary(); - $table->string('signature'); - $table->integer('pid')->nullable(); - $table->text('logs'); + $schema = Database::make()->getSchemaBuilder(); + if ($schema->hasTable($this->prefix . "command_logs")) { + return; + } + $schema->create($this->prefix . "command_logs", function ( + Blueprint $table, + ) { + $table->uuid("id")->primary(); + $table->string("signature"); + $table->integer("pid")->nullable(); + $table->text("logs"); }); } - } diff --git a/src/Commands/UpgradeFiles122.php b/src/Commands/UpgradeFiles122.php index 51f4ac4..57d6e93 100644 --- a/src/Commands/UpgradeFiles122.php +++ b/src/Commands/UpgradeFiles122.php @@ -17,11 +17,11 @@ class UpgradeFiles122 extends Command $schema = $this->argument('schema'); $disk = $this->argument('disk'); $db = Database::make(); - $records = $db->table("records")->where("schema", $schema)->get(); + $records = $db->table("lucent_records")->where("schema", $schema)->get(); foreach ($records as $record) { $array = json_decode($record->_file, true); $array["disk"] = $disk; - $db->table("records")->where("id", $record->id)->update(["_file" => json_encode($array)]); + $db->table("lucent_records")->where("id", $record->id)->update(["_file" => json_encode($array)]); } } } diff --git a/src/Database/Database.php b/src/Database/Database.php index 125fa81..ae5b4a7 100644 --- a/src/Database/Database.php +++ b/src/Database/Database.php @@ -7,11 +7,10 @@ use Illuminate\Support\Facades\DB; class Database { - public static function make(): Connection{ - $dbConnection = config("lucent.database"); + public static function make(): Connection + { + // $dbConnection = config("lucent.database"); - return DB::connection($dbConnection); + return DB::connection(); } - - -} \ No newline at end of file +} diff --git a/src/Edge/EdgeRepo.php b/src/Edge/EdgeRepo.php index cfee348..7f2a001 100644 --- a/src/Edge/EdgeRepo.php +++ b/src/Edge/EdgeRepo.php @@ -15,7 +15,7 @@ class EdgeRepo public function insert(Edge $edge): void { try { - Database::make()->table("edges")->insert($edge->toDB()); + Database::make()->table("lucent_edges")->insert($edge->toDB()); } catch (PDOException $e) { if ($e->getCode() == 23505) { throw new LucentException("Edge already exists"); @@ -34,7 +34,7 @@ class EdgeRepo { $edgesDB = collect($edges)->map(fn($e) => $e->toDB())->toArray(); try { - Database::make()->table("edges")->insert($edgesDB); + Database::make()->table("lucent_edges")->insert($edgesDB); } catch (PDOException $e) { if ($e->getCode() == 23505) { throw new LucentException("Edge already exists"); @@ -52,8 +52,8 @@ class EdgeRepo public function replaceForRecord(string $from, array $edges): void { $edgesDB = collect($edges)->map(fn($e) => $e->toDB())->toArray(); - Database::make()->table("edges")->where("source", $from)->delete(); - Database::make()->table("edges")->insert($edgesDB); + Database::make()->table("lucent_edges")->where("source", $from)->delete(); + Database::make()->table("lucent_edges")->insert($edgesDB); } @@ -62,13 +62,13 @@ class EdgeRepo */ public function findAll(): array { - $edges = Database::make()->table("edges")->get(); + $edges = Database::make()->table("lucent_edges")->get(); return $edges->map([$this, 'mapEdge'])->toArray(); } 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(); } @@ -89,7 +89,7 @@ class EdgeRepo public function remove(Edge $edge): void { - Database::make()->table("edges") + Database::make()->table("lucent_edges") ->where("source", $edge->source) ->where("target", $edge->target) ->where("sourceSchema", $edge->sourceSchema) @@ -100,7 +100,7 @@ class EdgeRepo public function findLastEdgeRank(string $source, string $field): string { - $data = Database::make()->table("edges") + $data = Database::make()->table("lucent_edges") ->where("source", $source) ->where("field", $field) ->orderBy("rank", "desc") diff --git a/src/File/FileService.php b/src/File/FileService.php index 63595af..12580e8 100644 --- a/src/File/FileService.php +++ b/src/File/FileService.php @@ -123,7 +123,7 @@ class FileService 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("_file->checksum", $checksum) ->where("_file->size", $filesize) diff --git a/src/Http/Controller/AuthController.php b/src/Http/Controller/AuthController.php index 0de07ab..9e17261 100644 --- a/src/Http/Controller/AuthController.php +++ b/src/Http/Controller/AuthController.php @@ -2,7 +2,6 @@ namespace Lucent\Http\Controller; -use App\Http\Controllers\Controller; use Illuminate\Contracts\Session\Session; use Illuminate\Contracts\View\View; use Illuminate\Http\RedirectResponse; @@ -13,44 +12,37 @@ use Lucent\Account\AuthService; use Lucent\Channel\ChannelService; use Lucent\LucentException; use Lucent\Svelte\Svelte; -use Lucent\Util\Form\FormException; use Lucent\Util\Form\ResponseFormError; use function Lucent\Response\fail; use function Lucent\Response\ok; - class AuthController { public function __construct( - private readonly AuthService $authService, + private readonly AuthService $authService, private readonly AccountService $accountService, private readonly ChannelService $channelService, - private readonly Session $session, - private readonly Svelte $svelte, - ) - { - - } + private readonly Session $session, + private readonly Svelte $svelte, + ) {} public function register(Request $request): View|RedirectResponse { if ($this->accountService->countUsers() > 0) { - return redirect($this->channelService->channel->lucentUrl . "/login"); + return redirect( + $this->channelService->channel->lucentUrl . "/login", + ); } - return $this->svelte->render( layout: "account", view: "register", title: "Create an account", - ); } - public function postRegister(Request $request): Response { - if ($this->accountService->countUsers() > 0) { abort(400); } @@ -70,7 +62,9 @@ class AuthController public function login() { if ($this->accountService->countUsers() == 0) { - return redirect($this->channelService->channel->lucentUrl . "/register"); + return redirect( + $this->channelService->channel->lucentUrl . "/register", + ); } return view("lucent::auth.login"); @@ -89,25 +83,24 @@ class AuthController "email" => $request->input("email"), "token" => $request->input("token"), ]); - } public function postVerify(Request $request) { try { - $this->authService->login($request->input("email"), $request->input("token")); + $this->authService->login( + $request->input("email"), + $request->input("token"), + ); } catch (LucentException $th) { return ResponseFormError::fromException($th); } return []; } - public function logout(): RedirectResponse { $this->session->flush(); return redirect($this->channelService->channel->lucentUrl . "/login"); } - - } diff --git a/src/Http/Controller/HomeController.php b/src/Http/Controller/HomeController.php index 049708b..2b9ab75 100644 --- a/src/Http/Controller/HomeController.php +++ b/src/Http/Controller/HomeController.php @@ -14,16 +14,13 @@ use function Lucent\Response\ok; class HomeController extends Controller { public function __construct( - private readonly Svelte $svelte, + private readonly Svelte $svelte, private readonly AccountService $accountService, - private readonly Query $query, - ) - { - } + private readonly Query $query, + ) {} public function home(): View { - return $this->svelte->render( layout: "channel", view: "homeIndex", @@ -38,10 +35,13 @@ class HomeController extends Controller $sort = data_get($urlParams, "sort") ?? "-_sys.updatedAt"; $filter = data_get($urlParams, "filter") ?? []; - $arguments = array_merge([ - "schema_in" => $this->accountService->currentReadableSchemas(), - "status_in" => ["draft", "published"] - ], $filter); + $arguments = array_merge( + [ + "schema_in" => $this->accountService->currentReadableSchemas(), + "status_in" => ["draft", "published"], + ], + $filter, + ); $limit = 20; diff --git a/src/Http/Controller/RecordController.php b/src/Http/Controller/RecordController.php index 7274aab..c6fa399 100644 --- a/src/Http/Controller/RecordController.php +++ b/src/Http/Controller/RecordController.php @@ -27,23 +27,26 @@ use function Lucent\Response\ok; class RecordController extends Controller { public function __construct( - private readonly RecordService $recordService, - private readonly AccountService $accountService, - private readonly ChannelService $channelService, - private readonly Svelte $svelte, - private readonly Query $query, - private readonly Manager $recordManager, + private readonly RecordService $recordService, + private readonly AccountService $accountService, + private readonly ChannelService $channelService, + private readonly Svelte $svelte, + private readonly Query $query, + private readonly Manager $recordManager, private readonly OperatorRegistry $operatorRegistry, private readonly ViewModel $viewModel, - ) - { - } + ) {} public function index(Request $request) { $schemaName = $request->route("schemaName"); - if (!in_array($schemaName, $this->accountService->currentReadableSchemas())) { + if ( + !in_array( + $schemaName, + $this->accountService->currentReadableSchemas(), + ) + ) { return $this->svelte->render( layout: "channel", view: "recordNotFound", @@ -53,16 +56,18 @@ class RecordController extends Controller $users = $this->accountService->all(); $schema = $this->channelService->getSchema($schemaName)->get(); + $urlParams = $request->all(); $sort = data_get($urlParams, "sort") ?? $schema->sortBy; $filter = data_get($urlParams, "filter") ?? []; - $arguments = array_merge([ - "schema" => $schema->name, - "status_in" => "draft,published", - ], $filter); - - + $arguments = array_merge( + [ + "schema" => $schema->name, + "status_in" => "draft,published", + ], + $filter, + ); $skip = data_get($urlParams, "skip") ?? 0; $limit = 30; @@ -81,7 +86,6 @@ class RecordController extends Controller ->parentsDepth(0) ->runWithCount(); - $records = $graph->getRootRecords()->toArray(); $data = [ @@ -93,45 +97,64 @@ class RecordController extends Controller "systemFields" => array_values(System::list()), "operators" => $this->operatorRegistry->all(), "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, "skip" => $skip, "total" => $graph->total ?? 0, "filter" => $request->input("filter") ?? [], "inModal" => true, - "isWritable" => in_array($schemaName, $this->accountService->currentWritableSchemas()) + "isWritable" => in_array( + $schemaName, + $this->accountService->currentWritableSchemas(), + ), ]; if ($request->ajax()) { $data["modalUrl"] = $request->fullUrl(); 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; } $data["inModal"] = false; + return $this->svelte->render( layout: "channel", view: "contentIndex", title: "Records", - data: $data + data: $data, ); } public function exportCSV(Request $request) { $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(); $sort = data_get($urlParams, "sort") ?? "-_sys.updatedAt"; $filter = data_get($urlParams, "filter") ?? []; - $arguments = array_merge([ - "schema" => $schema->name, - "status_in" => "draft,published", - ], $filter); - + $arguments = array_merge( + [ + "schema" => $schema->name, + "status_in" => "draft,published", + ], + $filter, + ); $records = $this->query ->filter($arguments) @@ -143,53 +166,75 @@ class RecordController extends Controller ->run() ->tree(); - header('Content-Type: application/csv'); - header('Content-Disposition: attachment; filename="' . $schemaName . '.csv";'); - $handle = fopen('php://output', 'w'); + header("Content-Type: application/csv"); + header( + 'Content-Disposition: attachment; filename="' . + $schemaName . + '.csv";', + ); + $handle = fopen("php://output", "w"); $relationColumns = $this->makeCsvRelationColumns($schema); - $csvRow = ["id", ...array_keys($records[0]->data->toArray()),...$relationColumns]; - fputcsv($handle, $csvRow, ','); + $csvRow = [ + "id", + ...array_keys($records[0]->data->toArray()), + ...$relationColumns, + ]; + fputcsv($handle, $csvRow, ","); foreach ($records as $record) { - $csvRow = [$record->id, ...$record->data->toArray()]; - $csvRow = array_merge($csvRow,$this->makeCsvRelationColumnValues($schema,$record->_children)); + $csvRow = array_merge( + $csvRow, + $this->makeCsvRelationColumnValues($schema, $record->_children), + ); $csvRow = array_values($csvRow); - fputcsv($handle, $csvRow, ','); + fputcsv($handle, $csvRow, ","); } fclose($handle); echo $handle; - exit; + exit(); } - private function makeCsvRelationColumns($schema):array{ - return $schema->fields->filter(fn($f) => get_class($f) === Reference::class)->reduce(function($c,$f){ - $c[] = $f->name." id"; - $c[] = $f->name." name"; - return $c; - },[]); + private function makeCsvRelationColumns($schema): array + { + return $schema->fields + ->filter(fn($f) => get_class($f) === Reference::class) + ->reduce(function ($c, $f) { + $c[] = $f->name . " id"; + $c[] = $f->name . " name"; + return $c; + }, []); } - private function makeCsvRelationColumnValues($schema, $children):array{ - return $schema->fields->filter(fn($f) => get_class($f) === Reference::class)->reduce(function($c,$f) use($children){ - - $fieldRecords = data_get($children,$f->name); - if(empty($fieldRecords)){ - $c[] = ""; - $c[] = ""; - }elseif (count($fieldRecords) === 1){ - $c[] = data_get($fieldRecords,"0.id"); - $c[] = $this->viewModel->getRecordName($fieldRecords[0]); - }else{ - $c[] = collect($fieldRecords)->pluck("id")->join("::"); - $c[] = collect($fieldRecords)->pluck("data.name")->join("::"); - } - return $c; - },[]); + private function makeCsvRelationColumnValues($schema, $children): array + { + return $schema->fields + ->filter(fn($f) => get_class($f) === Reference::class) + ->reduce(function ($c, $f) use ($children) { + $fieldRecords = data_get($children, $f->name); + if (empty($fieldRecords)) { + $c[] = ""; + $c[] = ""; + } elseif (count($fieldRecords) === 1) { + $c[] = data_get($fieldRecords, "0.id"); + $c[] = $this->viewModel->getRecordName($fieldRecords[0]); + } else { + $c[] = collect($fieldRecords)->pluck("id")->join("::"); + $c[] = collect($fieldRecords) + ->pluck("data.name") + ->join("::"); + } + return $c; + }, []); } public function new(Request $request) { - if (!in_array($request->input("schema"), $this->accountService->currentWritableSchemas())) { + if ( + !in_array( + $request->input("schema"), + $this->accountService->currentWritableSchemas(), + ) + ) { return $this->svelte->render( layout: "channel", view: "recordNotFound", @@ -197,8 +242,12 @@ class RecordController extends Controller ); } - $schema = $this->channelService->channel->schemas->where("name", $request->input("schema"))->first(); - $recordHistory = $this->recordManager->fromSession($request->session())->getRecords(); + $schema = $this->channelService->channel->schemas + ->where("name", $request->input("schema")) + ->first(); + $recordHistory = $this->recordManager + ->fromSession($request->session()) + ->getRecords(); $record = $this->recordService->createEmpty($schema); $queryRecord = QueryRecord::fromRecord($record); return $this->svelte->render( @@ -210,15 +259,22 @@ class RecordController extends Controller "record" => $queryRecord, "recordHistory" => $recordHistory, "isCreateMode" => true, - "isWritable" => in_array($record->schema, $this->accountService->currentWritableSchemas()) - ] + "isWritable" => in_array( + $record->schema, + $this->accountService->currentWritableSchemas(), + ), + ], ); } - 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( layout: "channel", 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); $queryRecord = QueryRecord::fromRecord($record); @@ -234,7 +292,10 @@ class RecordController extends Controller "schema" => $schema, "record" => $queryRecord, "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(); - if (!in_array($record->schema, $this->accountService->currentReadableSchemas())) { + if ( + !in_array( + $record->schema, + $this->accountService->currentReadableSchemas(), + ) + ) { return $this->svelte->render( layout: "channel", view: "recordNotFound", @@ -271,7 +337,10 @@ class RecordController extends Controller } $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( layout: "channel", view: "recordEdit", @@ -282,8 +351,11 @@ class RecordController extends Controller "record" => toArray($record), "users" => $this->accountService->all(), "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 (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") { - $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") == "search") { $arguments["search_regex"] = $request->input("value"); } } - $records = $this->query - ->filter($arguments) - ->limit(10) - ->tree(); + $records = $this->query->filter($arguments)->limit(10)->tree(); if ($records->isEmpty()) { return ok([]); @@ -320,7 +393,6 @@ class RecordController extends Controller { $recordId = $request->input("record.id"); try { - if ($request->input("isCreateMode")) { $recordId = $this->recordService->create( data: new RecordInputData( @@ -329,15 +401,20 @@ class RecordController extends Controller $request->input("record.data"), Status::from($request->input("record.status")), ), - edges: array_map(EdgeInputData::fromArray(...), $request->input("edges") ?? []) + edges: array_map( + EdgeInputData::fromArray(...), + $request->input("edges") ?? [], + ), ); } else { - $this->recordService->updateWithEdges( id: $request->input("record.id"), data: $request->input("record.data"), 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) ->parentsDepth(1) ->run(); - } catch (ValidatorException $th) { return fail($th->getValidatorErrors()); } catch (LucentException $th) { @@ -356,11 +432,9 @@ class RecordController extends Controller return ok(toArray($newGraph)); } - public function clone(Request $request) { try { - $newRecordId = $this->recordService->clone( recordId: $request->route("rid"), ); @@ -374,10 +448,8 @@ class RecordController extends Controller public function status(Request $request) { - $ids = array_map(fn($rec) => $rec["id"], $request->input("records")); - $this->recordService->changeStatusBulk( status: $request->route("status"), recordsIds: $ids, @@ -387,9 +459,12 @@ class RecordController extends Controller public function emptyTrash(Request $request) { - $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) @@ -409,11 +484,11 @@ class RecordController extends Controller try { $this->recordService->rollback( recordId: $request->route("rid"), - version: (int)$request->route("version") + version: (int) $request->route("version"), ); } catch (ValidatorException $th) { return fail($th->getFirstValidatorError()); - } catch (LucentException|Throwable $th) { + } catch (LucentException | Throwable $th) { return fail($th); } return ok(); diff --git a/src/Http/Middleware/AuthMiddleware.php b/src/Http/Middleware/AuthMiddleware.php index a274b37..8f271e5 100644 --- a/src/Http/Middleware/AuthMiddleware.php +++ b/src/Http/Middleware/AuthMiddleware.php @@ -16,21 +16,29 @@ readonly class AuthMiddleware private AuthService $authService, private AccountService $accountService, private ChannelService $channelService, - private ViewModel $viewModel - ) - { - } + private ViewModel $viewModel, + ) {} public function handle(Request $request, Closure $next) { if (!$this->authService->isLoggedIn()) { - return redirect($this->channelService->channel->lucentUrl . "/login"); + return redirect( + $this->channelService->channel->lucentUrl . "/login", + ); } $this->authService->refreshSession(); - View::share("channel",$this->channelService->channel); - View::share("user",session("user")); - View::share("schemas",$this->channelService->channel->schemas->whereIn("name",$this->accountService->currentReadableSchemas())->values()); - View::share("viewModel",$this->viewModel); + View::share("channel", $this->channelService->channel); + View::share("user", toArray($this->authService->getCurrentUser())); + View::share( + "schemas", + $this->channelService->channel->schemas + ->whereIn( + "name", + $this->accountService->currentReadableSchemas(), + ) + ->values(), + ); + View::share("viewModel", $this->viewModel); return $next($request); } diff --git a/src/LucentServiceProvider.php b/src/LucentServiceProvider.php index 61bf101..2fc4d1a 100644 --- a/src/LucentServiceProvider.php +++ b/src/LucentServiceProvider.php @@ -7,6 +7,10 @@ use Illuminate\Support\Facades\Blade; use Illuminate\Support\Facades\View; use Illuminate\Support\ServiceProvider; 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\Commands\CompileSchemas; use Lucent\Commands\GenerateCollectionSchema; @@ -17,10 +21,8 @@ use Lucent\Commands\RemoveOrphanEdges; use Lucent\Commands\SetupDatabase; use Lucent\Commands\UpgradeFiles122; use Lucent\File\FileService; -use Lucent\File\ImageService; use Lucent\Query\DatabaseGraph\DatabaseGraph; use Lucent\Query\DatabaseGraph\PgsqlDatabaseGraph; -use Lucent\Query\DatabaseGraph\SqliteDatabaseGraph; class LucentServiceProvider extends ServiceProvider { @@ -34,21 +36,20 @@ class LucentServiceProvider extends ServiceProvider }); $this->app->bind(ImageManager::class, function () { - return new ImageManager(['driver' => 'imagick']); + return new ImageManager(["driver" => "imagick"]); }); - - $this->app->bind(DatabaseGraph::class, function () { - $dbConnection = config("lucent.database"); - return match (config("database.connections.$dbConnection.driver")) { - "sqlite" => new SqliteDatabaseGraph(), - "pgsql" => new PgsqlDatabaseGraph(), - }; + return 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 { - - $manifestPath = public_path('vendor/lucent/dist/manifest.json'); + $manifestPath = public_path("vendor/lucent/dist/manifest.json"); $manifest = null; 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); - $router->aliasMiddleware('lucent.guest', \Lucent\Http\Middleware\GuestMiddleware::class); - - $this->loadViewsFrom(__DIR__ . '/../front/views', 'lucent'); - $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()) { $this->commands([ @@ -84,19 +94,28 @@ class LucentServiceProvider extends ServiceProvider ]); } - View::share('manifest', $manifest); - View::share('file', app()->make(FileService::class)); - Blade::anonymousComponentPath(__DIR__ . '../front/views/components', "lucent"); - - $this->publishes([ - __DIR__ . '/Config/main.php' => config_path('lucent.php'), - ],"lucent-config"); - - $this->publishes([ - __DIR__ . '/../front/dist' => public_path('vendor/lucent/dist'), - __DIR__ . '/../front/public' => public_path('vendor/lucent/public'), - ], 'lucent'); + View::share("manifest", $manifest); + View::share("file", app()->make(FileService::class)); + Blade::anonymousComponentPath( + __DIR__ . "../front/views/components", + "lucent", + ); + $this->publishes( + [ + __DIR__ . "/Config/main.php" => config_path("lucent.php"), + ], + "lucent-config", + ); + $this->publishes( + [ + __DIR__ . "/../front/dist" => public_path("vendor/lucent/dist"), + __DIR__ . "/../front/public" => public_path( + "vendor/lucent/public", + ), + ], + "lucent", + ); } } diff --git a/src/Query/DatabaseGraph/PgsqlDatabaseGraph.php b/src/Query/DatabaseGraph/PgsqlDatabaseGraph.php index 606e1b1..d606201 100644 --- a/src/Query/DatabaseGraph/PgsqlDatabaseGraph.php +++ b/src/Query/DatabaseGraph/PgsqlDatabaseGraph.php @@ -14,7 +14,7 @@ class PgsqlDatabaseGraph implements DatabaseGraph */ 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 ')) ->whereIn('source', $ids); @@ -24,7 +24,7 @@ class PgsqlDatabaseGraph implements DatabaseGraph $subquery->limit($options->childrenLimit) ->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') ->whereRaw("g.source = sg.target") ->where("depth", "<", $options->childrenDepth) @@ -42,12 +42,12 @@ class PgsqlDatabaseGraph implements DatabaseGraph */ 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 ')) ->limit($options->parentsLimit) ->whereIn('g.target', $ids) ->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') ->whereRaw("g.target = sg.source") ->where("depth", "<", $options->parentsDepth) diff --git a/src/Query/DatabaseGraph/SqliteDatabaseGraph.php b/src/Query/DatabaseGraph/SqliteDatabaseGraph.php index 423bad0..bf37462 100644 --- a/src/Query/DatabaseGraph/SqliteDatabaseGraph.php +++ b/src/Query/DatabaseGraph/SqliteDatabaseGraph.php @@ -13,7 +13,7 @@ class SqliteDatabaseGraph implements DatabaseGraph */ 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 ')) ->whereIn('source', $ids); @@ -23,7 +23,7 @@ class SqliteDatabaseGraph implements DatabaseGraph $subquery->limit($options->childrenLimit) ->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') ->whereRaw("g.source = sg.target") ->where("depth", "<", $options->childrenDepth) @@ -41,12 +41,12 @@ class SqliteDatabaseGraph implements DatabaseGraph */ 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 ')) ->limit($options->parentsLimit) ->whereIn('g.target', $ids) ->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') ->whereRaw("g.target = sg.source") ->where("depth", "<", $options->parentsDepth) diff --git a/src/Query/FilterParser.php b/src/Query/FilterParser.php index 1d6d987..2ea957b 100644 --- a/src/Query/FilterParser.php +++ b/src/Query/FilterParser.php @@ -59,7 +59,7 @@ final class FilterParser } $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()); }, []); diff --git a/src/Query/Query.php b/src/Query/Query.php index e034c59..3b4edc3 100644 --- a/src/Query/Query.php +++ b/src/Query/Query.php @@ -13,9 +13,8 @@ use Lucent\Record\InputFormatter; use Lucent\Record\QueryRecord; use Lucent\Record\Record; -final class Query +final class Query { - /** * @var array $filters */ @@ -23,11 +22,10 @@ final class Query public QueryOptions $options; public function __construct( - public readonly FilterParser $filterParser, + public readonly FilterParser $filterParser, public readonly InputFormatter $inputFormatter, - public readonly DatabaseGraph $databaseGraph, - ) - { + public readonly DatabaseGraph $databaseGraph, + ) { $this->options = new QueryOptions(); } @@ -43,7 +41,6 @@ final class Query return $this; } - public function run(): Graph { $resultsRecords = $this->mainQuery(); @@ -55,33 +52,53 @@ final class Query $resultChildrenEdges = []; if ($this->options->childrenDepth > 0 && !empty($ids)) { $resultChildrenEdges = $this->getChildren($ids); - $resultChildrenEdgesTargetIds = array_map(fn($e) => $e->target, $resultChildrenEdges); + $resultChildrenEdgesTargetIds = array_map( + fn($e) => $e->target, + $resultChildrenEdges, + ); } $resultParentSourceTargetIds = []; $resultParentEdges = []; if ($this->options->parentsDepth > 0 && !empty($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 = []; if (!empty($edgesIds)) { - $edgeRecords = Database::make()->table('records') + $edgeRecords = Database::make() + ->table("lucent_records") ->whereIn("id", $edgesIds) ->whereIn("status", $this->options->status) - ->get()->toArray(); + ->get() + ->toArray(); } - $resultsRecordsUnique = collect(array_merge($resultsRecords, $edgeRecords))->unique("id")->values()->toArray(); -// $resultEdges = collect(array_merge($resultChildrenEdges, $resultParentEdges)) -// ->unique(fn($edge) => $edge->source . $edge->target . $edge->field) -// ->toArray(); + $resultsRecordsUnique = collect( + array_merge($resultsRecords, $edgeRecords), + ) + ->unique("id") + ->values() + ->toArray(); + // $resultEdges = collect(array_merge($resultChildrenEdges, $resultParentEdges)) + // ->unique(fn($edge) => $edge->source . $edge->target . $edge->field) + // ->toArray(); - $formattedRecords = $this->formatRecords($resultsRecordsUnique, $resultChildrenEdges, $resultParentEdges); + $formattedRecords = $this->formatRecords( + $resultsRecordsUnique, + $resultChildrenEdges, + $resultParentEdges, + ); $this->reset(); return $formattedRecords; - } private function reset() @@ -90,33 +107,41 @@ final class Query $this->filters = []; } - private function formatRecords(array $records, array $edges, array $parentEdges): Graph - { - $queryRecords = collect($records)->map(function ($recordData) { + private function formatRecords( + array $records, + 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->isRoot = data_get($recordData, "isRoot") === true; + return $queryRecord; + }) + ->toArray(); - $queryRecord = QueryRecord::fromRecord($record); - $queryRecord->isRoot = data_get($recordData, "isRoot") === true; - return $queryRecord; - })->toArray(); - - - $queryEdges = collect($edges)->map(function ($edgeData) { - - return Edge::fromArray((array)$edgeData); - - })->sortBy("rank")->values()->toArray(); - - $queryParentEdges = collect($parentEdges)->map(function ($edgeData) { - - - return Edge::fromArray((array)$edgeData); - - })->sortBy("rank")->values()->toArray(); + $queryEdges = collect($edges) + ->map(function ($edgeData) { + return Edge::fromArray((array) $edgeData); + }) + ->sortBy("rank") + ->values() + ->toArray(); + $queryParentEdges = collect($parentEdges) + ->map(function ($edgeData) { + return Edge::fromArray((array) $edgeData); + }) + ->sortBy("rank") + ->values() + ->toArray(); return new Graph( new Collection($queryRecords), @@ -126,13 +151,11 @@ final class Query ); } - public function tree(): Collection { return $this->run()->tree(); } - private function parseFilters(Builder $query): Builder { foreach ($this->filters as $filter) { @@ -142,10 +165,9 @@ final class Query return $query; } - private function mainQuery(): array { - $query = Database::make()->table("records"); + $query = Database::make()->table("lucent_records"); $query = $this->parseFilters($query); $query = $this->findNotLinked($query); @@ -155,13 +177,15 @@ final class Query } $query = $this->orderByQuery($query); - return $query->get()->map(function ($r) { - $r->isRoot = true; - return $r; - })->toArray(); + return $query + ->get() + ->map(function ($r) { + $r->isRoot = true; + return $r; + }) + ->toArray(); } - private function findNotLinked(Builder $query): Builder { if (empty($this->options->notLinked)) { @@ -170,14 +194,19 @@ final class Query // This returns only records that have no parents $query - ->select("records.*") - ->join('edges', 'records.id', '=', 'edges.target', 'left outer') - ->whereNull("edges.target"); + ->select("lucent_records.*") + ->join( + "lucent_edges", + "lucent_records.id", + "=", + "lucent_edges.target", + "left outer", + ) + ->whereNull("lucent_edges.target"); return $query; } - private - function getChildren(array $ids): array + private function getChildren(array $ids): array { return $this->databaseGraph->getChildren($ids, $this->options); } @@ -187,12 +216,9 @@ final class Query return $this->databaseGraph->getParents($ids, $this->options); } - - public - function runWithCount(): Graph + public function runWithCount(): Graph { - - $query = Database::make()->table("records"); + $query = Database::make()->table("lucent_records"); $query = $this->parseFilters($query); $query = $this->findNotLinked($query); $graph = $this->run(); @@ -200,15 +226,13 @@ final class Query return $graph; } - public function limit(int $limit): Query { $this->options->limit = $limit; return $this; } - public - function skip(int $skip): Query + public function skip(int $skip): Query { $this->options->skip = $skip; return $this; @@ -250,7 +274,6 @@ final class Query return $this; } - public function sort(string $sort): Query { $this->options->sort[] = $sort; @@ -269,12 +292,11 @@ final class Query return $this; } - public - function orderByQuery(Builder $query): Builder + public function orderByQuery(Builder $query): Builder { foreach ($this->options->sort as $item) { - $field = str_replace(".", "->", ltrim($item, '-')); - $dir = str_starts_with($item, '-') ? "desc" : "asc"; + $field = str_replace(".", "->", ltrim($item, "-")); + $dir = str_starts_with($item, "-") ? "desc" : "asc"; if ($field) { $query->orderBy($field, $dir); } diff --git a/src/Record/RecordRepo.php b/src/Record/RecordRepo.php index a8dcd89..360d77a 100644 --- a/src/Record/RecordRepo.php +++ b/src/Record/RecordRepo.php @@ -6,16 +6,13 @@ use Lucent\Database\Database; class RecordRepo { - public function __construct() - { - } + public function __construct() {} public static function create(Record $record): void { $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 { - Database::make()->table("records")->whereIn("id", $ids)->update([ - 'status' => $status->value - ]); + Database::make() + ->table("lucent_records") + ->whereIn("id", $ids) + ->update([ + "status" => $status->value, + ]); } public static function update(Record $record): void { $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 */ - public function deleteMany( - array $ids, - ): void + public function deleteMany(array $ids): void { - - Database::make()->table("records")->whereIn("id", $ids)->delete(); - Database::make()->table("edges")->whereIn("source", $ids)->delete(); - Database::make()->table("edges")->whereIn("target", $ids)->delete(); - Database::make()->table("revisions")->whereIn("recordId", $ids)->delete(); + Database::make() + ->table("lucent_records") + ->whereIn("id", $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( - string $schemaName, - ): void + public function deleteTrashedBySchema(string $schemaName): void { - $ids = Database::make()->table("records") + $ids = Database::make() + ->table("lucent_records") ->where("schema", $schemaName) ->where("status", Status::TRASHED->value) - ->get()->pluck("id")->toArray(); + ->get() + ->pluck("id") + ->toArray(); $this->deleteMany($ids); } diff --git a/src/Record/RecordService.php b/src/Record/RecordService.php index c3f29bf..c39cb64 100644 --- a/src/Record/RecordService.php +++ b/src/Record/RecordService.php @@ -23,20 +23,17 @@ use Lucent\Schema\Validator\ValidatorException; readonly class RecordService { - public function __construct( - private AuthService $authService, + private AuthService $authService, private RevisionService $revisionService, - private ChannelService $channelService, - private Validator $recordValidator, - private Query $query, - private InputFormatter $inputFormatter, - private RecordRepo $recordRepo, - private EdgeService $edgeService, - private FileService $fileService, - ) - { - } + private ChannelService $channelService, + private Validator $recordValidator, + private Query $query, + private InputFormatter $inputFormatter, + private RecordRepo $recordRepo, + private EdgeService $edgeService, + private FileService $fileService, + ) {} /** * @param string $url @@ -47,14 +44,15 @@ readonly class RecordService * @throws ValidatorException */ public function createFromUrl( - string $url, + string $url, RecordInputData $data, - array $edges - ): string - { + array $edges, + ): string { $schema = $this->channelService->getSchema($data->schemaName)->get(); 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); if ($fileData->isDuplicate) { @@ -64,14 +62,15 @@ readonly class RecordService } public function createFromUploadedFile( - UploadedFile $uploadedFile, + UploadedFile $uploadedFile, RecordInputData $data, - array $edges - ): string - { + array $edges, + ): string { $schema = $this->channelService->getSchema($data->schemaName)->get(); 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); if ($fileData->isDuplicate) { @@ -81,14 +80,15 @@ readonly class RecordService } public function createFromFileData( - FileData $fileData, + FileData $fileData, RecordInputData $data, - array $edges - ): string - { + array $edges, + ): string { $schema = $this->channelService->getSchema($data->schemaName)->get(); 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); } @@ -102,12 +102,13 @@ readonly class RecordService */ public function create( RecordInputData $data, - ?FileData $file = null, - array $edges = [] - ): string - { - - $formattedData = $this->inputFormatter->fill($data->schemaName, new RecordData($data->data)); + ?FileData $file = null, + array $edges = [], + ): string { + $formattedData = $this->inputFormatter->fill( + $data->schemaName, + new RecordData($data->data), + ); $newRecordId = empty($data->id) ? Id::new() : $data->id; $record = new Record( @@ -120,38 +121,49 @@ readonly class RecordService ); if ($data->status === Status::PUBLISHED) { - $errors = $this->recordValidator->check($data->schemaName, $record->data); + $errors = $this->recordValidator->check( + $data->schemaName, + $record->data, + ); if ($errors->isNotEmpty()) { $this->recordValidator->throwException($errors); } } 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); return $record->id; - } /** * @throws LucentException * @throws ValidatorException */ - public function update( - string $id, - array $data, - Status $status, - ): void + public function update(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)) { 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) { - $errors = $this->recordValidator->check($record->schema, $formattedData); + $errors = $this->recordValidator->check( + $record->schema, + $formattedData, + ); if ($errors->isNotEmpty()) { $this->recordValidator->throwException($errors); } @@ -169,7 +181,6 @@ readonly class RecordService RecordRepo::update($newRecord); $newEdges = $this->edgeService->findForSource($record->id); $this->revisionService->create($newRecord, $newEdges); - } /** @@ -178,20 +189,28 @@ readonly class RecordService */ public function updateWithEdges( string $id, - array $data, + array $data, Status $status, - array $edges - ): void - { - $record = $this->query->filter(["id" => $id])->run()->records->first(); + array $edges, + ): void { + $record = $this->query + ->filter(["id" => $id]) + ->run() + ->records->first(); if (empty($record)) { 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) { - $errors = $this->recordValidator->check($record->schema, $formattedData); + $errors = $this->recordValidator->check( + $record->schema, + $formattedData, + ); if ($errors->isNotEmpty()) { $this->recordValidator->throwException($errors); } @@ -207,15 +226,15 @@ readonly class RecordService ); 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); - } - public function changeStatusBulk( - string $status, - array $recordsIds, - ): void + public function changeStatusBulk(string $status, array $recordsIds): void { RecordRepo::updateStatusBulk(Status::from($status), $recordsIds); } @@ -224,10 +243,7 @@ readonly class RecordService * @throws LucentException * @throws ValidatorException */ - public - function clone( - string $recordId, - ): string + public function clone(string $recordId): string { $graph = $this->query ->filter(["id" => $recordId]) @@ -240,15 +256,18 @@ readonly class RecordService throw new LucentException("Record id is missing"); } - $newRecordId = (string)Str::uuid(); + $newRecordId = (string) Str::uuid(); $newEdgesData = $graph->edges ->filter(fn(Edge $edge) => $edge->source == $recordId) ->values() - ->map(fn(Edge $edge) => new EdgeInputData( - target: $edge->target, - targetSchema: $edge->targetSchema, - field: $edge->field, - ))->toArray(); + ->map( + fn(Edge $edge) => new EdgeInputData( + target: $edge->target, + targetSchema: $edge->targetSchema, + field: $edge->field, + ), + ) + ->toArray(); $record->id = $newRecordId; @@ -257,62 +276,62 @@ readonly class RecordService schemaName: $record->schema, id: $record->id, data: $record->data->toArray(), - status: Status::DRAFT + status: Status::DRAFT, ), file: $record->_file, - edges: $newEdgesData + edges: $newEdgesData, ); - } - - public function deleteMany( - array $recordsIds, - ): void + public function deleteMany(array $recordsIds): void { $this->recordRepo->deleteMany($recordsIds); } - public function emptyTrash( - string $schemaName, - ): void + public function emptyTrash(string $schemaName): void { - $schema = $this->channelService->getSchema($schemaName)->get(); $this->recordRepo->deleteTrashedBySchema($schemaName); } - /** * @throws LucentException * @throws ValidatorException */ - public function rollback( - string $recordId, - int $version, - ): void + public function rollback(string $recordId, int $version): void { - $revision = $this->revisionService->getByRecordIdAndVersion($recordId, $version)->get(); + $revision = $this->revisionService + ->getByRecordIdAndVersion($recordId, $version) + ->get(); $this->updateWithEdges( id: $revision->recordId, data: $revision->data->toArray(), 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 ($carry, FieldInterface $f) { + $defaultValues = $schema->fields->reduce(function ( + $carry, + FieldInterface $f, + ) { $carry[$f->name] = $f->default ?? null; return $carry; }, []); - $formattedData = $this->inputFormatter->fill($schema->name, new RecordData($defaultValues)); + $formattedData = $this->inputFormatter->fill( + $schema->name, + new RecordData($defaultValues), + ); return new Record( id: Id::new(), @@ -322,6 +341,5 @@ readonly class RecordService data: $formattedData, _file: null, ); - } } diff --git a/src/Revision/RevisionRepo.php b/src/Revision/RevisionRepo.php index 697d27d..bbac73f 100644 --- a/src/Revision/RevisionRepo.php +++ b/src/Revision/RevisionRepo.php @@ -14,7 +14,7 @@ use stdClass; class RevisionRepo { - private string $table = "revisions"; + private string $table = "lucent_revisions"; public function create(Revision $revision): string { diff --git a/src/Setup/Step/DatabaseSetupStep.php b/src/Setup/Step/DatabaseSetupStep.php index aa63d7f..3fde891 100644 --- a/src/Setup/Step/DatabaseSetupStep.php +++ b/src/Setup/Step/DatabaseSetupStep.php @@ -8,53 +8,23 @@ use Lucent\Setup\Data\SetupStep; class DatabaseSetupStep implements IStep { - public function __invoke(): SetupStep { - - $name = "Database Connection"; - $databaseConnectionName = config("lucent.database"); - - - $databaseConfig = config('database.connections.'.$databaseConnectionName); - - if (empty($databaseConfig)) { - $instructions = << [ - '$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 = <<table("records")->get(); + Database::make()->table("lucent_records")->get(); } catch (QueryException $e) { $instructions = <<authService->getCurrentUser()); $context["view"] = $view; $context["layout"] = $layout; $context["title"] = $title; $context["data"] = $data; $context["channel"] = $this->channelService->channel; - $context["readableSchemas"] = $this->accountService->currentReadableSchemas(); + $context[ + "readableSchemas" + ] = $this->accountService->currentReadableSchemas(); $json = json_encode($context); - $divTag = sprintf('
', $layout); - $jsonTag = sprintf('', $layout, $json); + $divTag = sprintf( + '
', + $layout, + ); + $jsonTag = sprintf( + '', + $layout, + $json, + ); $svelte = $divTag . $jsonTag; - return view('lucent::svelte', [ - 'svelte' => $svelte, - 'view' => $view, - 'data' => $data, - 'title' => $title, - 'layout' => $layout, - 'channel' => $this->channelService->channel, + return view("lucent::svelte", [ + "svelte" => $svelte, + "view" => $view, + "data" => $data, + "title" => $title, + "layout" => $layout, + "channel" => $this->channelService->channel, ]); } - } -