diff --git a/composer.json b/composer.json index f3814e8..57d81e0 100644 --- a/composer.json +++ b/composer.json @@ -31,6 +31,7 @@ "Steamy\\": "src/", "Steamy\\Core\\": "src/core/", "Steamy\\Controller\\": "src/controllers/", + "Steamy\\Controller\\API\\": "src/controllers/api/", "Steamy\\Model\\": "src/models/" } }, diff --git a/docs/API.md b/docs/API.md index bb9b4ba..d3a08ce 100644 --- a/docs/API.md +++ b/docs/API.md @@ -20,17 +20,16 @@ There are two types of endpoints: 1. **Public endpoints** : They return a public resource that can be accessed **without a token**. 2. **Protected endpoints** : They return a protected resource that can only be accessed **with a valid JWT token**. -### Authentication +### Session -| Endpoint | Description | Protected | -|---------------------|---------------------------------------------|-----------| -| `GET /api/v1/login` | Authenticates user and returns a JWT token. | No | +| Endpoint | Description | Protected | +|-------------------------|--------------------------------------------------|-----------| +| `POST /api/v1/sessions` | Authenticates admin and creates a session token. | No | Note: -- Only administrators can receive a JWT token. +- Only administrators can receive a session token. - Only administrators can access protected endpoints. -- The JWT token expires after 24 hours and a new one must be requested. ### User diff --git a/docs/INSTALLATION_GUIDE.md b/docs/INSTALLATION_GUIDE.md index 4631ff3..2e7ffec 100644 --- a/docs/INSTALLATION_GUIDE.md +++ b/docs/INSTALLATION_GUIDE.md @@ -116,3 +116,6 @@ In the root directory of the project, run: ```bash npm install ``` +## Autoload setup + +Whenever changes are made to the autoload settings in `composer.json`, you must run `composer dump-autoload`. \ No newline at end of file diff --git a/docs/USAGE_GUIDE.md b/docs/USAGE_GUIDE.md index 93b89e6..c778dbf 100644 --- a/docs/USAGE_GUIDE.md +++ b/docs/USAGE_GUIDE.md @@ -35,4 +35,4 @@ To export database with `mysqldump`: ```bash mysqldump -u root -p cafe > resources/database/dump/cafe.sql -``` +``` \ No newline at end of file diff --git a/public/robots.txt b/public/robots.txt index 14267e9..9cc4092 100644 --- a/public/robots.txt +++ b/public/robots.txt @@ -1,2 +1,3 @@ User-agent: * -Allow: / \ No newline at end of file +Allow: / +Disallow: /api/ \ No newline at end of file diff --git a/src/controllers/API.php b/src/controllers/API.php new file mode 100644 index 0000000..0ab4e29 --- /dev/null +++ b/src/controllers/API.php @@ -0,0 +1,54 @@ +resource = Utility::splitURL()[2] ?? ""; + } + + /** + * Checks if root relative url starts with /api/v1 + * @return bool + */ + private function validateURLFormat(): bool + { + return preg_match("/^api\/v1/", $_GET["url"]) > 0; + } + + public function index(): void + { + if (!$this->validateURLFormat()) { + echo "Invalid API URL: " . $_GET["url"]; + die(); + } + + // call appropriate controller to handle resource + $controllerClassName = 'Steamy\\Controller\\API\\' . ucfirst($this->resource); + + if (class_exists($controllerClassName)) { + (new $controllerClassName())->index(); + } else { + echo "Invalid API resource: " . $this->resource; + die(); + } + } +} diff --git a/src/controllers/api/Products.php b/src/controllers/api/Products.php new file mode 100644 index 0000000..1b1f78d --- /dev/null +++ b/src/controllers/api/Products.php @@ -0,0 +1,57 @@ +toArray(); + } + echo json_encode($result); + } + + private function addProduct(): void + { + } + + private function deleteProduct(): void + { + } + + private function updateProduct(): void + { + } + + + public function index(): void + { + switch ($_SERVER['REQUEST_METHOD']) { + case 'GET': + $this->getProducts(); + break; + case 'POST': + $this->addProduct(); + break; + case 'DELETE': + $this->deleteProduct(); + break; + case 'PUT': + $this->updateProduct(); + break; + default: + http_response_code(400); + die(); + } + } +} \ No newline at end of file diff --git a/src/controllers/api/Sessions.php b/src/controllers/api/Sessions.php new file mode 100644 index 0000000..48d559c --- /dev/null +++ b/src/controllers/api/Sessions.php @@ -0,0 +1,54 @@ +verifyPassword($password)) { + http_response_code(401); + die(); + } + + $_SESSION['admin_email'] = $email; + session_regenerate_id(); + } + + public function index(): void + { + switch ($_SERVER['REQUEST_METHOD']) { + case 'POST': + $this->handleLogin(); + break; + default: + http_response_code(400); + die(); + } + } +} \ No newline at end of file diff --git a/src/core/App.php b/src/core/App.php index 3f98c6a..9b11190 100644 --- a/src/core/App.php +++ b/src/core/App.php @@ -5,6 +5,7 @@ namespace Steamy\Core; use Steamy\Controller\_404; +use Steamy\Controller\API; class App { @@ -16,16 +17,20 @@ public function loadController(): void { $URL = Utility::splitURL(); - $controllerClassName = 'Steamy\\Controller\\' . ucfirst($URL[0]); - - - if (class_exists($controllerClassName)) { - $controller = new $controllerClassName(); - } else { - // Fallback to 404 controller - $controller = new _404(); + switch ($URL[0]) { + case 'api': + (new API())->index(); + break; + default: + $controllerClassName = 'Steamy\\Controller\\' . ucfirst($URL[0]); + + if (class_exists($controllerClassName)) { + // call appropriate controller + (new $controllerClassName())->index(); + } else { + // Fallback to 404 controller + (new _404())->index(); + } } - - $controller->index(); // display contents } } \ No newline at end of file diff --git a/src/core/Utility.php b/src/core/Utility.php index 3a28fc3..3125cca 100644 --- a/src/core/Utility.php +++ b/src/core/Utility.php @@ -4,15 +4,18 @@ namespace Steamy\Core; +/** + * Utility class containing various helper functions. + */ class Utility { /** - * Display any data in a formatted block. Use this function + * Displays data in a formatted block. Use this function * for debugging. - * @param $stuff + * @param $stuff mixed some data * @return void */ - public static function show($stuff): void + public static function show(mixed $stuff): void { echo "
"; print_r($stuff); @@ -25,7 +28,7 @@ public static function show($stuff): void * This function retrieves the 'url' parameter from the $_GET array or defaults to 'home', * trims leading and trailing slashes, and then explodes the URL into an array of segments. * - * @return array An array containing the URL segments. + * @return string[] An array containing the URL segments where each segment is in lowercase. */ public static function splitURL(): array { @@ -35,20 +38,19 @@ public static function splitURL(): array /** @noinspection PhpNoReturnAttributeCanBeAddedInspection */ /** - * Redirects website to a page. - * @param $path string relative URL of page + * Redirects user to a page and ends execution of script. + * - `redirect('home')` redirects to `ROOT`. + * - `redirect('shop/products/1')` redirects to `ROOT/shop/products/1`. + * + * @param $relative_url string root-relative URL of page. It must not start with /. * @return void */ - public static function redirect(string $path): void + public static function redirect(string $relative_url): void { - header("Location: " . ROOT . "/" . $path); + header("Location: " . ROOT . "/" . $relative_url); die(); } - - - //Function fuzzySearch will be used to search for products on the shop page. - /** * Perform fuzzy search on an array of strings. * @@ -56,7 +58,7 @@ public static function redirect(string $path): void * an array of strings from the input array that closely match the search term. * It uses the Levenshtein distance algorithm to determine the similarity between * the search term and each string in the array. - * + * * @param string $searchTerm The term to search for. * @param array $strings The array of strings to search within. * @param int $threshold The maximum allowed Levenshtein distance. @@ -75,13 +77,13 @@ public static function fuzzySearch(string $searchTerm, array $strings, int $thre } - /** + /** * Calculates the Levenshtein distance between two strings. * * The Levenshtein distance is a metric to measure the difference between two strings. * It is the minimum number of single-character edits (insertions, deletions, or replaces) * required to change one word into the other. - * + * * @param string $str1 The first string. * @param string $str2 The second string. * @return int The Levenshtein distance between the two strings. @@ -99,7 +101,7 @@ public static function levenshteinDistance(string $str1, string $str2): int $dp[$i][$j] = 0; } } - + // Fill the first row and column of the array for ($i = 0; $i <= $m; $i++) { $dp[$i][0] = $i; @@ -123,5 +125,4 @@ public static function levenshteinDistance(string $str1, string $str2): int // Return the final result, which is the distance between the two strings return $dp[$m][$n]; } - } diff --git a/src/models/Administrator.php b/src/models/Administrator.php index e68da2f..ddfafe7 100644 --- a/src/models/Administrator.php +++ b/src/models/Administrator.php @@ -77,9 +77,6 @@ public function save(): void $inserted_record = self::first($user_data, 'user'); -// utility::show("inserted record: "); -// Utility::show($inserted_record); - if (!$inserted_record) { return; } @@ -95,6 +92,48 @@ public function save(): void $this->insert($admin_data, $this->table); } + + /** + * Returns the Administrator object corresponding to the given email. + * + * @param string $email The email of the administrator. + * @return ?Administrator The Client object if found, otherwise null. + */ + public static function getByEmail(string $email): ?Administrator + { + $query = <<$email]); + + // Check if the result is empty + if (!$result) { + return null; + } + + // Create a new Administrator object + $administrator = new Administrator( + email: $email, + first_name: $result->first_name, + last_name: $result->last_name, + plain_password: "dummy", + phone_no: $result->phone_no, + job_title: $result->job_title, + is_super_admin: filter_var($result->is_super_admin, FILTER_SANITIZE_NUMBER_INT) + ); + + // Set the user ID and password hash + $administrator->user_id = $result->user_id; + $administrator->password = $result->password; + + return $administrator; + } + public function getJobTitle(): string { return $this->job_title; diff --git a/src/models/Product.php b/src/models/Product.php index 11461ae..fb16124 100644 --- a/src/models/Product.php +++ b/src/models/Product.php @@ -103,7 +103,7 @@ public function toArray(): array } /** - * @return array An array of Product objects + * @return Product[] An array of Product objects */ public static function getAll(): array {