From 3b211ff552af542b3a53d50694b06666a1682d03 Mon Sep 17 00:00:00 2001 From: creme332 <65414576+creme332@users.noreply.github.com> Date: Thu, 4 Apr 2024 13:32:30 +0400 Subject: [PATCH 01/14] create basic api controller --- src/controllers/Api.php | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 src/controllers/Api.php diff --git a/src/controllers/Api.php b/src/controllers/Api.php new file mode 100644 index 0000000..a66a6bd --- /dev/null +++ b/src/controllers/Api.php @@ -0,0 +1,38 @@ + 0; + } + + public function __construct() + { + header("Content-Type:application/json"); + } + + public function index(): void + { + if (!$this->validateURLFormat()) { + echo "Invalid API URL" . $_GET["url"]; + return; + } + echo json_encode("hello world"); + } +} From 30333d5ffcc4b9f3f2d7a006cf1cf083de7aea51 Mon Sep 17 00:00:00 2001 From: creme332 <65414576+creme332@users.noreply.github.com> Date: Tue, 9 Apr 2024 11:25:05 +0400 Subject: [PATCH 02/14] make api inaccessible to crawlers --- public/robots.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 From 9cb5497e1714f03079ea84d15b1bbc0a2b8809c7 Mon Sep 17 00:00:00 2001 From: creme332 <65414576+creme332@users.noreply.github.com> Date: Fri, 19 Apr 2024 12:23:08 +0400 Subject: [PATCH 03/14] refactor loadController, handle api route --- src/core/App.php | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) 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 From f9b64b5c06125149feef380cfc7664dc65bcf900 Mon Sep 17 00:00:00 2001 From: creme332 <65414576+creme332@users.noreply.github.com> Date: Fri, 19 Apr 2024 12:23:31 +0400 Subject: [PATCH 04/14] create a namespace for api --- composer.json | 1 + 1 file changed, 1 insertion(+) 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/" } }, From 68598ba0cadee97bff506dfc630b3592466d0717 Mon Sep 17 00:00:00 2001 From: creme332 <65414576+creme332@users.noreply.github.com> Date: Fri, 19 Apr 2024 13:11:29 +0400 Subject: [PATCH 05/14] create api router --- src/controllers/API.php | 54 +++++++++++++++++++++++++++++++++++++++++ src/controllers/Api.php | 38 ----------------------------- 2 files changed, 54 insertions(+), 38 deletions(-) create mode 100644 src/controllers/API.php delete mode 100644 src/controllers/Api.php 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.php b/src/controllers/Api.php deleted file mode 100644 index a66a6bd..0000000 --- a/src/controllers/Api.php +++ /dev/null @@ -1,38 +0,0 @@ - 0; - } - - public function __construct() - { - header("Content-Type:application/json"); - } - - public function index(): void - { - if (!$this->validateURLFormat()) { - echo "Invalid API URL" . $_GET["url"]; - return; - } - echo json_encode("hello world"); - } -} From b900483cc881ccb7fdbb56bafbcd6bf5b49938bd Mon Sep 17 00:00:00 2001 From: creme332 <65414576+creme332@users.noreply.github.com> Date: Fri, 19 Apr 2024 13:12:15 +0400 Subject: [PATCH 06/14] create api controller for products resource --- src/controllers/api/Products.php | 34 ++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 src/controllers/api/Products.php diff --git a/src/controllers/api/Products.php b/src/controllers/api/Products.php new file mode 100644 index 0000000..8ed792c --- /dev/null +++ b/src/controllers/api/Products.php @@ -0,0 +1,34 @@ +toArray(); + } + echo json_encode($result); + } + + public function index(): void + { + switch ($_SERVER['REQUEST_METHOD']) { + case 'GET': + $this->getProducts(); + break; + default: + echo json_encode("Error"); + } + } +} \ No newline at end of file From 1d76091e367a02e61adb7bf32f18af29ac0caef0 Mon Sep 17 00:00:00 2001 From: creme332 <65414576+creme332@users.noreply.github.com> Date: Fri, 19 Apr 2024 13:12:40 +0400 Subject: [PATCH 07/14] update return type of getAll in phpdoc --- src/models/Product.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/models/Product.php b/src/models/Product.php index 9d1eaf8..330b4ef 100644 --- a/src/models/Product.php +++ b/src/models/Product.php @@ -99,7 +99,7 @@ public function toArray(): array } /** - * @return array An array of Product objects + * @return Product[] An array of Product objects */ public static function getAll(): array { From 4749e5642e156ca6ba6bc725fb8037d77b411b96 Mon Sep 17 00:00:00 2001 From: creme332 <65414576+creme332@users.noreply.github.com> Date: Fri, 19 Apr 2024 13:13:10 +0400 Subject: [PATCH 08/14] add note on autoloading --- docs/USAGE_GUIDE.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/USAGE_GUIDE.md b/docs/USAGE_GUIDE.md index 7acf738..f367846 100644 --- a/docs/USAGE_GUIDE.md +++ b/docs/USAGE_GUIDE.md @@ -36,3 +36,7 @@ To export database with `mysqldump`: ```bash mysqldump -u root -p cafe > cafe.sql ``` + +## Update autoload files + +Whenever changes are made to the autoload settings in `composer.json`, you must run `composer dump-autoload`. From c6a2c5bb91e568657efc305290224452c7344b55 Mon Sep 17 00:00:00 2001 From: creme332 <65414576+creme332@users.noreply.github.com> Date: Fri, 19 Apr 2024 13:24:48 +0400 Subject: [PATCH 09/14] improve phpdoc --- src/core/Utility.php | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) 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];
     }
