Skip to content

Commit

Permalink
Merge pull request #175
Browse files Browse the repository at this point in the history
dev
  • Loading branch information
leonardosahon authored Oct 17, 2024
2 parents ac0b6d8 + 472bf59 commit be0403b
Show file tree
Hide file tree
Showing 7 changed files with 268 additions and 62 deletions.
5 changes: 5 additions & 0 deletions src/Core/LayException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?php
declare(strict_types=1);
namespace BrickLayer\Lay\Core;

class LayException extends Exception {}
71 changes: 55 additions & 16 deletions src/Libs/Cron/LayCron.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@
use BrickLayer\Lay\Core\LayConfig;
use BrickLayer\Lay\Libs\LayArray;
use BrickLayer\Lay\Libs\LayDate;
use BrickLayer\Lay\Libs\LayFn;

//TODO: Attach a project ID to all cron jobs, so that the system can identify all jobs created by a particular project
//The id is the one generated for each lay project
final class LayCron
{
private const CRON_FILE = "/tmp/crontab.txt";
private const CRON_DB_FILE = "cron_jobs.json";
private const APP_ID_KEY = "--LAY_APP_ID";
private const DB_SCHEMA = [
"mailto" => "",
"jobs" => [],
Expand Down Expand Up @@ -69,7 +69,10 @@ public static function dump_crontab(bool $suppress_win_exception = false) : stri
");
}

exec("cat " . self::CRON_FILE, $out);
$out = self::new()->get_crontab();

if(!$out)
return false;

return implode(PHP_EOL, $out);
}
Expand Down Expand Up @@ -149,12 +152,49 @@ private function db_data_save() : bool {
return (bool) file_put_contents($this->cron_db(), json_encode($data));
}

private function project_server_jobs(string $mailto, string $cron_jobs) : string
{
$server_jobs = file_get_contents(self::CRON_FILE);

$all_jobs = "";
$app_id = LayConfig::app_id();

foreach (explode(PHP_EOL, $server_jobs) as $i => $job) {
if(empty($job))
continue;

if($i == 0 && $job == 'MAILTO=""') {
$all_jobs .= $mailto;
continue;
}

$job_app_id = LayFn::extract_cli_tag(self::APP_ID_KEY, true, $job);
$job_app_id = $job_app_id ? trim($job_app_id) : $job_app_id;

if($job_app_id != $app_id) {
$all_jobs .= $job . PHP_EOL;
}
}

if(empty($all_jobs))
$all_jobs = $mailto;

$all_jobs .= $cron_jobs;

return $all_jobs;
}

