diff --git a/front/js/svelte/content/tools/Tools.svelte b/front/js/svelte/content/tools/Tools.svelte index dc69f0b..fdd9fbf 100644 --- a/front/js/svelte/content/tools/Tools.svelte +++ b/front/js/svelte/content/tools/Tools.svelte @@ -42,10 +42,6 @@ window.location = url; } } - - function uploadComplete(e) { - records = e.detail; - }
diff --git a/front/js/svelte/dialog/FileDialog.svelte b/front/js/svelte/dialog/FileDialog.svelte index e0951b9..ed67a1c 100644 --- a/front/js/svelte/dialog/FileDialog.svelte +++ b/front/js/svelte/dialog/FileDialog.svelte @@ -1,15 +1,14 @@ - {#if data.schema} -
- - - {#if selectedRecords.length > 0} - - {selectedRecords.length} records selected - - {/if} +
+ + + {#if selectedRecords.length > 0} + + {selectedRecords.length} records selected + + {/if} - -
+ +
-
- -
- {/if} +
+ +
diff --git a/front/js/svelte/dialog/FileIndex.svelte b/front/js/svelte/dialog/FileIndex.svelte new file mode 100644 index 0000000..9238da0 --- /dev/null +++ b/front/js/svelte/dialog/FileIndex.svelte @@ -0,0 +1,119 @@ + + +
+ + + + {#if isWritable} + + {/if} + + + + + {#each files as file (file.id)} + + + + + + {/each} + +
+ 0 && + selected.length < files.length} + checked={selected.length === files.length} + > +
+
+ {#if isWritable} + select(file)} + checked={selected.find( + (s) => s.id === file.id, + )} + value={file} + > + {/if} +
+ 0 ? "medium" : "small"} + /> + +
+ {file.filename} + {(file.size / 1024).toFixed(1)}kB + + {#if file.width > 0} + {file.width + + "x" + + file.height} + {/if} + + Download + +
+
+
+
+
diff --git a/front/js/svelte/files/Preview.svelte b/front/js/svelte/files/Preview.svelte index 752f878..c6ff9d2 100644 --- a/front/js/svelte/files/Preview.svelte +++ b/front/js/svelte/files/Preview.svelte @@ -1,9 +1,9 @@ -
- {#if record} - {#if record._file.mime.startsWith("image")} +
+ {#if file} + {#if file.mime.startsWith("image")} {record._file.path} {:else} - + .{record._file.path.split(".").pop().toLowerCase()}.{file.path.split(".").pop().toLowerCase()} {/if} {/if} {#if showFilename} {record._file.path} + href="{channel.lucentUrl}/files/{file.id}" + title={file.path} + class="preview-file-filename lx-small-text text-decoration-none" + >{file.path} + {/if}
\ No newline at end of file + diff --git a/front/js/svelte/files/imageserver.js b/front/js/svelte/files/imageserver.js index ec04eb1..bd75f26 100644 --- a/front/js/svelte/files/imageserver.js +++ b/front/js/svelte/files/imageserver.js @@ -1,30 +1,29 @@ -export function imgurl(channel, record) { - if (record._file.mime === "image/svg+xml") { - return fileurl(channel, record); +export function imgurl(channel, file) { + if (file.mime === "image/svg+xml") { + return fileurl(channel, file); + } + return channel.filesUrl + `/thumbs/${file.path}`; +} + +export function fileurl(channel, file) { + return channel.filesUrl + `/${file.path}`; +} + +export function htmlurl(channel, file, preset) { + let html = ""; + let url = fileurl(channel, file); + + if (file.width > 0) { + let presetUrl = url; + if (preset) { + presetUrl = channel.filesUrl + `/templates/${preset}/${file.path}`; } - return channel.disks[record._file.disk] + `/thumbs/${record._file.path}`; -} - -export function fileurl(channel, record) { - return channel.disks[record._file.disk] + `/${record._file.path}`; -} - -export function htmlurl(channel, record, preset) { - - let html = ""; - let url = fileurl(channel, record) - - if (record._file.width > 0) { - let presetUrl = url; - if (preset) { - presetUrl = channel.disks[record._file.disk] + `/templates/${preset}/${record._file.path}`; - } - html = `${record._file.path}` - } else if (record._file.mime === "image/svg+xml") { - html = `${record._file.path}` - } else { - html = `${record._file.originalName}` - } - - return html; + html = `${file.path}`; + } else if (file.mime === "image/svg+xml") { + html = `${file.path}`; + } else { + html = `${file.originalName}`; + } + + return html; } diff --git a/front/js/svelte/records/elements/File.svelte b/front/js/svelte/records/elements/File.svelte index 645a30a..eb50b00 100644 --- a/front/js/svelte/records/elements/File.svelte +++ b/front/js/svelte/records/elements/File.svelte @@ -22,11 +22,6 @@ ); } - function openBrowseModal(e, schema) { - e.preventDefault(); - browseModal.open(schema); - } - async function reorder(e) { graph.edges = await sortByField( e.detail.source, @@ -52,15 +47,15 @@ function uploadComplete(e) { records = e.detail; } + + function openBrowseModal(e) { + e.preventDefault(); + browseModal.open(record.id); + }
- +
@@ -80,4 +75,4 @@ {/each} {/if} --> - + diff --git a/src/Channel/Channel.php b/src/Channel/Channel.php index e414593..bf4ed17 100644 --- a/src/Channel/Channel.php +++ b/src/Channel/Channel.php @@ -28,18 +28,20 @@ final class Channel ) { $this->lucentUrl = $url . "/lucent"; $this->filesUrl = $this->makeFilesUrl(); + $this->previewTargetUrl = $url . "/" . $previewTarget; } private function makeFilesUrl(): string { - return match (config("filesystems.disks.lucent.driver")) { - "s3" => config("filesystems.disks.lucent.endpoint") . + $lucentDisk = config("lucent.disk"); + return match (config("filesystems.disks.$lucentDisk.driver")) { + "s3" => config("filesystems.disks.$lucentDisk.endpoint") . "/" . - config("filesystems.disks.lucent.bucket"), + config("filesystems.disks.$lucentDisk.bucket"), "local" => $this->url . "/storage" . - config("filesystems.disks.lucent.endpoint"), + config("filesystems.disks.$lucentDisk.endpoint"), default => "", }; } diff --git a/src/Commands/SetupDatabase.php b/src/Commands/SetupDatabase.php index 412bad7..24e6138 100644 --- a/src/Commands/SetupDatabase.php +++ b/src/Commands/SetupDatabase.php @@ -110,9 +110,9 @@ class SetupDatabase extends Command Blueprint $table, ) { $table->uuid("id")->primary(); - $table->uuid("record"); - $table->string("name"); - $table->string("ogName"); + $table->uuid("recordId"); + $table->string("filename"); + $table->jsonb("captions"); $table->string("mime"); $table->string("path"); $table->integer("size"); @@ -122,7 +122,7 @@ class SetupDatabase extends Command }); DB::statement( - "CREATE INDEX ON " . $this->prefix . "files (record)", + "CREATE INDEX ON " . $this->prefix . "files (recordId)", ); } } diff --git a/src/Data/File.php b/src/Data/File.php index 144a6a9..1484e70 100644 --- a/src/Data/File.php +++ b/src/Data/File.php @@ -2,12 +2,14 @@ namespace Lucent\Data; +use stdClass; + class File { function __construct( public readonly string $id, public readonly string $recordId, - public readonly string $originalName, + public readonly string $filename, public readonly string $mime, public readonly string $path, public readonly int $size, @@ -21,7 +23,22 @@ class File return new File( id: data_get($data, "id"), recordId: data_get($data, "recordId"), - originalName: data_get($data, "originalName"), + filename: data_get($data, "filename"), + mime: data_get($data, "mime"), + path: data_get($data, "path"), + size: data_get($data, "size"), + width: data_get($data, "width"), + height: data_get($data, "height"), + checksum: data_get($data, "checksum"), + ); + } + + public static function fromDB(stdClass $data): File + { + return new File( + id: data_get($data, "id"), + recordId: data_get($data, "recordId"), + filename: data_get($data, "filename"), mime: data_get($data, "mime"), path: data_get($data, "path"), size: data_get($data, "size"), @@ -35,4 +52,20 @@ class File { return \json_decode(\json_encode($this), true); } + + public function toDB(): array + { + return [ + "id" => $this->id, + "recordId" => $this->recordId, + "filename" => $this->filename, + "mime" => $this->mime, + "path" => $this->path, + "size" => $this->size, + "width" => $this->width, + "height" => $this->height, + "checksum" => $this->checksum, + "captions" => "[]", // for the future + ]; + } } diff --git a/src/File/FileRepo.php b/src/File/FileRepo.php new file mode 100644 index 0000000..fd579f4 --- /dev/null +++ b/src/File/FileRepo.php @@ -0,0 +1,30 @@ +table("lucent_files")->insert($file->toDB()); + } + + /** + * @return File[] + */ + public static function byRecordId(string $recordId): array + { + return Database::make() + ->table("lucent_files") + ->where("recordId", $recordId) + ->get() + ->map(File::fromDB(...)) + ->toArray(); + } +} diff --git a/src/File/FileService.php b/src/File/FileService.php index af9983b..a3fc151 100644 --- a/src/File/FileService.php +++ b/src/File/FileService.php @@ -85,10 +85,10 @@ class FileService [$width, $height] = $this->isImage($mimetype) ? getimagesize($file) : [0, 0]; - return new DataFile( + $dataFile = new DataFile( id: Id::new(), recordId: $recordId, - originalName: $originalFilename, + filename: $originalFilename, mime: $mimetype, path: $path, size: $file->getSize(), @@ -96,6 +96,9 @@ class FileService height: $height, checksum: $checksum, ); + + FileRepo::create($dataFile); + return $dataFile; } private function createFileName( @@ -112,10 +115,11 @@ class FileService private function isImage(string $mimetype): bool { $imageMimes = [ - "image/webp", - "image/gif", "image/jpeg", "image/png", + "image/avif", + "image/webp", + "image/gif", "image/tiff", ]; return in_array($mimetype, $imageMimes); @@ -156,4 +160,12 @@ class FileService $image = $originalImage->fit(300, 300); $disk->put($thumbDir, $image->encode("webp", 75)); } + + /** + * @return DataFile[] + */ + public function filesForRecord(string $recordId): array + { + return FileRepo::byRecordId($recordId); + } } diff --git a/src/Http/Controller/RecordController.php b/src/Http/Controller/RecordController.php index c6fa399..24a0274 100644 --- a/src/Http/Controller/RecordController.php +++ b/src/Http/Controller/RecordController.php @@ -15,6 +15,7 @@ use Lucent\Record\InputData\RecordInputData; use Lucent\Record\Manager; use Lucent\Record\QueryRecord; use Lucent\Record\RecordService; +use Lucent\File\FileService; use Lucent\Record\Status; use Lucent\Schema\System; use Lucent\Schema\Ui\Reference; @@ -30,6 +31,7 @@ class RecordController extends Controller private readonly RecordService $recordService, private readonly AccountService $accountService, private readonly ChannelService $channelService, + private readonly FileService $fileService, private readonly Svelte $svelte, private readonly Query $query, private readonly Manager $recordManager, @@ -493,4 +495,11 @@ class RecordController extends Controller } return ok(); } + + public function files(Request $request) + { + $recordId = $request->input("recordId"); + + return $this->fileService->filesForRecord($recordId); + } } diff --git a/src/Http/web.php b/src/Http/web.php index 0da82e4..5ec3f12 100644 --- a/src/Http/web.php +++ b/src/Http/web.php @@ -12,89 +12,130 @@ use Lucent\Http\Controller\RecordController; use Lucent\Http\Controller\RevisionController; use Lucent\Http\Controller\SetupController; +Route::get("/lucent/setup", [SetupController::class, "setup"]); +Route::get("/lfs-{disk}/{any}", [FileController::class, "fromDisk"])->where( + "any", + ".*", +); -Route::get('/lucent/setup', [SetupController::class, 'setup']); -Route::get('/lfs-{disk}/{any}', [FileController::class, 'fromDisk'])->where('any', '.*'); +Route::group( + [ + "middleware" => ["web"], + "prefix" => "lucent", + ], + function () { + Route::middleware(["lucent.guest"])->group(function () { + Route::get("/", [AuthController::class, "login"]); + Route::get("/register", [AuthController::class, "register"]); + Route::post("/register", [AuthController::class, "postRegister"]); + Route::get("/login", [AuthController::class, "login"]); + Route::post("/login", [AuthController::class, "postLogin"]); + Route::get("/verify", [AuthController::class, "verify"]); + Route::post("/verify", [AuthController::class, "postVerify"]); + }); -Route::group([ - 'middleware' => ['web'], - 'prefix' => "lucent" -], function () { + Route::middleware("lucent.auth")->group(function () { + Route::get("/logout", [AuthController::class, "logout"]); + Route::get("/profile", [AccountController::class, "profile"]); + Route::post("/account/update-name", [ + AccountController::class, + "updateName", + ]); + Route::post("/account/update-email", [ + AccountController::class, + "updateEmail", + ]); + Route::get("/command-report/{signature}", [ + BuildController::class, + "report", + ]); + Route::get("/command-report-source/{signature}", [ + BuildController::class, + "reportSource", + ]); + Route::post("/command/{signature}", [ + BuildController::class, + "build", + ]); + }); + Route::middleware(["lucent.auth"])->group(function () { + Route::get("/members/", [MemberController::class, "index"]); + Route::post("/members/invite", [MemberController::class, "invite"]); + Route::post("/members/update", [MemberController::class, "update"]); + }); + Route::middleware(["lucent.auth"]) + ->prefix("/records") + ->group(function () { + Route::get("/files", [RecordController::class, "files"]); + Route::get("/new", [RecordController::class, "new"]); + Route::get("/newInline", [ + RecordController::class, + "newInline", + ]); + Route::get("/suggestions", [ + RecordController::class, + "suggestions", + ]); + Route::get("/{rid}", [RecordController::class, "edit"]); + Route::post("/clone/{rid}", [RecordController::class, "clone"]); + // Route::get('/editInline/{rid}', [RecordController::class, 'editInline']); + Route::get("/{rid}/parents", [ + RecordController::class, + "parents", + ]); + Route::post("/", [RecordController::class, "save"]); + Route::post("/status/{status}", [ + RecordController::class, + "status", + ]); + Route::post("/delete", [RecordController::class, "delete"]); + Route::post("/{rid}/rollback/{version}", [ + RecordController::class, + "rollback", + ]); + }); - Route::middleware(['lucent.guest'])->group(function () { - Route::get('/', [AuthController::class, 'login']); + Route::middleware(["lucent.auth"]) + ->prefix("/edges") + ->group(function () { + Route::post("/insert-many", [ + EdgeController::class, + "insertMany", + ]); + }); - Route::get('/register', [AuthController::class, 'register']); - Route::post('/register', [AuthController::class, 'postRegister']); - Route::get('/login', [AuthController::class, 'login']); - Route::post('/login', [AuthController::class, 'postLogin']); - Route::get('/verify', [AuthController::class, 'verify']); - Route::post('/verify', [AuthController::class, 'postVerify']); + Route::middleware(["lucent.auth"])->group(function () { + Route::get("/records/{rid}/revisions", [ + RevisionController::class, + "index", + ]); + }); - }); + Route::middleware(["lucent.auth"])->group(function () { + Route::get("/", [HomeController::class, "home"]); + Route::get("/home/records", [HomeController::class, "records"]); + }); - Route::middleware('lucent.auth')->group(function () { - Route::get('/logout', [AuthController::class, 'logout']); - Route::get('/profile', [AccountController::class, 'profile']); - Route::post('/account/update-name', [AccountController::class, 'updateName']); - Route::post('/account/update-email', [AccountController::class, 'updateEmail']); - Route::get('/command-report/{signature}', [BuildController::class, 'report']); - Route::get('/command-report-source/{signature}', [BuildController::class, 'reportSource']); - Route::post('/command/{signature}', [BuildController::class, 'build']); - }); - - - Route::middleware(["lucent.auth"])->group(function () { - Route::get('/members/', [MemberController::class, 'index']); - Route::post('/members/invite', [MemberController::class, 'invite']); - Route::post('/members/update', [MemberController::class, 'update']); - }); - - - Route::middleware(["lucent.auth"])->prefix("/records")->group(function () { - - Route::get('/new', [RecordController::class, 'new']); - Route::get('/newInline', [RecordController::class, 'newInline']); - Route::get('/suggestions', [RecordController::class, 'suggestions']); - Route::get('/{rid}', [RecordController::class, 'edit']); - Route::post('/clone/{rid}', [RecordController::class, 'clone']); -// Route::get('/editInline/{rid}', [RecordController::class, 'editInline']); - Route::get('/{rid}/parents', [RecordController::class, 'parents']); - Route::post('/', [RecordController::class, 'save']); - Route::post('/status/{status}', [RecordController::class, 'status']); - Route::post('/delete', [RecordController::class, 'delete']); - Route::post('/{rid}/rollback/{version}', [RecordController::class, 'rollback']); - }); - - Route::middleware(["lucent.auth"])->prefix("/edges")->group(function () { - Route::post('/insert-many', [EdgeController::class, 'insertMany']); - }); - - Route::middleware(["lucent.auth"])->group(function () { - Route::get('/records/{rid}/revisions', [RevisionController::class, 'index']); - - }); - - Route::middleware(["lucent.auth"])->group(function () { - Route::get('/', [HomeController::class, 'home']); - Route::get('/home/records', [HomeController::class, 'records']); - }); - - Route::middleware(["lucent.auth"])->prefix("/content")->group(function () { - Route::get('/{schemaName}', [RecordController::class, 'index']); - Route::get('/{schemaName}/csv', [RecordController::class, 'exportCSV']); - Route::get('/{schemaName}/emptyTrash', [RecordController::class, 'emptyTrash']); - }); - - Route::middleware(["lucent.auth"])->group(function () { - Route::post('/files/upload', [FileController::class, 'upload']); - Route::get('/files/download', [FileController::class, 'download']); - }); - - - -}); + Route::middleware(["lucent.auth"]) + ->prefix("/content") + ->group(function () { + Route::get("/{schemaName}", [RecordController::class, "index"]); + Route::get("/{schemaName}/csv", [ + RecordController::class, + "exportCSV", + ]); + Route::get("/{schemaName}/emptyTrash", [ + RecordController::class, + "emptyTrash", + ]); + }); + Route::middleware(["lucent.auth"])->group(function () { + Route::post("/files/upload", [FileController::class, "upload"]); + Route::get("/files/download", [FileController::class, "download"]); + }); + }, +);