-
 }

From a0a3d1a3f93a9509ab5591253607b54daa69fd93 Mon Sep 17 00:00:00 2001
From: creme332 <65414576+creme332@users.noreply.github.com>
Date: Fri, 19 Apr 2024 13:29:40 +0400
Subject: [PATCH 10/14] move autoload setup to installation guide

---
 docs/INSTALLATION_GUIDE.md | 3 +++
 docs/USAGE_GUIDE.md        | 6 +-----
 2 files changed, 4 insertions(+), 5 deletions(-)

diff --git a/docs/INSTALLATION_GUIDE.md b/docs/INSTALLATION_GUIDE.md
index cb9f60f..fb5ed64 100644
--- a/docs/INSTALLATION_GUIDE.md
+++ b/docs/INSTALLATION_GUIDE.md
@@ -110,3 +110,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 f367846..98dc589 100644
--- a/docs/USAGE_GUIDE.md
+++ b/docs/USAGE_GUIDE.md
@@ -35,8 +35,4 @@ To export database with `mysqldump`:
 
 ```bash
 mysqldump -u root -p cafe > cafe.sql
-```
-
-## Update autoload files
-
-Whenever changes are made to the autoload settings in `composer.json`, you must run `composer dump-autoload`.
+```
\ No newline at end of file

From 6262b54cce59a422a9847406eb085b9c51c9f08b Mon Sep 17 00:00:00 2001
From: creme332 <65414576+creme332@users.noreply.github.com>
Date: Sat, 20 Apr 2024 10:57:45 +0400
Subject: [PATCH 11/14] replace login endpoint with session endpoint

---
 docs/API.md | 11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)

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
 

From 0bf753c2b3de80465f4e8b092e2228928683f90e Mon Sep 17 00:00:00 2001
From: creme332 <65414576+creme332@users.noreply.github.com>
Date: Sat, 20 Apr 2024 10:58:03 +0400
Subject: [PATCH 12/14] add getByEmail

---
 src/models/Administrator.php | 45 +++++++++++++++++++++++++++++++++---
 1 file changed, 42 insertions(+), 3 deletions(-)

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;

From b2f15db5c2d5d34381c0b34f76b23e60ea4aab75 Mon Sep 17 00:00:00 2001
From: creme332 <65414576+creme332@users.noreply.github.com>
Date: Sat, 20 Apr 2024 10:59:22 +0400
Subject: [PATCH 13/14] create an api controller for sessions endpoint

---
 src/controllers/api/Sessions.php | 54 ++++++++++++++++++++++++++++++++
 1 file changed, 54 insertions(+)
 create mode 100644 src/controllers/api/Sessions.php

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

From 14288d67bdd18d60f46137a6bb7e21b1ade2c9c1 Mon Sep 17 00:00:00 2001
From: creme332 <65414576+creme332@users.noreply.github.com>
Date: Sat, 20 Apr 2024 11:02:08 +0400
Subject: [PATCH 14/14] handle error better, add incomplete functions for all
 valid routes

---
 src/controllers/api/Products.php | 25 ++++++++++++++++++++++++-
 1 file changed, 24 insertions(+), 1 deletion(-)

diff --git a/src/controllers/api/Products.php b/src/controllers/api/Products.php
index 8ed792c..1b1f78d 100644
--- a/src/controllers/api/Products.php
+++ b/src/controllers/api/Products.php
@@ -21,14 +21,37 @@ private function getProducts(): void
         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:
-                echo json_encode("Error");
+                http_response_code(400);
+                die();
         }
     }
 }
\ No newline at end of file