Add upload backend

This commit is contained in:
Bruno Rybársky 2024-03-01 22:14:20 +01:00
parent 0c2cb115bf
commit 4a2712af3c
2 changed files with 194 additions and 0 deletions

14
endpoints/upload.php Normal file

@ -0,0 +1,14 @@
<?php
require_once "lib/upload.php";
function endpoint($endpoint_data): array
{
return match ($endpoint_data["action"]) {
"getMyFiles" => listFiles(),
"getAllFiles" => listFiles(false),
"UploadFiles" => parseIncomingFiles(),
default => ["Status" => "Fail", "message" => "Invalid action"],
};
}

180
lib/upload.php Normal file

@ -0,0 +1,180 @@
<?php
function makePathSafe($userInput): string
{
// Keep only alphanumeric characters, underscores, and hyphens
$safeString = preg_replace('/[^\w\-]/', '', $userInput);
// Ensure no path traversal
$safeString = str_replace('..', '_', $safeString);
// Trim leading/trailing underscores
$safeString = trim($safeString, '_');
// Replace directory separator characters with underscores
$safeString = str_replace(['/', '\\'], '_', $safeString);
// Limit length for safety
return substr($safeString, 0, 255);
}
function getIncomingFiles(): array
{
$files = $_FILES;
$files2 = [];
foreach ($files as $infoArr) {
$filesByInput = [];
foreach ($infoArr as $key => $valueArr) {
if (is_array($valueArr)) { // file input "multiple"
foreach ($valueArr as $i => $value) {
$filesByInput[$i][$key] = $value;
}
} else { // -> string, normal file input
$filesByInput[] = $infoArr;
break;
}
}
$files2 = array_merge($files2, $filesByInput);
}
$files3 = [];
foreach ($files2 as $file) { // let's filter empty & errors
if (!$file['error']) $files3[] = $file;
}
return $files3;
}
function saveUploadedFileInDatabase($filePath, $fileType):bool
{
global $mysqli;
$stmt = $mysqli->prepare("INSERT INTO Files (Path, Type, UploadedBy, UploadedAt) VALUES (?, ?, ?, NOW())");
$stmt->bind_param("ssi", $filePath, $fileType, $_SESSION["ID"]);
$stmt->execute();
$stat = $stmt->affected_rows > 0;
$stmt->close();
return $stat;
}
function doImageUpload($inFile, $outFile): bool
{
// Create Imagick object
$imagick = new Imagick($inFile);
// Set the desired format for reencoding (WebP)
$imagick->setImageFormat('webp');
// Remove non-essential metadata
$imagick->stripImage();
// Write the reencoded image to the output file
$imagick->writeImage($outFile);
// Destroy the Imagick object to free up resources
$imagick->destroy();
// Check if the reencoding was successful
if (file_exists($outFile)) {
return saveUploadedFileInDatabase($outFile, 'image/webp');
} else {
return false;
}
}
function listFiles($onlyMine = true):array
{
$output = ["Status" => "Fail"];
require_once "lib/account.php";
if(($onlyMine && isLoggedIn()) || (!$onlyMine && isModerator())) {
global $mysqli;
$query = "SELECT ID, Path, Type, UploadedAt, UploadedBy FROM Files";
if($onlyMine){
$query .= " WHERE UploadedBy = ?";
}
$stmt = $mysqli->prepare($query);
if($onlyMine) {
$stmt->bind_param("i", $_SESSION["ID"]);
}
$id = 0;
$path = "";
$type = "";
$uploadedAt = "";
$uploadedBy = 0;
$stmt->bind_result($id, $path, $type, $uploadedAt, $uploadedBy);
$stmt->execute();
// Fetch the results into the bound variables
while ($stmt->fetch()) {
$files[] = [
'ID' => $id,
'Path' => $path,
'Type' => $type,
'UploadedAt' => $uploadedAt,
'UploadedBy' => $uploadedBy,
];
}
// Check if any results were fetched
if (!empty($files)) {
$output["Status"] = "Success";
$output["Files"] = $files;
}
$stmt->close();
}
return $output;
}
function parseIncomingFiles(): array
{
$incomingFiles = getIncomingFiles();
$success = true;
foreach ($incomingFiles as $incomingFile) {
if ($incomingFile["error"] == 0 && is_file($incomingFile["tmp_name"])) {
$type = explode("/", $incomingFile["type"]);
switch ($type) {
case "image":
$imgFname = pathinfo($incomingFile["name"], PATHINFO_FILENAME);
$uploadPath = getUploadPath("image", $imgFname);
if(!empty($uploadPath)) {
if (!doImageUpload($incomingFile["tmp_name"], $uploadPath)) {
$success = false;
}
}
else{
$success = false;
}
break;
}
}
}
$output = ["Status" => "Fail"];
if($success){
$output["Status"] = "Success";
}
return $output;
}
function getUploadPath($type = "unknown", $filename = "hehe"): string
{
$type = makePathSafe($type);
$id = makePathSafe($_SESSION["ID"]);
$date = makePathSafe(date("Y/m/d"));
$filename = makePathSafe($filename);
$extension = match ($type) {
'image' => 'webp',
default => 'dummy',
};
if($extension != "dummy") {
return "uploads/$type/$id/$date/$filename.$extension";
}
else {
return "";
}
}