Compare commits
8 Commits
a9851847fc
...
dev-lunar
| Author | SHA1 | Date | |
|---|---|---|---|
| 454cece1d8 | |||
| c49acd74de | |||
| 965b7e660b | |||
| 8c7f65abf5 | |||
| 507d643aee | |||
| f1a0d6a2b1 | |||
| f74c850e01 | |||
| 9d6d39fe62 |
@@ -9,7 +9,6 @@
|
||||
"ext-imagick": "*",
|
||||
"ext-pdo": "*",
|
||||
"php": "^8.4",
|
||||
"phpoption/phpoption": "^1.9",
|
||||
"spatie/image-optimizer": "^1.8",
|
||||
"staudenmeir/laravel-cte": "^1.0",
|
||||
"intervention/image": "^4.0"
|
||||
|
||||
Generated
+7
-7
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "b2f189b5c64498c6190267db27e55494",
|
||||
"content-hash": "a1bf12f1e2b86bc0da8547f2f6944a86",
|
||||
"packages": [
|
||||
{
|
||||
"name": "brick/math",
|
||||
@@ -797,16 +797,16 @@
|
||||
},
|
||||
{
|
||||
"name": "intervention/image",
|
||||
"version": "4.0.4",
|
||||
"version": "4.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Intervention/image.git",
|
||||
"reference": "f58d379b1f13c036b2ef5c3c26eb4b0c88b647ed"
|
||||
"reference": "fb795553f76afbe55c80d32b6bfe2090a6b1a0af"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Intervention/image/zipball/f58d379b1f13c036b2ef5c3c26eb4b0c88b647ed",
|
||||
"reference": "f58d379b1f13c036b2ef5c3c26eb4b0c88b647ed",
|
||||
"url": "https://api.github.com/repos/Intervention/image/zipball/fb795553f76afbe55c80d32b6bfe2090a6b1a0af",
|
||||
"reference": "fb795553f76afbe55c80d32b6bfe2090a6b1a0af",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -853,7 +853,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/Intervention/image/issues",
|
||||
"source": "https://github.com/Intervention/image/tree/4.0.4"
|
||||
"source": "https://github.com/Intervention/image/tree/4.1.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -869,7 +869,7 @@
|
||||
"type": "ko_fi"
|
||||
}
|
||||
],
|
||||
"time": "2026-05-03T04:47:13+00:00"
|
||||
"time": "2026-05-15T06:52:36+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/framework",
|
||||
|
||||
@@ -50,4 +50,6 @@ interface AuthService
|
||||
public function registerAdmin(string $name, string $email): User;
|
||||
|
||||
public function validateRoles(array $roles): array;
|
||||
public function isExternal(): bool;
|
||||
public function redirectHome(): \Illuminate\Routing\Redirector|\Illuminate\Http\RedirectResponse;
|
||||
}
|
||||
|
||||
@@ -220,4 +220,13 @@ readonly class AuthServiceLucent implements AuthService
|
||||
->values()
|
||||
->toArray();
|
||||
}
|
||||
|
||||
public function isExternal(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
public function redirectHome(): \Illuminate\Routing\Redirector|\Illuminate\Http\RedirectResponse
|
||||
{
|
||||
return redirect("/home");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,4 +102,13 @@ readonly class AuthServiceLunar implements AuthService
|
||||
->values()
|
||||
->toArray();
|
||||
}
|
||||
|
||||
public function isExternal(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
public function redirectHome(): \Illuminate\Routing\Redirector|\Illuminate\Http\RedirectResponse
|
||||
{
|
||||
return redirect("/lunar");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
namespace Lucent\Account;
|
||||
|
||||
use Lucent\Primitive\Collection;
|
||||
use PhpOption\Option;
|
||||
use Lucent\Option\Option;
|
||||
|
||||
interface UserRepo
|
||||
{
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace Lucent\Account;
|
||||
use Carbon\Carbon;
|
||||
use Lucent\Database\Database;
|
||||
use Lucent\Primitive\Collection;
|
||||
use PhpOption\Option;
|
||||
use Lucent\Option\Option;
|
||||
|
||||
class UserRepoLucent implements UserRepo
|
||||
{
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace Lucent\Account;
|
||||
use Carbon\Carbon;
|
||||
use Lucent\Database\Database;
|
||||
use Lucent\Primitive\Collection;
|
||||
use PhpOption\Option;
|
||||
use Lucent\Option\Option;
|
||||
|
||||
class UserRepoLunar implements UserRepo
|
||||
{
|
||||
|
||||
@@ -8,7 +8,7 @@ use Lucent\File\FileService;
|
||||
use Lucent\Primitive\Collection;
|
||||
use Lucent\Data\Schema;
|
||||
use Lucent\Schema\SchemaService;
|
||||
use PhpOption\Option;
|
||||
use Lucent\Option\Option;
|
||||
|
||||
final class ChannelService
|
||||
{
|
||||
|
||||
@@ -8,7 +8,7 @@ use Lucent\Data\Schema;
|
||||
use Lucent\Schema\SchemaService;
|
||||
use Lucent\File\FileService;
|
||||
|
||||
class CompileSchemas extends Command
|
||||
class CompileSchemasCommand extends Command
|
||||
{
|
||||
protected $signature = "lucent:schemas";
|
||||
|
||||
@@ -4,12 +4,14 @@ namespace Lucent\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Lucent\File\FileService;
|
||||
use Lucent\ResultType\Error;
|
||||
use Lucent\ResultType\Result;
|
||||
use Lucent\ResultType\Success;
|
||||
use ZipArchive;
|
||||
|
||||
class Export extends Command
|
||||
class ExportCommand extends Command
|
||||
{
|
||||
protected $signature = "lucent:export";
|
||||
protected $prefix = "lucent_";
|
||||
|
||||
protected $description = "Export data and files";
|
||||
|
||||
@@ -32,8 +34,30 @@ class Export extends Command
|
||||
$stamp = now()->format("Y_m_d_His");
|
||||
$sqlFile = $exportDir . "/dump_{$stamp}.sql";
|
||||
$zipFile = $exportDir . "/export_{$stamp}.zip";
|
||||
$filesDir = $fileService->loadPublicDisk()->path("lucent/files");
|
||||
|
||||
// Dump database
|
||||
$result = $this->dumpDatabase($db, $tables, $sqlFile)->flatMap(
|
||||
fn($sql) => $this->buildZip($sql, $filesDir, $zipFile),
|
||||
);
|
||||
|
||||
if (file_exists($sqlFile)) {
|
||||
unlink($sqlFile);
|
||||
}
|
||||
|
||||
if ($result->error()->isDefined()) {
|
||||
$this->error($result->error()->get());
|
||||
return;
|
||||
}
|
||||
|
||||
$this->info("Exported to {$zipFile}");
|
||||
}
|
||||
|
||||
/** @return Result<string, string> */
|
||||
private function dumpDatabase(
|
||||
array $db,
|
||||
array $tables,
|
||||
string $sqlFile,
|
||||
): Result {
|
||||
$tableArgs = collect($tables)->map(fn($t) => "-t {$t}")->join(" ");
|
||||
$command = sprintf(
|
||||
"PGPASSWORD=%s pg_dump -h %s -p %s -U %s -d %s %s --no-owner --no-acl > %s",
|
||||
@@ -49,27 +73,29 @@ class Export extends Command
|
||||
exec($command, result_code: $code);
|
||||
|
||||
if ($code !== 0) {
|
||||
$this->error("pg_dump failed");
|
||||
return;
|
||||
return Error::create("pg_dump failed.");
|
||||
}
|
||||
|
||||
$this->info("Database dumped.");
|
||||
|
||||
// Zip SQL + files
|
||||
$publicDisk = $fileService->loadPublicDisk();
|
||||
|
||||
$filesDir = $publicDisk->path("lucent/files");
|
||||
return Success::create($sqlFile);
|
||||
}
|
||||
|
||||
/** @return Result<string, string> */
|
||||
private function buildZip(
|
||||
string $sqlFile,
|
||||
string $filesDir,
|
||||
string $zipFile,
|
||||
): Result {
|
||||
$zip = new ZipArchive();
|
||||
if (
|
||||
$zip->open($zipFile, ZipArchive::CREATE | ZipArchive::OVERWRITE) !==
|
||||
true
|
||||
) {
|
||||
$this->error("Could not create zip archive.");
|
||||
return;
|
||||
return Error::create("Could not create zip archive.");
|
||||
}
|
||||
|
||||
$zip->addFile($sqlFile, "dump_{$stamp}.sql");
|
||||
$zip->addFile($sqlFile, basename($sqlFile));
|
||||
|
||||
if (is_dir($filesDir)) {
|
||||
$iterator = new \RecursiveIteratorIterator(
|
||||
@@ -91,9 +117,6 @@ class Export extends Command
|
||||
|
||||
$zip->close();
|
||||
|
||||
// Clean up originals
|
||||
unlink($sqlFile);
|
||||
|
||||
$this->info("Exported to {$zipFile}");
|
||||
return Success::create($zipFile);
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -6,7 +6,7 @@ use Illuminate\Console\Command;
|
||||
use Lucent\Primitive\Collection;
|
||||
use Lucent\Schema\CollectionSchema;
|
||||
|
||||
class GenerateCollectionSchema extends Command
|
||||
class GenerateCollectionSchemaCommand extends Command
|
||||
{
|
||||
|
||||
protected $signature = 'lucent:generate:collection {name}';
|
||||
@@ -3,8 +3,9 @@
|
||||
namespace Lucent\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class GenerateImageFilter extends Command
|
||||
class GenerateImageFilterCommand extends Command
|
||||
{
|
||||
protected $signature = "lucent:generate:image_filter {name}";
|
||||
|
||||
@@ -20,7 +21,10 @@ class GenerateImageFilter extends Command
|
||||
mkdir($dir, 0755, true);
|
||||
}
|
||||
|
||||
$filePath = "{$dir}/{$name}.php";
|
||||
$className = Str::of($name)->camel()->ucfirst() . "ImageFilter";
|
||||
$pathName = Str::of($name)->snake()->lower();
|
||||
|
||||
$filePath = "{$dir}/{$className}.php";
|
||||
|
||||
if (file_exists($filePath)) {
|
||||
$this->error("Filter {$name} already exists at {$filePath}");
|
||||
@@ -35,7 +39,7 @@ class GenerateImageFilter extends Command
|
||||
use Lucent\ImageFilterInterface;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
|
||||
class {$name} implements ImageFilterInterface
|
||||
class {$className} implements ImageFilterInterface
|
||||
{
|
||||
public function apply(ImageInterface \$image): ImageInterface
|
||||
{
|
||||
@@ -47,7 +51,7 @@ class GenerateImageFilter extends Command
|
||||
}
|
||||
|
||||
public function getPath(): string {
|
||||
return "{$name}";
|
||||
return "{$pathName}";
|
||||
}
|
||||
}
|
||||
PHP;
|
||||
@@ -4,9 +4,12 @@ namespace Lucent\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Lucent\File\FileService;
|
||||
use Lucent\ResultType\Error;
|
||||
use Lucent\ResultType\Result;
|
||||
use Lucent\ResultType\Success;
|
||||
use ZipArchive;
|
||||
|
||||
class Import extends Command
|
||||
class ImportCommand extends Command
|
||||
{
|
||||
protected $signature = "lucent:import";
|
||||
|
||||
@@ -44,26 +47,44 @@ class Import extends Command
|
||||
return;
|
||||
}
|
||||
|
||||
// Extract to temp directory
|
||||
$tempDir = storage_path("exports/.import_tmp_" . uniqid());
|
||||
|
||||
$result = $this->extractZip($zipFile, $tempDir)
|
||||
->flatMap(fn($dir) => $this->restoreDatabase($dir))
|
||||
->flatMap(fn($dir) => $this->restoreFiles($dir, $fileService));
|
||||
|
||||
$this->cleanup($tempDir);
|
||||
|
||||
if ($result->error()->isDefined()) {
|
||||
$this->error($result->error()->get());
|
||||
return;
|
||||
}
|
||||
|
||||
$this->info("Import complete.");
|
||||
}
|
||||
|
||||
/** @return Result<string, string> */
|
||||
private function extractZip(string $zipFile, string $tempDir): Result
|
||||
{
|
||||
mkdir($tempDir, 0755, true);
|
||||
|
||||
$zip = new ZipArchive();
|
||||
if ($zip->open($zipFile) !== true) {
|
||||
$this->error("Could not open zip archive.");
|
||||
$this->cleanup($tempDir);
|
||||
return;
|
||||
return Error::create("Could not open zip archive.");
|
||||
}
|
||||
|
||||
$zip->extractTo($tempDir);
|
||||
$zip->close();
|
||||
|
||||
// Restore database
|
||||
return Success::create($tempDir);
|
||||
}
|
||||
|
||||
/** @return Result<string, string> */
|
||||
private function restoreDatabase(string $tempDir): Result
|
||||
{
|
||||
$sqlFiles = glob($tempDir . "/*.sql");
|
||||
if (empty($sqlFiles)) {
|
||||
$this->error("No SQL dump found inside the archive.");
|
||||
$this->cleanup($tempDir);
|
||||
return;
|
||||
return Error::create("No SQL dump found inside the archive.");
|
||||
}
|
||||
|
||||
$db = config("database.connections.pgsql");
|
||||
@@ -74,7 +95,6 @@ class Import extends Command
|
||||
"lucent_edges",
|
||||
];
|
||||
|
||||
// Truncate existing tables before restore
|
||||
$truncate = collect($tables)
|
||||
->map(fn($t) => "TRUNCATE TABLE {$t} CASCADE;")
|
||||
->join(" ");
|
||||
@@ -91,12 +111,6 @@ class Import extends Command
|
||||
|
||||
exec($truncateCmd, result_code: $truncateCode);
|
||||
|
||||
if ($truncateCode !== 0) {
|
||||
$this->error("Failed to truncate existing tables.");
|
||||
$this->cleanup($tempDir);
|
||||
return;
|
||||
}
|
||||
|
||||
$restoreCmd = sprintf(
|
||||
"PGPASSWORD=%s psql -h %s -p %s -U %s -d %s -f %s",
|
||||
$db["password"],
|
||||
@@ -108,53 +122,53 @@ class Import extends Command
|
||||
);
|
||||
|
||||
exec($restoreCmd, result_code: $restoreCode);
|
||||
|
||||
if ($restoreCode !== 0) {
|
||||
$this->error("Database restore failed.");
|
||||
$this->cleanup($tempDir);
|
||||
return;
|
||||
return Error::create("Database restore failed.");
|
||||
}
|
||||
|
||||
$this->info("Database restored.");
|
||||
|
||||
// Replace files
|
||||
return Success::create($tempDir);
|
||||
}
|
||||
|
||||
/** @return Result<null, string> */
|
||||
private function restoreFiles(
|
||||
string $tempDir,
|
||||
FileService $fileService,
|
||||
): Result {
|
||||
$srcFilesDir = $tempDir . "/files";
|
||||
|
||||
if (is_dir($srcFilesDir)) {
|
||||
$publicDisk = $fileService->loadPublicDisk();
|
||||
$destFilesDir = $publicDisk->path("lucent/files");
|
||||
|
||||
// Remove existing files directory or create it if missing
|
||||
if (is_dir($destFilesDir)) {
|
||||
exec("rm -rf " . escapeshellarg($destFilesDir));
|
||||
} else {
|
||||
mkdir($destFilesDir, 0755, true);
|
||||
}
|
||||
|
||||
exec(
|
||||
sprintf(
|
||||
"cp -R %s/* %s",
|
||||
escapeshellarg($srcFilesDir),
|
||||
escapeshellarg($destFilesDir),
|
||||
),
|
||||
result_code: $copyCode,
|
||||
);
|
||||
|
||||
if ($copyCode !== 0) {
|
||||
$this->error("Failed to restore files.");
|
||||
$this->cleanup($tempDir);
|
||||
return;
|
||||
}
|
||||
|
||||
$this->info("Files restored.");
|
||||
} else {
|
||||
if (!is_dir($srcFilesDir)) {
|
||||
$this->warn(
|
||||
"No files directory found in archive — skipping file restore.",
|
||||
);
|
||||
return Success::create(null);
|
||||
}
|
||||
|
||||
$this->cleanup($tempDir);
|
||||
$this->info("Import complete.");
|
||||
$publicDisk = $fileService->loadPublicDisk();
|
||||
$destFilesDir = $publicDisk->path("lucent/files");
|
||||
|
||||
if (is_dir($destFilesDir)) {
|
||||
exec("rm -rf " . escapeshellarg($destFilesDir));
|
||||
}
|
||||
mkdir($destFilesDir, 0755, true);
|
||||
|
||||
exec(
|
||||
sprintf(
|
||||
"cp -R %s/* %s",
|
||||
escapeshellarg($srcFilesDir),
|
||||
escapeshellarg($destFilesDir),
|
||||
),
|
||||
result_code: $copyCode,
|
||||
);
|
||||
|
||||
if ($copyCode !== 0) {
|
||||
return Error::create("Failed to restore files.");
|
||||
}
|
||||
|
||||
$this->info("Files restored.");
|
||||
|
||||
return Success::create(null);
|
||||
}
|
||||
|
||||
private function cleanup(string $dir): void
|
||||
@@ -5,7 +5,7 @@ namespace Lucent\Commands;
|
||||
use DirectoryIterator;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class LiveLink extends Command
|
||||
class LiveLinkCommand extends Command
|
||||
{
|
||||
|
||||
protected $signature = 'lucent:livelink';
|
||||
@@ -8,7 +8,7 @@ use Lucent\File\FileRepo;
|
||||
use Lucent\File\FileService;
|
||||
use Lucent\Query\Query;
|
||||
|
||||
class RebuildThumbnails extends Command
|
||||
class RebuildThumbnailsCommand extends Command
|
||||
{
|
||||
protected $signature = "lucent:rebuild:thumbnails";
|
||||
|
||||
@@ -27,8 +27,8 @@ class RebuildThumbnails extends Command
|
||||
$files = FileRepo::query()->get();
|
||||
$disk = $this->fileService->loadPublicDisk();
|
||||
foreach ($files as $file) {
|
||||
$this->fileService->createTemplates($disk, $file->path);
|
||||
try {
|
||||
$this->fileService->createTemplates($disk, $file->path);
|
||||
} catch (Exception $e) {
|
||||
$this->error(
|
||||
"File " . $file->filename . " could not be rebuilt \n",
|
||||
@@ -6,7 +6,7 @@ use Illuminate\Console\Command;
|
||||
use Lucent\Edge\EdgeService;
|
||||
use Lucent\Query\Query;
|
||||
|
||||
class RemoveOrphanEdges extends Command
|
||||
class RemoveOrphanEdgesCommand extends Command
|
||||
{
|
||||
|
||||
protected $signature = 'lucent:removeOrphanEdges';
|
||||
@@ -11,7 +11,7 @@ use Lucent\Setup\Step\LucentConfigStep;
|
||||
use Lucent\Setup\Step\StorageLinkSetupStep;
|
||||
use Lucent\Setup\Step\StorageSetupStep;
|
||||
|
||||
class Setup extends Command
|
||||
class SetupCommand extends Command
|
||||
{
|
||||
protected $signature = "lucent:setup";
|
||||
|
||||
@@ -7,7 +7,7 @@ use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Lucent\Database\Database;
|
||||
|
||||
class SetupDatabase extends Command
|
||||
class SetupDatabaseCommand extends Command
|
||||
{
|
||||
protected $signature = "lucent:setup-db";
|
||||
protected $prefix = "lucent_";
|
||||
+53
-25
@@ -4,6 +4,7 @@ namespace Lucent\File;
|
||||
|
||||
use Illuminate\Contracts\Filesystem\Filesystem;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use Illuminate\Log\Logger;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
use Intervention\Image\Format;
|
||||
@@ -12,6 +13,9 @@ use Lucent\Channel\ChannelService;
|
||||
use Lucent\Id\Id;
|
||||
use Lucent\LucentException;
|
||||
use Lucent\Data\File as DataFile;
|
||||
use Lucent\ResultType\Error;
|
||||
use Lucent\ResultType\Result;
|
||||
use Lucent\ResultType\Success;
|
||||
use Spatie\ImageOptimizer\OptimizerChainFactory;
|
||||
|
||||
class FileService
|
||||
@@ -19,6 +23,7 @@ class FileService
|
||||
public function __construct(
|
||||
public ChannelService $channelService,
|
||||
public ImageManager $imageManager,
|
||||
public Logger $logger,
|
||||
) {}
|
||||
|
||||
public function createFromUrl(
|
||||
@@ -68,7 +73,10 @@ class FileService
|
||||
}
|
||||
|
||||
if ($this->isImage($mimetype)) {
|
||||
$this->createTemplates($disk, $path);
|
||||
$result = $this->createTemplates($disk, $path);
|
||||
if ($result->error()->isDefined()) {
|
||||
throw new LucentException($result->error()->get());
|
||||
}
|
||||
}
|
||||
|
||||
[$width, $height] = $this->isImage($mimetype)
|
||||
@@ -124,45 +132,65 @@ class FileService
|
||||
return Storage::disk(config("lucent.private_disk"));
|
||||
}
|
||||
|
||||
public function createTemplates(Filesystem $disk, string $path): void
|
||||
/** @return Result<null, string> */
|
||||
public function createTemplates(Filesystem $disk, string $path): Result
|
||||
{
|
||||
$originalImage = $this->imageManager->decode(
|
||||
$this->loadPublicDisk()->get("lucent/" . $path),
|
||||
);
|
||||
foreach ($this->channelService->channel->imageFilters as $filterClass) {
|
||||
$imageClone = clone $originalImage;
|
||||
$filterClassInstance = new $filterClass();
|
||||
$filePath = "lucent/" . $path;
|
||||
|
||||
$image = $imageClone->modify($filterClassInstance);
|
||||
if (!$this->loadPublicDisk()->exists($filePath)) {
|
||||
return Error::create("File not found: {$filePath}");
|
||||
}
|
||||
|
||||
$originalImage = $this->imageManager->decode(
|
||||
$this->loadPublicDisk()->get($filePath),
|
||||
);
|
||||
|
||||
foreach ($this->channelService->channel->imageFilters as $filterClass) {
|
||||
$filterClassInstance = new $filterClass();
|
||||
$templateUri =
|
||||
"lucent/templates/" .
|
||||
$filterClassInstance->getPath() .
|
||||
"/" .
|
||||
substr($path, 0, strrpos($path, ".")) .
|
||||
".webp";
|
||||
$disk->put(
|
||||
$templateUri,
|
||||
|
||||
$image->encodeUsingFormat(
|
||||
$ok = $disk->put(
|
||||
$templateUri,
|
||||
(clone $originalImage)
|
||||
->modify($filterClassInstance)
|
||||
->encodeUsingFormat(
|
||||
Format::WEBP,
|
||||
progressive: true,
|
||||
quality: 80,
|
||||
),
|
||||
);
|
||||
|
||||
if (!$ok) {
|
||||
return Error::create(
|
||||
"Failed to write template: {$templateUri}",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$thumbUri =
|
||||
"lucent/thumbs/" . substr($path, 0, strrpos($path, ".")) . ".webp";
|
||||
|
||||
$ok = $disk->put(
|
||||
$thumbUri,
|
||||
$originalImage
|
||||
->cover(300, 300)
|
||||
->encodeUsingFormat(
|
||||
Format::WEBP,
|
||||
progressive: true,
|
||||
quality: 80,
|
||||
),
|
||||
);
|
||||
);
|
||||
|
||||
if (!$ok) {
|
||||
return Error::create("Failed to write thumbnail: {$thumbUri}");
|
||||
}
|
||||
|
||||
$thumbDir =
|
||||
"lucent/thumbs/" . substr($path, 0, strrpos($path, ".")) . ".webp";
|
||||
|
||||
$image = $originalImage->cover(300, 300);
|
||||
$disk->put(
|
||||
$thumbDir,
|
||||
$image->encodeUsingFormat(
|
||||
Format::WEBP,
|
||||
progressive: true,
|
||||
quality: 80,
|
||||
),
|
||||
);
|
||||
return Success::create(null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -28,6 +28,10 @@ class AuthController
|
||||
|
||||
public function register(Request $request): View|RedirectResponse
|
||||
{
|
||||
if ($this->authService->isExternal()) {
|
||||
return $this->authService->redirectHome();
|
||||
}
|
||||
|
||||
if ($this->accountService->countUsers() > 0) {
|
||||
return redirect(
|
||||
$this->channelService->channel->lucentUrl . "/login",
|
||||
@@ -43,6 +47,10 @@ class AuthController
|
||||
|
||||
public function postRegister(Request $request): Response
|
||||
{
|
||||
if ($this->authService->isExternal()) {
|
||||
abort(400);
|
||||
}
|
||||
|
||||
if ($this->accountService->countUsers() > 0) {
|
||||
abort(400);
|
||||
}
|
||||
@@ -61,6 +69,9 @@ class AuthController
|
||||
|
||||
public function login()
|
||||
{
|
||||
if ($this->authService->isExternal()) {
|
||||
return $this->authService->redirectHome();
|
||||
}
|
||||
if ($this->accountService->countUsers() == 0) {
|
||||
return redirect(
|
||||
$this->channelService->channel->lucentUrl . "/register",
|
||||
@@ -76,6 +87,9 @@ class AuthController
|
||||
|
||||
public function postLogin(Request $request)
|
||||
{
|
||||
if ($this->authService->isExternal()) {
|
||||
abort(400);
|
||||
}
|
||||
$this->authService->sendLoginEmail($request->input("email"));
|
||||
return [];
|
||||
}
|
||||
@@ -87,6 +101,10 @@ class AuthController
|
||||
// "token" => $request->input("token"),
|
||||
// ]);
|
||||
|
||||
if ($this->authService->isExternal()) {
|
||||
abort(400);
|
||||
}
|
||||
|
||||
return $this->svelte->render(
|
||||
layout: "account",
|
||||
view: "verify",
|
||||
@@ -100,6 +118,10 @@ class AuthController
|
||||
|
||||
public function postVerify(Request $request)
|
||||
{
|
||||
if ($this->authService->isExternal()) {
|
||||
abort(400);
|
||||
}
|
||||
|
||||
try {
|
||||
$this->authService->login(
|
||||
$request->input("email"),
|
||||
@@ -113,6 +135,10 @@ class AuthController
|
||||
|
||||
public function logout(): RedirectResponse
|
||||
{
|
||||
if ($this->authService->isExternal()) {
|
||||
abort(400);
|
||||
}
|
||||
|
||||
$this->session->flush();
|
||||
return redirect($this->channelService->channel->lucentUrl . "/login");
|
||||
}
|
||||
|
||||
@@ -14,16 +14,16 @@ use Lucent\Account\UserRepo;
|
||||
use Lucent\Account\UserRepoLucent;
|
||||
use Lucent\Account\UserRepoLunar;
|
||||
use Lucent\Channel\ChannelService;
|
||||
use Lucent\Commands\CompileSchemas;
|
||||
use Lucent\Commands\GenerateCollectionSchema;
|
||||
use Lucent\Commands\GenerateImageFilter;
|
||||
use Lucent\Commands\LiveLink;
|
||||
use Lucent\Commands\RebuildThumbnails;
|
||||
use Lucent\Commands\RemoveOrphanEdges;
|
||||
use Lucent\Commands\SetupDatabase;
|
||||
use Lucent\Commands\Export;
|
||||
use Lucent\Commands\Import;
|
||||
use Lucent\Commands\Setup;
|
||||
use Lucent\Commands\CompileSchemasCommand;
|
||||
use Lucent\Commands\ExportCommand;
|
||||
use Lucent\Commands\GenerateCollectionSchemaCommand;
|
||||
use Lucent\Commands\GenerateImageFilterCommand;
|
||||
use Lucent\Commands\ImportCommand;
|
||||
use Lucent\Commands\LiveLinkCommand;
|
||||
use Lucent\Commands\RebuildThumbnailsCommand;
|
||||
use Lucent\Commands\RemoveOrphanEdgesCommand;
|
||||
use Lucent\Commands\SetupCommand;
|
||||
use Lucent\Commands\SetupDatabaseCommand;
|
||||
use Lucent\Data\ChannelAuth;
|
||||
use Lucent\File\FileService;
|
||||
use Lucent\Query\DatabaseGraph\DatabaseGraph;
|
||||
@@ -95,16 +95,16 @@ class LucentServiceProvider extends ServiceProvider
|
||||
|
||||
if ($this->app->runningInConsole()) {
|
||||
$this->commands([
|
||||
CompileSchemas::class,
|
||||
RebuildThumbnails::class,
|
||||
LiveLink::class,
|
||||
RemoveOrphanEdges::class,
|
||||
SetupDatabase::class,
|
||||
GenerateCollectionSchema::class,
|
||||
GenerateImageFilter::class,
|
||||
Export::class,
|
||||
Import::class,
|
||||
Setup::class,
|
||||
CompileSchemasCommand::class,
|
||||
RebuildThumbnailsCommand::class,
|
||||
LiveLinkCommand::class,
|
||||
RemoveOrphanEdgesCommand::class,
|
||||
SetupDatabaseCommand::class,
|
||||
GenerateCollectionSchemaCommand::class,
|
||||
GenerateImageFilterCommand::class,
|
||||
ExportCommand::class,
|
||||
ImportCommand::class,
|
||||
SetupCommand::class,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
<?php
|
||||
|
||||
namespace Lucent\Option;
|
||||
|
||||
use Traversable;
|
||||
|
||||
/**
|
||||
* @template T
|
||||
*
|
||||
* @extends Option<T>
|
||||
*/
|
||||
final class LazyOption extends Option
|
||||
{
|
||||
/** @var callable(mixed...):(Option<T>) */
|
||||
private $callback;
|
||||
|
||||
/** @var array<int, mixed> */
|
||||
private $arguments;
|
||||
|
||||
/** @var Option<T>|null */
|
||||
private $option;
|
||||
|
||||
/**
|
||||
* @template S
|
||||
* @param callable(mixed...):(Option<S>) $callback
|
||||
* @param array<int, mixed> $arguments
|
||||
*
|
||||
* @return LazyOption<S>
|
||||
*/
|
||||
public static function create($callback, array $arguments = []): self
|
||||
{
|
||||
return new self($callback, $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param callable(mixed...):(Option<T>) $callback
|
||||
* @param array<int, mixed> $arguments
|
||||
*/
|
||||
public function __construct($callback, array $arguments = [])
|
||||
{
|
||||
if (!is_callable($callback)) {
|
||||
throw new \InvalidArgumentException('Invalid callback given');
|
||||
}
|
||||
|
||||
$this->callback = $callback;
|
||||
$this->arguments = $arguments;
|
||||
}
|
||||
|
||||
public function isDefined(): bool
|
||||
{
|
||||
return $this->option()->isDefined();
|
||||
}
|
||||
|
||||
public function isEmpty(): bool
|
||||
{
|
||||
return $this->option()->isEmpty();
|
||||
}
|
||||
|
||||
public function get()
|
||||
{
|
||||
return $this->option()->get();
|
||||
}
|
||||
|
||||
public function getOrElse($default)
|
||||
{
|
||||
return $this->option()->getOrElse($default);
|
||||
}
|
||||
|
||||
public function getOrCall($callable)
|
||||
{
|
||||
return $this->option()->getOrCall($callable);
|
||||
}
|
||||
|
||||
public function getOrThrow(\Exception $ex)
|
||||
{
|
||||
return $this->option()->getOrThrow($ex);
|
||||
}
|
||||
|
||||
public function orElse(Option $else)
|
||||
{
|
||||
return $this->option()->orElse($else);
|
||||
}
|
||||
|
||||
public function ifDefined($callable)
|
||||
{
|
||||
$this->option()->forAll($callable);
|
||||
}
|
||||
|
||||
public function forAll($callable)
|
||||
{
|
||||
return $this->option()->forAll($callable);
|
||||
}
|
||||
|
||||
public function map($callable)
|
||||
{
|
||||
return $this->option()->map($callable);
|
||||
}
|
||||
|
||||
public function flatMap($callable)
|
||||
{
|
||||
return $this->option()->flatMap($callable);
|
||||
}
|
||||
|
||||
public function filter($callable)
|
||||
{
|
||||
return $this->option()->filter($callable);
|
||||
}
|
||||
|
||||
public function filterNot($callable)
|
||||
{
|
||||
return $this->option()->filterNot($callable);
|
||||
}
|
||||
|
||||
public function select($value)
|
||||
{
|
||||
return $this->option()->select($value);
|
||||
}
|
||||
|
||||
public function reject($value)
|
||||
{
|
||||
return $this->option()->reject($value);
|
||||
}
|
||||
|
||||
/** @return Traversable<T> */
|
||||
public function getIterator(): Traversable
|
||||
{
|
||||
return $this->option()->getIterator();
|
||||
}
|
||||
|
||||
public function foldLeft($initialValue, $callable)
|
||||
{
|
||||
return $this->option()->foldLeft($initialValue, $callable);
|
||||
}
|
||||
|
||||
public function foldRight($initialValue, $callable)
|
||||
{
|
||||
return $this->option()->foldRight($initialValue, $callable);
|
||||
}
|
||||
|
||||
/** @return Option<T> */
|
||||
private function option(): Option
|
||||
{
|
||||
if (null === $this->option) {
|
||||
/** @var mixed */
|
||||
$option = call_user_func_array($this->callback, $this->arguments);
|
||||
if ($option instanceof Option) {
|
||||
$this->option = $option;
|
||||
} else {
|
||||
throw new \RuntimeException(sprintf('Expected instance of %s', Option::class));
|
||||
}
|
||||
}
|
||||
|
||||
return $this->option;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
namespace Lucent\Option;
|
||||
|
||||
use EmptyIterator;
|
||||
|
||||
/**
|
||||
* @extends Option<mixed>
|
||||
*/
|
||||
final class None extends Option
|
||||
{
|
||||
/** @var None|null */
|
||||
private static $instance;
|
||||
|
||||
/** @return None */
|
||||
public static function create(): self
|
||||
{
|
||||
if (null === self::$instance) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
public function get()
|
||||
{
|
||||
throw new \RuntimeException('None has no value.');
|
||||
}
|
||||
|
||||
public function getOrCall($callable)
|
||||
{
|
||||
return $callable();
|
||||
}
|
||||
|
||||
public function getOrElse($default)
|
||||
{
|
||||
return $default;
|
||||
}
|
||||
|
||||
public function getOrThrow(\Exception $ex)
|
||||
{
|
||||
throw $ex;
|
||||
}
|
||||
|
||||
public function isEmpty(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function isDefined(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function orElse(Option $else)
|
||||
{
|
||||
return $else;
|
||||
}
|
||||
|
||||
public function ifDefined($callable)
|
||||
{
|
||||
// no-op
|
||||
}
|
||||
|
||||
public function forAll($callable)
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function map($callable)
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function flatMap($callable)
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function filter($callable)
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function filterNot($callable)
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function select($value)
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function reject($value)
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getIterator(): EmptyIterator
|
||||
{
|
||||
return new EmptyIterator();
|
||||
}
|
||||
|
||||
public function foldLeft($initialValue, $callable)
|
||||
{
|
||||
return $initialValue;
|
||||
}
|
||||
|
||||
public function foldRight($initialValue, $callable)
|
||||
{
|
||||
return $initialValue;
|
||||
}
|
||||
|
||||
private function __construct()
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
<?php
|
||||
|
||||
namespace Lucent\Option;
|
||||
|
||||
use ArrayAccess;
|
||||
use IteratorAggregate;
|
||||
|
||||
/**
|
||||
* @template T
|
||||
*
|
||||
* @implements IteratorAggregate<T>
|
||||
*/
|
||||
abstract class Option implements IteratorAggregate
|
||||
{
|
||||
/**
|
||||
* @template S
|
||||
*
|
||||
* @param S $value
|
||||
* @param S $noneValue
|
||||
*
|
||||
* @return Option<S>
|
||||
*/
|
||||
public static function fromValue($value, $noneValue = null)
|
||||
{
|
||||
if ($value === $noneValue) {
|
||||
return None::create();
|
||||
}
|
||||
|
||||
return new Some($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @template S
|
||||
*
|
||||
* @param array<string|int,S>|ArrayAccess<string|int,S>|null $array
|
||||
* @param string|int|null $key
|
||||
*
|
||||
* @return Option<S>
|
||||
*/
|
||||
public static function fromArraysValue($array, $key)
|
||||
{
|
||||
if ($key === null || !(is_array($array) || $array instanceof ArrayAccess) || !isset($array[$key])) {
|
||||
return None::create();
|
||||
}
|
||||
|
||||
return new Some($array[$key]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @template S
|
||||
*
|
||||
* @param callable $callback
|
||||
* @param array $arguments
|
||||
* @param S $noneValue
|
||||
*
|
||||
* @return LazyOption<S>
|
||||
*/
|
||||
public static function fromReturn($callback, array $arguments = [], $noneValue = null)
|
||||
{
|
||||
return new LazyOption(static function () use ($callback, $arguments, $noneValue) {
|
||||
/** @var mixed */
|
||||
$return = call_user_func_array($callback, $arguments);
|
||||
|
||||
if ($return === $noneValue) {
|
||||
return None::create();
|
||||
}
|
||||
|
||||
return new Some($return);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @template S
|
||||
*
|
||||
* @param Option<S>|callable|S $value
|
||||
* @param S $noneValue
|
||||
*
|
||||
* @return Option<S>|LazyOption<S>
|
||||
*/
|
||||
public static function ensure($value, $noneValue = null)
|
||||
{
|
||||
if ($value instanceof self) {
|
||||
return $value;
|
||||
} elseif (is_callable($value)) {
|
||||
return new LazyOption(static function () use ($value, $noneValue) {
|
||||
/** @var mixed */
|
||||
$return = $value();
|
||||
|
||||
if ($return instanceof self) {
|
||||
return $return;
|
||||
} else {
|
||||
return self::fromValue($return, $noneValue);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
return self::fromValue($value, $noneValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @template S
|
||||
*
|
||||
* @param callable $callback
|
||||
* @param mixed $noneValue
|
||||
*
|
||||
* @return callable
|
||||
*/
|
||||
public static function lift($callback, $noneValue = null)
|
||||
{
|
||||
return static function () use ($callback, $noneValue) {
|
||||
/** @var array<int, mixed> */
|
||||
$args = func_get_args();
|
||||
|
||||
$reduced_args = array_reduce(
|
||||
$args,
|
||||
/** @param bool $status */
|
||||
static function ($status, self $o) {
|
||||
return $o->isEmpty() ? true : $status;
|
||||
},
|
||||
false
|
||||
);
|
||||
|
||||
if ($reduced_args) {
|
||||
return None::create();
|
||||
}
|
||||
|
||||
$args = array_map(
|
||||
static function (self $o) {
|
||||
return $o->get();
|
||||
},
|
||||
$args
|
||||
);
|
||||
|
||||
return self::ensure(call_user_func_array($callback, $args), $noneValue);
|
||||
};
|
||||
}
|
||||
|
||||
/** @return T */
|
||||
abstract public function get();
|
||||
|
||||
/**
|
||||
* @template S
|
||||
* @param S $default
|
||||
* @return T|S
|
||||
*/
|
||||
abstract public function getOrElse($default);
|
||||
|
||||
/**
|
||||
* @template S
|
||||
* @param callable():S $callable
|
||||
* @return T|S
|
||||
*/
|
||||
abstract public function getOrCall($callable);
|
||||
|
||||
/** @return T */
|
||||
abstract public function getOrThrow(\Exception $ex);
|
||||
|
||||
abstract public function isEmpty(): bool;
|
||||
|
||||
abstract public function isDefined(): bool;
|
||||
|
||||
/**
|
||||
* @param Option<T> $else
|
||||
* @return Option<T>
|
||||
*/
|
||||
abstract public function orElse(self $else);
|
||||
|
||||
/** @deprecated Use forAll() instead. */
|
||||
abstract public function ifDefined($callable);
|
||||
|
||||
/**
|
||||
* @param callable(T):mixed $callable
|
||||
* @return Option<T>
|
||||
*/
|
||||
abstract public function forAll($callable);
|
||||
|
||||
/**
|
||||
* @template S
|
||||
* @param callable(T):S $callable
|
||||
* @return Option<S>
|
||||
*/
|
||||
abstract public function map($callable);
|
||||
|
||||
/**
|
||||
* @template S
|
||||
* @param callable(T):Option<S> $callable
|
||||
* @return Option<S>
|
||||
*/
|
||||
abstract public function flatMap($callable);
|
||||
|
||||
/**
|
||||
* @param callable(T):bool $callable
|
||||
* @return Option<T>
|
||||
*/
|
||||
abstract public function filter($callable);
|
||||
|
||||
/**
|
||||
* @param callable(T):bool $callable
|
||||
* @return Option<T>
|
||||
*/
|
||||
abstract public function filterNot($callable);
|
||||
|
||||
/**
|
||||
* @param T $value
|
||||
* @return Option<T>
|
||||
*/
|
||||
abstract public function select($value);
|
||||
|
||||
/**
|
||||
* @param T $value
|
||||
* @return Option<T>
|
||||
*/
|
||||
abstract public function reject($value);
|
||||
|
||||
/**
|
||||
* @template S
|
||||
* @param S $initialValue
|
||||
* @param callable(S, T):S $callable
|
||||
* @return S
|
||||
*/
|
||||
abstract public function foldLeft($initialValue, $callable);
|
||||
|
||||
/**
|
||||
* @template S
|
||||
* @param S $initialValue
|
||||
* @param callable(T, S):S $callable
|
||||
* @return S
|
||||
*/
|
||||
abstract public function foldRight($initialValue, $callable);
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
<?php
|
||||
|
||||
namespace Lucent\Option;
|
||||
|
||||
use ArrayIterator;
|
||||
|
||||
/**
|
||||
* @template T
|
||||
*
|
||||
* @extends Option<T>
|
||||
*/
|
||||
final class Some extends Option
|
||||
{
|
||||
/** @var T */
|
||||
private $value;
|
||||
|
||||
/** @param T $value */
|
||||
public function __construct($value)
|
||||
{
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @template U
|
||||
* @param U $value
|
||||
* @return Some<U>
|
||||
*/
|
||||
public static function create($value): self
|
||||
{
|
||||
return new self($value);
|
||||
}
|
||||
|
||||
public function isDefined(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function isEmpty(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function get()
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
public function getOrElse($default)
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
public function getOrCall($callable)
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
public function getOrThrow(\Exception $ex)
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
public function orElse(Option $else)
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function ifDefined($callable)
|
||||
{
|
||||
$this->forAll($callable);
|
||||
}
|
||||
|
||||
public function forAll($callable)
|
||||
{
|
||||
$callable($this->value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function map($callable)
|
||||
{
|
||||
return new self($callable($this->value));
|
||||
}
|
||||
|
||||
public function flatMap($callable)
|
||||
{
|
||||
/** @var mixed */
|
||||
$rs = $callable($this->value);
|
||||
if (!$rs instanceof Option) {
|
||||
throw new \RuntimeException('Callables passed to flatMap() must return an Option. Maybe you should use map() instead?');
|
||||
}
|
||||
|
||||
return $rs;
|
||||
}
|
||||
|
||||
public function filter($callable)
|
||||
{
|
||||
if (true === $callable($this->value)) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
return None::create();
|
||||
}
|
||||
|
||||
public function filterNot($callable)
|
||||
{
|
||||
if (false === $callable($this->value)) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
return None::create();
|
||||
}
|
||||
|
||||
public function select($value)
|
||||
{
|
||||
if ($this->value === $value) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
return None::create();
|
||||
}
|
||||
|
||||
public function reject($value)
|
||||
{
|
||||
if ($this->value === $value) {
|
||||
return None::create();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** @return ArrayIterator<int, T> */
|
||||
public function getIterator(): ArrayIterator
|
||||
{
|
||||
return new ArrayIterator([$this->value]);
|
||||
}
|
||||
|
||||
public function foldLeft($initialValue, $callable)
|
||||
{
|
||||
return $callable($initialValue, $this->value);
|
||||
}
|
||||
|
||||
public function foldRight($initialValue, $callable)
|
||||
{
|
||||
return $callable($this->value, $initialValue);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Lucent\ResultType;
|
||||
|
||||
use Lucent\Option\None;
|
||||
use Lucent\Option\Some;
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @template E
|
||||
*
|
||||
* @extends \Lucent\ResultType\Result<T,E>
|
||||
*/
|
||||
final class Error extends Result
|
||||
{
|
||||
/**
|
||||
* @var E
|
||||
*/
|
||||
private $value;
|
||||
|
||||
/**
|
||||
* Internal constructor for an error value.
|
||||
*
|
||||
* @param E $value
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function __construct($value)
|
||||
{
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new error value.
|
||||
*
|
||||
* @template F
|
||||
*
|
||||
* @param F $value
|
||||
*
|
||||
* @return \Lucent\ResultType\Result<T,F>
|
||||
*/
|
||||
public static function create($value): Error
|
||||
{
|
||||
return new self($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the success option value.
|
||||
*
|
||||
* @return \Lucent\Option\Option<T>
|
||||
*/
|
||||
public function success()
|
||||
{
|
||||
return None::create();
|
||||
}
|
||||
|
||||
/**
|
||||
* Map over the success value.
|
||||
*
|
||||
* @template S
|
||||
*
|
||||
* @param callable(T):S $f
|
||||
*
|
||||
* @return \Lucent\ResultType\Result<S,E>
|
||||
*/
|
||||
public function map(callable $f): Result
|
||||
{
|
||||
return self::create($this->value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flat map over the success value.
|
||||
*
|
||||
* @template S
|
||||
* @template F
|
||||
*
|
||||
* @param callable(T):\Lucent\ResultType\Result<S,F> $f
|
||||
*
|
||||
* @return \Lucent\ResultType\Result<S,F>
|
||||
*/
|
||||
public function flatMap(callable $f): Result
|
||||
{
|
||||
/** @var \Lucent\ResultType\Result<S,F> */
|
||||
return self::create($this->value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the error option value.
|
||||
*
|
||||
* @return \Lucent\Option\Option<E>
|
||||
*/
|
||||
public function error(): Some
|
||||
{
|
||||
return Some::create($this->value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Map over the error value.
|
||||
*
|
||||
* @template F
|
||||
*
|
||||
* @param callable(E):F $f
|
||||
*
|
||||
* @return \Lucent\ResultType\Result<T,F>
|
||||
*/
|
||||
public function mapError(callable $f): Result
|
||||
{
|
||||
return self::create($f($this->value));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Lucent\ResultType;
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @template E
|
||||
*/
|
||||
abstract class Result
|
||||
{
|
||||
/**
|
||||
* Get the success option value.
|
||||
*
|
||||
* @return \Lucent\Option\Option<T>
|
||||
*/
|
||||
abstract public function success();
|
||||
|
||||
/**
|
||||
* Map over the success value.
|
||||
*
|
||||
* @template S
|
||||
*
|
||||
* @param callable(T):S $f
|
||||
*
|
||||
* @return \Lucent\ResultType\Result<S,E>
|
||||
*/
|
||||
abstract public function map(callable $f);
|
||||
|
||||
/**
|
||||
* Flat map over the success value.
|
||||
*
|
||||
* @template S
|
||||
* @template F
|
||||
*
|
||||
* @param callable(T):\Lucent\ResultType\Result<S,F> $f
|
||||
*
|
||||
* @return \Lucent\ResultType\Result<S,F>
|
||||
*/
|
||||
abstract public function flatMap(callable $f);
|
||||
|
||||
/**
|
||||
* Get the error option value.
|
||||
*
|
||||
* @return \Lucent\Option\Option<E>
|
||||
*/
|
||||
abstract public function error();
|
||||
|
||||
/**
|
||||
* Map over the error value.
|
||||
*
|
||||
* @template F
|
||||
*
|
||||
* @param callable(E):F $f
|
||||
*
|
||||
* @return \Lucent\ResultType\Result<T,F>
|
||||
*/
|
||||
abstract public function mapError(callable $f);
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Lucent\ResultType;
|
||||
|
||||
use Lucent\Option\None;
|
||||
use Lucent\Option\Some;
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @template E
|
||||
*
|
||||
* @extends \Lucent\ResultType\Result<T,E>
|
||||
*/
|
||||
final class Success extends Result
|
||||
{
|
||||
/**
|
||||
* @var T
|
||||
*/
|
||||
private $value;
|
||||
|
||||
/**
|
||||
* Internal constructor for a success value.
|
||||
*
|
||||
* @param T $value
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function __construct($value)
|
||||
{
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new error value.
|
||||
*
|
||||
* @template S
|
||||
*
|
||||
* @param S $value
|
||||
*
|
||||
* @return \Lucent\ResultType\Result<S,E>
|
||||
*/
|
||||
public static function create($value): Success
|
||||
{
|
||||
return new self($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the success option value.
|
||||
*
|
||||
* @return \Lucent\Option\Option<T>
|
||||
*/
|
||||
public function success(): Some
|
||||
{
|
||||
return Some::create($this->value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Map over the success value.
|
||||
*
|
||||
* @template S
|
||||
*
|
||||
* @param callable(T):S $f
|
||||
*
|
||||
* @return \Lucent\ResultType\Result<S,E>
|
||||
*/
|
||||
public function map(callable $f): Result
|
||||
{
|
||||
return self::create($f($this->value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Flat map over the success value.
|
||||
*
|
||||
* @template S
|
||||
* @template F
|
||||
*
|
||||
* @param callable(T):\Lucent\ResultType\Result<S,F> $f
|
||||
*
|
||||
* @return \Lucent\ResultType\Result<S,F>
|
||||
*/
|
||||
public function flatMap(callable $f)
|
||||
{
|
||||
return $f($this->value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the error option value.
|
||||
*
|
||||
* @return \Lucent\Option\Option<E>
|
||||
*/
|
||||
public function error()
|
||||
{
|
||||
return None::create();
|
||||
}
|
||||
|
||||
/**
|
||||
* Map over the error value.
|
||||
*
|
||||
* @template F
|
||||
*
|
||||
* @param callable(E):F $f
|
||||
*
|
||||
* @return \Lucent\ResultType\Result<T,F>
|
||||
*/
|
||||
public function mapError(callable $f): Result
|
||||
{
|
||||
return self::create($this->value);
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ use Lucent\Database\Database;
|
||||
use Lucent\Edge\Edge;
|
||||
use Lucent\Primitive\Collection;
|
||||
use Lucent\Record\RecordData;
|
||||
use PhpOption\Option;
|
||||
use Lucent\Option\Option;
|
||||
use stdClass;
|
||||
|
||||
class RevisionRepo
|
||||
|
||||
@@ -6,7 +6,7 @@ use Lucent\Channel\ChannelService;
|
||||
use Lucent\Edge\Edge;
|
||||
use Lucent\Primitive\Collection;
|
||||
use Lucent\Record\Record;
|
||||
use PhpOption\Option;
|
||||
use Lucent\Option\Option;
|
||||
|
||||
readonly class RevisionService
|
||||
{
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
<?php
|
||||
|
||||
use PhpOption\None;
|
||||
use PhpOption\Some;
|
||||
use Lucent\Option\None;
|
||||
use Lucent\Option\Some;
|
||||
|
||||
if (!function_exists("some")) {
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user