private function crontab_save() : bool {
$mailto = $this->report_email ? 'MAILTO=' . $this->report_email : 'MAILTO=""';
$mailto .= PHP_EOL;
$cron_jobs = implode("", $this->jobs_list);

$exec = @file_put_contents(self::CRON_FILE, $mailto . $cron_jobs);
$data = $this->project_server_jobs(
mailto: $mailto,
cron_jobs: $cron_jobs
);

$exec = @file_put_contents(self::CRON_FILE, $data);

if($exec) {
exec("crontab '" . self::CRON_FILE . "' 2>&1", $out);
Expand Down Expand Up @@ -201,21 +241,17 @@ private function make_job(string $job) : string {
}

private function add_job(string $job) : void {
$add = str_contains(shell_exec("crontab -l 2>&1"), "no crontab for");
$job .= " --LAY_APP_ID '" . LayConfig::app_id() ."'";
$job = rtrim($job, PHP_EOL) . " " . self::APP_ID_KEY . " " . LayConfig::app_id() . PHP_EOL;

$job_exists = $this->db_job_exists($job)['found'];

if(!$add && !$this->db_job_exists($job)['found']) {
if(!$job_exists) {
if(isset($this->job_id))
$this->jobs_list[$this->job_id] = $job;
else
$this->jobs_list[] = $job;

$add = true;
}

if(!$add && $this->db_email_exists())
return;

$this->commit();
}

Expand Down Expand Up @@ -372,11 +408,14 @@ public function get_job(string|int $uid) : ?array {
];
}

public function get_crontab() : string {
if(!file_exists(self::CRON_FILE))
return "";
public function get_crontab() : ?array
{
exec("crontab -l 2>&1", $out);

if(str_contains($out[0], "no crontab for"))
return null;

return file_get_contents(self::CRON_FILE);
return $out;
}

public function unset(string|int $uid_or_job) : bool {
Expand Down
10 changes: 9 additions & 1 deletion src/Libs/FileUpload/Enums/FileUploadExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,18 @@

enum FileUploadExtension : string
{
// Docs
case PDF = 'application/pdf';
case CSV = 'text/csv';
case EXCEL = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
case EXCEL_OLD = 'application/vnd.ms-excel';
case ZIP = 'application/zip';

// Extra doc extensions
case EXCEL_OLD = 'application/vnd.ms-excel';
case ZIP_OLD = 'application/x-zip-compressed';

// Images
case PNG = "image/png";
case JPEG = "image/jpeg";
case HEIC = "image/heic";
}
184 changes: 164 additions & 20 deletions src/Libs/FileUpload/FileUpload.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,22 @@
use JetBrains\PhpStorm\ArrayShape;

final class FileUpload {
#[ArrayShape([
'uploaded' => 'bool',
'dev_error' => 'string',
'error' => 'string',
'error_type' => "BrickLayer\\Lay\\Libs\\FileUpload\\Enums\\FileUploadErrors",
'upload_type' => "BrickLayer\\Lay\\Libs\\FileUpload\\Enums\\FileUploadType",
'storage' => "BrickLayer\\Lay\\Libs\\FileUpload\\Enums\\FileUploadStorage",
'url' => 'string',
'size' => 'int',
'width' => 'int',
'height' => 'int',
])]
public ?array $response = null;

protected FileUploadStorage $storage;
protected ?FileUploadStorage $storage = null;
protected ?FileUploadType $upload_type = null;

use Image;
use Doc;
Expand All @@ -23,10 +36,79 @@ final class FileUpload {
* @throws \Exception
*/
public function __construct(
protected ?FileUploadType $upload_type = null,
#[ArrayShape([
// Name of file from the form
'post_name' => 'string',

// New name and file extension of file after upload
'new_name' => 'string',

//<<START DISK KEY
'directory' => 'string',
'permission' => 'int',
//<<END DISK KEY

// The path the bucket should use in storing your file. Example: files/user/1/report/
'bucket_path' => 'string',

// Use this to force bucket upload in development environment
'upload_on_dev' => 'bool',

// File limit in bytes
'file_limit' => 'int',

// If nothing is provided the system will not validate for the extension type
'extension' => 'BrickLayer\Lay\Libs\FileUpload\Enums\FileUploadExtension',

// An array of BrickLayer\Lay\Libs\FileUpload\Enums\FileUploadExtension
'extension_list' => 'array<BrickLayer\Lay\Libs\FileUpload\Enums\FileUploadExtension]>',

// Use this to add a custom MIME that does not exist in the extension key above
'custom_mime' => 'array', // ['application/zip', 'application/x-zip-compressed']

// The type of storage the file should be uploaded to
'storage' => 'BrickLayer\Lay\Libs\FileUpload\Enums\FileUploadStorage',

// Add last modified time to the returned url key, so that your browser can cache it.
// This is necessary if you are using the same 'new_name' for multiple versions of a file
// The new file will overwrite the old file, and the last_mod_time will force the browser to update its copy
'add_mod_time' => 'bool',

// The compression quality to produce after uploading an image: [10 - 100]
'quality' => 'int',

// The dimension an image should maintain: [max_width, max_height]
'dimension' => 'array',

// If the php temporary file should be moved or copied. This is necessary if you want to generate a thumbnail
// and other versions of the image from one upload file
'copy_tmp_file' => 'bool',
])]
array $opts = []
)
{
$req = $this->check_all_requirements(
post_name: $opts['post_name'],
custom_mime: $opts['custom_mime'] ?? null,
extension_list: $opts['extension_list'] ?? null,
);

if($req)
return $this->response = $req;

if( !$req ) {
$mime = mime_content_type($_FILES[$opts['post_name']]['tmp_name']);

if(str_starts_with($mime, "image/"))
$this->upload_type = FileUploadType::IMG;

elseif(str_starts_with($mime, "video/"))
$this->upload_type = FileUploadType::VIDEO;

else
$this->upload_type = FileUploadType::DOC;
}

if($this->upload_type == FileUploadType::IMG)
$this->response = $this->image_upload($opts);

Expand Down Expand Up @@ -115,6 +197,7 @@ private function check_all_requirements(
?int $file_limit = null,
FileUploadExtension|null|string $extension = null,
?array $custom_mime = null,
?array $extension_list = null,
) : ?array
{
if(!isset($_FILES[$post_name]))
Expand Down Expand Up @@ -156,38 +239,99 @@ private function check_all_requirements(
);
}

if($extension || $custom_mime) {
if($extension_list) {
$mime = mime_content_type($file);
$found = false;

if(!$custom_mime)
$pass = match ($extension) {
FileUploadExtension::PDF => $mime == FileUploadExtension::PDF,
FileUploadExtension::CSV => $mime == FileUploadExtension::CSV,

FileUploadExtension::ZIP,
FileUploadExtension::ZIP_OLD =>
$mime == FileUploadExtension::ZIP || $mime == FileUploadExtension::ZIP_OLD,
foreach ($extension_list as $list) {
if(!($list instanceof FileUploadExtension)) {
$extension_list = implode(",", $extension_list);
$this->exception("extension_list must be of type " . FileUploadExtension::class . "; extension_list: [$extension_list]. File Mime: [$mime]");
}

FileUploadExtension::EXCEL,
FileUploadExtension::EXCEL_OLD =>
$mime == FileUploadExtension::EXCEL_OLD || $mime == FileUploadExtension::EXCEL
};
else
$pass = in_array($mime, $custom_mime, true);
if($list->value == $mime) {
$found = true;
break;
}
}

if(!$pass) {
$extension = is_string($extension) ? $extension : $extension->name;
if(!$found) {
$extension_list = implode(",", $extension_list);

return $this->upload_response(
false,
[
"error" => "Uploaded file does not match the required file type: [$extension]",
"dev_error" => "Uploaded file had: [$mime], but required mime types are: [$extension_list]; Class: " . self::class,
"error" => "File type is invalid",
"error_type" => FileUploadErrors::WRONG_FILE_TYPE
]
);
}
}

if($extension || $custom_mime) {
$mime = mime_content_type($file);

if(!$custom_mime) {
$pass = false;
$test_multiple = function (FileUploadExtension ...$ext) use ($extension, $mime) : bool {
if(!in_array($extension, $ext, true))
return false;

$pass = false;

foreach ($ext as $e) {
if($pass)
break;

$pass = $mime == $e->value;
}

return $pass;
};

foreach (FileUploadExtension::cases() as $case) {
if($pass)
break;

if($pass = $test_multiple(FileUploadExtension::ZIP, FileUploadExtension::ZIP_OLD))
break;

if($pass = $test_multiple(FileUploadExtension::EXCEL, FileUploadExtension::EXCEL_OLD))
break;

$pass = $mime == $case->value;
}

if(!$pass) {
$extension = is_string($extension) ? $extension : $extension->name;

return $this->upload_response(
false,
[
"dev_error" => "Uploaded file had: [$mime], but required mime type is: [$extension]; Class: " . self::class,
"error" => "Uploaded file does not match the required file type: [$extension]",
"error_type" => FileUploadErrors::WRONG_FILE_TYPE
]
);
}
}
else {
if (!in_array($mime, $custom_mime, true)) {
$custom_mime = implode(",", $custom_mime);

return $this->upload_response(
false,
[
"dev_error" => "Uploaded file had: [$mime], but required mime types are: [$custom_mime]; Class: " . self::class,
"error" => "Uploaded file is not accepted",
"error_type" => FileUploadErrors::WRONG_FILE_TYPE
]
);
}
}
}

return null;
}
}
Loading

0 comments on commit be0403b

Please sign in to comment.