Add Minio integration for wash certificate storage

Introduced Minio support to store and manage wash certificates, replacing local file storage. This includes implementing traits, interfaces, and a Minio client integration, along with a CLI utility and relevant tests. Updated relevant modules and configurations.
This commit is contained in:
Jepp9350
2025-01-14 10:56:50 +01:00
parent d62e3fa17a
commit 0413071432
10 changed files with 387 additions and 40 deletions
+23 -22
View File
@@ -8,10 +8,11 @@ class router
private string $method;
private array $routes;
private array $routeClasses; // Contains the classes that have the route_t trait
public function __construct()
{
$this->url = $_SERVER['REQUEST_URI'];
$this->method = $_SERVER['REQUEST_METHOD'];
$this->url = $_SERVER['REQUEST_URI'] ?? ($argv[1] ?? '/'); // Get the URL (Or the first argument if it's a CLI request)
$this->method = $_SERVER['REQUEST_METHOD'] ?? ($argv[2] ?? 'GET'); // Get the method (Or the second argument if it's a CLI request)
$this->routes = [];
$this->routeClasses = [];
}
@@ -21,22 +22,11 @@ class router
$this->routes[] = ['route' => $route, 'method' => $method, 'function' => $function];
}
public function run(): void
{
foreach ($this->routeClasses as $class) {
$route = new $class();
// Add the routes to the router
$route->run();
}
$this->routeRequest();
}
public function auto_load_routes(string $path): void
{
global $response;
$files = scandir($path);
foreach ($files as $file) {
foreach ( $files as $file ) {
if ($file == '.' || $file == '..') {
continue;
}
@@ -45,7 +35,7 @@ class router
// Get all the classes in the files with the route_t trait
$classes = get_declared_classes();
foreach ($classes as $class) {
foreach ( $classes as $class ) {
if (in_array('traits\route_t', class_uses($class))) {
$this->routeClasses[] = $class;
}
@@ -59,21 +49,22 @@ class router
}
}
public function ERROR_HANDLER($callback): void
public function run(): void
{
try {
$callback();
} catch (\Exception $e) {
global $response;
$response->internal_server_error($e->getMessage());
foreach ( $this->routeClasses as $class ) {
$route = new $class();
// Add the routes to the router
$route->run();
}
$this->routeRequest();
}
private function routeRequest(): void
{
global $response;
$matching_route_found = false;
foreach ($this->routes as $route) {
foreach ( $this->routes as $route ) {
if ($this->doesRouteMatchCurrent($route['route']) && $route['method'] == $this->method) {
$route['function']();
$matching_route_found = true;
@@ -105,4 +96,14 @@ class router
// None of the matches were found
return false;
}
public function ERROR_HANDLER($callback): void
{
try {
$callback();
} catch (\Exception $e) {
global $response;
$response->internal_server_error($e->getMessage());
}
}
}
+30
View File
@@ -0,0 +1,30 @@
<?php
namespace classes;
use interfaces\minio_wash_certificates_i;
use traits\minio_t;
class wash_certificate_store implements minio_wash_certificates_i
{
use minio_t;
public function __construct()
{
self::setBucket('truckwashdev'); // Change this to washcertificates when in production
}
/**
* @inheritDoc
*/
public function washCertificateExists(string $certificate_id): bool
{
// Check if the file exists
$objects = self::getS3Client()->listObjects([
'Bucket' => self::getBucket(),
'Prefix' => $certificate_id
]);
return count($objects['Contents'] ?? []) > 0;
}
}
+23
View File
@@ -0,0 +1,23 @@
<?php
// This script only runs when the program is called from the command line. It is used to run the CLI script.
if (php_sapi_name() !== 'cli') {
exit;
}
$args = $argv;
echo "This is the CLI script";
echo "\n";
echo "Arguments: ";
print_r($args);
// If the first argument is 'run', switch to the second argument
if ($args[1] === 'run') {
switch ($args[2]) {
case 'minio-test':
echo "Running the minio test script";
require_once 'tests/minio/minioTest.php';
break;
default:
echo "Invalid script name";
break;
}
}
+6 -1
View File
@@ -1,4 +1,4 @@
<?php global $CONFIG_DB, $DEBUG, $ENCRYPTION_KEY, $CORS, $ECONOMIC_API, $WORDPRESS_STATIC_TOKEN, $USE_PROD_ECONOMIC_IN_DEBUG;
<?php global $CONFIG_DB, $DEBUG, $ENCRYPTION_KEY, $CORS, $ECONOMIC_API, $WORDPRESS_STATIC_TOKEN, $USE_PROD_ECONOMIC_IN_DEBUG, $EMAIL_WASH_CERTIFICATE_TOKEN, $MINIO;
$CONFIG_DB = [
'host' => '', // IP address of the database server e.g. 127.0.0.1
'user' => '', // Username of the database server e.g. root
@@ -23,3 +23,8 @@ if ($DEBUG && !$USE_PROD_ECONOMIC_IN_DEBUG) {
}
$WORDPRESS_STATIC_TOKEN = ''; // Static token used to authenticate the WordPress plugin
$EMAIL_WASH_CERTIFICATE_TOKEN = ''; // Token used to authenticate the wash certificate generator
$MINIO = [
'endpoint' => '', // Minio endpoint e.g. http://0.0.0.0:9000
'access_key' => '', // Minio access
'secret_key' => '' // Minio secret key
];
+14 -7
View File
@@ -19,11 +19,11 @@ header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS");
/** Autoload */
require_once 'vendor/autoload.php';
/** Load all Interfaces */
foreach (glob(WD . '/interfaces/*.php') as $interface) {
foreach ( glob(WD . '/interfaces/*.php') as $interface ) {
require_once $interface;
}
/** Load all Traits */
foreach (glob(WD . '/traits/*.php') as $trait) {
foreach ( glob(WD . '/traits/*.php') as $trait ) {
require_once $trait;
}
@@ -36,6 +36,7 @@ require_once 'classes/db.php';
require_once 'classes/response.php';
require_once 'classes/request.php';
require_once 'classes/ratelimit.php';
require_once 'classes/wash_certificate_store.php';
/**
* Modules
@@ -47,9 +48,9 @@ require_once 'modules/economic/customers/economic_customer_mo.php';
require_once 'modules/economic/invoices/draft/economicInvoicesDrafts.php';
require_once 'modules/economic/invoices/draft/economic_invoice_draft_mo.php';
use classes\response;
use classes\request;
use classes\db;
use classes\request;
use classes\response;
use classes\router;
// Start the session
@@ -65,17 +66,17 @@ try {
$response->error($e->getMessage(), 500);
}
// Load all the traits
foreach (glob(WD . '/traits/*.php') as $trait) {
foreach ( glob(WD . '/traits/*.php') as $trait ) {
require_once $trait;
}
// Load all the routes
foreach (glob(WD . '/routes/*.php') as $route) {
foreach ( glob(WD . '/routes/*.php') as $route ) {
require_once $route;
}
/** Load all Objects */
foreach (glob(WD . '/objects/*.php') as $object) {
foreach ( glob(WD . '/objects/*.php') as $object ) {
try {
require_once $object;
} catch (Exception $e) {
@@ -83,6 +84,12 @@ foreach (glob(WD . '/objects/*.php') as $object) {
}
}
// If the program was called from the command line, run the cli script
if (php_sapi_name() === 'cli') {
require_once 'cli.php';
exit;
}
// Autoload all the routes
$router->auto_load_routes(WD . '/routes');
+13
View File
@@ -0,0 +1,13 @@
<?php
namespace interfaces;
interface minio_wash_certificates_i
{
/**
* Check if a wash certificate exists
* @param string $certificate_id
* @return bool
*/
public function washCertificateExists(string $certificate_id): bool;
}
+24 -9
View File
@@ -3,7 +3,15 @@
* This is the generator for the wash certificates (Used by the WordPress plugin)
* The reason behind this being outside the WordPress plugin is that the PHPExcel library is not compatible with the WordPress plugin, and it is easier to maintain the generator here.
*/
use classes\wash_certificate_store;
require_once '../../config.php';
require_once '../../vendor/autoload.php';
/** Load the relevant classes */
require_once '../../interfaces/minio_wash_certificates_i.php';
require_once '../../traits/minio_t.php';
require_once '../../classes/wash_certificate_store.php';
global $WORDPRESS_STATIC_TOKEN;
// Validate the token was loaded from the config file
if (!isset($WORDPRESS_STATIC_TOKEN) || $WORDPRESS_STATIC_TOKEN === '') {
@@ -60,13 +68,11 @@ if (!isset($_GET['sealOrPlumber']) || !isset($_GET['performedBy']) || !isset($_G
exit;
}
// Check if the certificate already exists in the folder
if (file_exists("output/certificates/wash_certificate_" . $_GET['bookingId'] . ".pdf")) {
// Return the generated certificate path
$generatedCertificatePath = "output/certificates/wash_certificate_" . $_GET['bookingId'] . ".pdf";
// Check if the certificate already exists in the bucket
$wash_certificate_store = new wash_certificate_store();
if ($wash_certificate_store->washCertificateExists("wash_certificate_" . $_GET['bookingId'] . ".pdf")) {
// Return the certificate url
echo $generatedCertificatePath;
echo $wash_certificate_store->getPresignedUrl("wash_certificate_" . $_GET['bookingId'] . ".pdf");
// Exit the script
exit;
}
@@ -74,12 +80,21 @@ if (file_exists("output/certificates/wash_certificate_" . $_GET['bookingId'] . "
// Usage example
$template = "templates/template2024julv3.xlsx";
$generator = new WashCertificateGenerator($template, $_GET['department']);
$generator->generateCertificate("output/certificates/wash_certificate_" . $_GET['bookingId'] . ".pdf", $_GET['sealOrPlumber'], $_GET['regNumber'], $_GET['regNumberTrailer'], $_GET['performedBy']);
$generator->generateCertificate(dirname(__FILE__) . "/output/certificates/wash_certificate_" . $_GET['bookingId'] . ".pdf", $_GET['sealOrPlumber'], $_GET['regNumber'], $_GET['regNumberTrailer'], $_GET['performedBy']);
// Determine the generated certificate name
$generatedCertificateName = "wash_certificate_" . $_GET['bookingId'] . ".pdf";
// Return the generated certificate path
$generatedCertificatePath = "output/certificates/wash_certificate_" . $_GET['bookingId'] . ".pdf";
// Return the generated certificate path
echo $generatedCertificatePath;
// Upload the certificate to the bucket
$wash_certificate_store->uploadFile($generatedCertificateName, dirname(__FILE__) . '/' . $generatedCertificatePath);
// Delete the local copy of the certificate
unlink(dirname(__FILE__) . '/' . $generatedCertificatePath);
// Return the generated certificate object URL
$wash_certificate_store->getObjectUrl($generatedCertificateName);
// Exit the script
exit;
+79
View File
@@ -0,0 +1,79 @@
<?php
// prevent direct access
if (!defined('WD')) {
exit;
}
global $MINIO;
use classes\wash_certificate_store;
function warn($message): void
{
echo "\n\033[33m$message\033[0m\n";
}
// Check if the MINIO array is set
if (!isset($MINIO)) {
throw new Exception('MINIO array is not set in config.php');
}
// Check if the MINIO array has the required keys
if (!isset($MINIO['endpoint']) || !isset($MINIO['access_key']) || !isset($MINIO['secret_key'])) {
throw new Exception('MINIO array is missing required keys');
}
// Create a new S3Client object
$wash_certificate_store = new wash_certificate_store();
// List all wash certificates
$wash_certificates = $wash_certificate_store->listFiles();
// Check if the request was successful
if (count($wash_certificates) > 0) {
// Return the count of wash certificates
echo "\nWash certificates found: " . count($wash_certificates);
} else {
// Show warning message
warn('No wash certificates found');
}
// Create a new wash certificate
$wash_certificate = $wash_certificate_store->createObject('test.txt', 'Hello, World!');
// Check if the request was successful
if ($wash_certificate) {
// Show success message
echo "\nWash certificate created successfully";
} else {
// Show error message
warn('Failed to create wash certificate');
}
// Check if a wash certificate exists
$wash_certificate_exists = $wash_certificate_store->washCertificateExists('test.txt');
// Check if the request was successful
if ($wash_certificate_exists) {
// Show success message
echo "\nWash certificate exists";
} else {
// Show warning message
warn('Wash certificate does not exist');
}
// Create a download link for the wash certificate
$wash_certificate_download_link = $wash_certificate_store->getPresignedUrl('test.txt');
// Check if the request was successful
if ($wash_certificate_download_link) {
// Show the download link
echo "\nDownload link: $wash_certificate_download_link";
} else {
// Show error message
warn('Failed to create download link');
}
// Upload a wash certificate from a file
$wash_certificate_upload = $wash_certificate_store->uploadFile('test.txt', 'tests/minio/testUpload.txt');
// Check if the request was successful
if ($wash_certificate_upload) {
// Show success message
echo "\nWash certificate uploaded successfully from file";
} else {
// Show error message
warn('Failed to upload wash certificate from file');
}
+1
View File
@@ -0,0 +1 @@
This is a test file.
+173
View File
@@ -0,0 +1,173 @@
<?php
namespace traits;
use Aws\S3\S3Client;
use classes\wash_certificate_store;
trait minio_t
{
/**
* The bucket to store the files in
* @return string
*/
private string $bucket;
/**
* The S3 client to interact with the Minio server
* @return S3Client
*/
private s3Client $s3Client;
/**
* List all the files in the bucket
* @return array
*/
public function listFiles(): array
{
$objects = self::getS3Client()->listObjects([
'Bucket' => self::getBucket()
]);
// If there are no files, return an empty array
if (!isset($objects['Contents'])) {
return [];
}
return $objects['Contents'];
}
/**
* Returns the S3 client to interact with the Minio server
* @return S3Client
*/
public function getS3Client(): S3Client
{
// If the S3 client is not set, create a new one
if (!isset($this->s3Client)) {
$this->connect();
}
return $this->s3Client;
}
/**
* Connect to the Minio server
*/
private function connect(): self
{
$this->s3Client = new S3Client([
'version' => 'latest',
'region' => 'us-east-1',
'endpoint' => $this->getEndpoint(),
'use_path_style_endpoint' => true,
'credentials' => [
'key' => $this->getAccessKey(),
'secret' => $this->getSecretKey(),
],
]);
return $this;
}
/**
* Returns the endpoint of the Minio server
* @return string
*/
public function getEndpoint(): string
{
global $MINIO;
return $MINIO['endpoint'];
}
/**
* Returns the access key of the Minio server
* @return string
*/
public function getAccessKey(): string
{
global $MINIO;
return $MINIO['access_key'];
}
/**
* Returns the secret key of the Minio server
* @return string
*/
public function getSecretKey(): string
{
global $MINIO;
return $MINIO['secret_key'];
}
/**
* Returns the bucket to store the files in
* @return string
*/
public function getBucket(): string
{
return $this->bucket;
}
/**
* Sets the bucket to store the files in
* @param string $bucket
* @return wash_certificate_store|minio_t
*/
public function setBucket(string $bucket): self
{
$this->bucket = $bucket;
return $this;
}
/**
* Create a new object in the bucket
* @param string $key The key of the object (file)
* @param string $body The content of the object (file)
* @return bool
*/
public function createObject(string $key, string $body): bool
{
$result = self::getS3Client()->putObject([
'Bucket' => self::getBucket(),
'Key' => $key,
'Body' => $body
]);
return $result['@metadata']['statusCode'] == 200;
}
/**
* Get the URL of the object in the bucket
* @param string $key The key of the object (file)
* @return string
*/
public function getObjectUrl(string $key): string
{
return self::getS3Client()->getObjectUrl(self::getBucket(), $key);
}
/**
* Generate a presigned URL for the object in the bucket (valid for 20 minutes)
* @param string $key The key of the object (file)
* @return string
*/
public function getPresignedUrl(string $key): string
{
$command = self::getS3Client()->getCommand('GetObject', [
'Bucket' => self::getBucket(),
'Key' => $key
]);
return (string)self::getS3Client()->createPresignedRequest($command, '+20 minutes')->getUri();
}
/**
* Upload a file to the bucket
* @param string $key The key of the object (file)
* @param string $file The path to the file to upload
* @return bool
*/
public function uploadFile(string $key, string $file): bool
{
$result = self::getS3Client()->putObject([
'Bucket' => self::getBucket(),
'Key' => $key,
'SourceFile' => $file
]);
return $result['@metadata']['statusCode'] == 200;
}
}