diff options
| -rw-r--r-- | public/delete.php | 64 | ||||
| -rw-r--r-- | public/index.php | 35 | ||||
| -rw-r--r-- | public/upload.php | 16 | ||||
| -rw-r--r-- | public/uploaders.php | 8 |
4 files changed, 118 insertions, 5 deletions
diff --git a/public/delete.php b/public/delete.php new file mode 100644 index 0000000..161c6ed --- /dev/null +++ b/public/delete.php @@ -0,0 +1,64 @@ +<?php +include_once $_SERVER['DOCUMENT_ROOT'] . '/../config.php'; +include_once $_SERVER['DOCUMENT_ROOT'] . '/../lib/utils.php'; + +if (!FILE_DELETION) { + json_response(null, 'File deletion is not allowed!', 403); + exit(); +} + +$file_id = $_GET['f'] ?: null; +$password = $_GET['key'] ?: null; + +if (!isset($file_id, $password)) { + json_response(null, "Fields 'f' and 'key' must be set!", 400); + exit(); +} + +$file_id = explode('.', $file_id); +$file_ext = $file_id[1]; +$file_id = $file_id[0]; + +if (!preg_match('/^[a-zA-Z0-9_-]+$/', $file_id) || !preg_match('/^[a-zA-Z0-9]+$/', $file_ext)) { + json_response(null, "Invalid file ID or extension", 400); + exit(); +} + +if (!is_file(FILE_UPLOAD_DIRECTORY . "/{$file_id}.{$file_ext}")) { + json_response(null, "File {$file_id} not found", 404); + exit(); +} + +if (!is_file(FILE_METADATA_DIRECTORY . "/{$file_id}.metadata.json")) { + json_response(null, "File metadata {$file_id} not found", 404); + exit(); +} + +$metadata = json_decode(file_get_contents(FILE_METADATA_DIRECTORY . "/{$file_id}.metadata.json"), true); + +if (!array_key_exists('password', $metadata)) { + json_response(null, "File {$file_id} does not have a password. File cannot be deleted!", 400); + exit(); +} + +if (!password_verify($password, $metadata['password'])) { + json_response(null, "Bad password", 401); + exit(); +} + +if ( + !unlink(FILE_UPLOAD_DIRECTORY . "/{$file_id}.{$file_ext}") || + !unlink(FILE_THUMBNAIL_DIRECTORY . "/{$file_id}.webp") || + !unlink(FILE_METADATA_DIRECTORY . "/{$file_id}.metadata.json") +) { + json_response(null, "Failed to delete a file ID {$file_id}", 500); + exit(); +} + +json_response( + [ + 'id' => $file_id, + 'extension' => $file_ext + ], + 'Successfully deleted the file' +);
\ No newline at end of file diff --git a/public/index.php b/public/index.php index 9da069d..d6c759c 100644 --- a/public/index.php +++ b/public/index.php @@ -273,6 +273,10 @@ include_once $_SERVER['DOCUMENT_ROOT'] . '/../lib/partials.php'; if (file.urls && file.urls.download_url) { file_url = file.urls.download_url; } + let file_deletion = ''; + if (file.urls && file.urls.deletion_url) { + file_deletion = `<button onclick="deleteUploadedFile('${file.urls.deletion_url}', '${file.id}')">Delete</button>`; + } return ` <div class="box item column gap-4 pad-4"> @@ -292,11 +296,36 @@ include_once $_SERVER['DOCUMENT_ROOT'] . '/../lib/partials.php'; <a href="${file_url}"> <button>Open</button> </a> + ${file_deletion} </div> </div> `; } + function deleteUploadedFile(url, id) { + fetch(url, { + 'headers': { + 'Accept': 'application/json' + }, + 'method': 'DELETE' + }).then((r) => r.json()) + .then((json) => { + if (json.status_code != 200) { + alert(`${json.message} (${json.status_code})`); + return; + } + + let files = getUploadedFiles(); + files = files.filter((x) => x.id !== id); + localStorage.setItem('uploaded_files', JSON.stringify(files)); + loadUploadedFiles(); + }) + .catch((err) => { + alert('Failed to delete the file. Look into the console!'); + console.error(err); + }); + } + // loading already existing uploaded files function loadUploadedFiles() { let files = getUploadedFiles(); @@ -307,13 +336,9 @@ include_once $_SERVER['DOCUMENT_ROOT'] . '/../lib/partials.php'; html += addUploadedFile(file); } - if (html.length > 0) { - uploadedFiles.parentElement.style.display = 'flex'; - } + uploadedFiles.parentElement.style.display = html.length > 0 ? 'flex' : 'none'; uploadedFiles.innerHTML = html; - - localStorage.setItem('uploaded_files', JSON.stringify(files)); } loadUploadedFiles(); diff --git a/public/upload.php b/public/upload.php index 96d1f4b..1f5b838 100644 --- a/public/upload.php +++ b/public/upload.php @@ -162,11 +162,27 @@ try { 'download_url' => INSTANCE_URL . "/{$file_data['id']}.{$file_data['extension']}" ]; + if (FILE_METADATA && FILE_DELETION) { + $file_data['password'] = $_POST['password'] ?? generate_random_char_sequence(FILE_ID_CHARACTERS, FILE_DELETION_KEY_LENGTH); + $file_data['urls']['deletion_url'] = INSTANCE_URL . "/delete.php?f={$file_data['id']}.{$file_data['extension']}&key={$file_data['password']}"; + } + if ($_SERVER['HTTP_ACCEPT'] == 'application/json') { json_response($file_data, null, 201); } else { header("Location: /{$file_data['id']}.{$file_data['extension']}"); } + + if (FILE_METADATA) { + unset($file_data['urls']); + $file_data['password'] = password_hash($file_data['password'], PASSWORD_DEFAULT); + if (!is_dir(FILE_METADATA_DIRECTORY) && !mkdir(FILE_METADATA_DIRECTORY, 0777, true)) { + throw new RuntimeException('Failed to create a folder for file metadata'); + } + if (!file_put_contents(FILE_METADATA_DIRECTORY . "/{$file_data['id']}.metadata.json", json_encode($file_data, JSON_UNESCAPED_SLASHES))) { + throw new RuntimeException('Failed to create a file metadata'); + } + } } catch (RuntimeException $e) { if ($_SERVER['HTTP_ACCEPT'] == 'application/json') { json_response(null, $e->getMessage(), 400); diff --git a/public/uploaders.php b/public/uploaders.php index 57cf8b4..ed0478a 100644 --- a/public/uploaders.php +++ b/public/uploaders.php @@ -75,6 +75,10 @@ foreach (FILE_ACCEPTED_MIME_TYPES as $k => $v) { <th>URL:</th> <td><code class="copy">{json:data.urls.download_url}</code></td> </tr> + <tr> + <th>Deletion URL:</th> + <td><code class="copy">{json:data.urls.deletion_url}</code></td> + </tr> </table> <p>Then, select it via <b>Destinations → Image uploader → Custom image uploader</b></p> @@ -103,6 +107,10 @@ foreach (FILE_ACCEPTED_MIME_TYPES as $k => $v) { <th>Image link:</th> <td><code class="copy">{data.urls.download_url}</code></td> </tr> + <tr> + <th>Deletion link:</th> + <td><code class="copy">{data.urls.deletion_url}</code></td> + </tr> </table> </section> </div> |
