ljoaquim hai 3 meses
achega
1d041e6cfa

+ 20 - 0
.env.example

@@ -0,0 +1,20 @@
+APP_PORT=8080
+DB_FILE=test.db
+JWT_SECRET=Aer8woa9zeec2gai4ahQuah3Ahbee5eiSefae8pheepahnootuShoo0oKahf
+
+
+#================= BUILD =================
+BUILD_APP="php-api"
+BUILD_VER="1.0.0"
+BUILD_STAGE="build/php-api"
+BUILD_DESCRIPTION="API PHP php-api (SmartPay)"
+BUILD_LICENSE="Proprietary"
+BUILD_VENDOR="SmartPay"
+BUILD_MAINTAINER="lucas.joaquim@smartpay.com.vc"
+BUILD_DEPENDS="php8.2-cli | php-cli (>= 8.2)"
+BUILD_ARCH="all"
+
+#================= DEPLOY =================
+DEPLOY_HOST="pixplay.com.vc"
+DEPLOY_TOKEN=""
+DEPLOY_FILE="./php-api_1.0.0_all.deb"

+ 5 - 0
.gitignore

@@ -0,0 +1,5 @@
+vendor/
+.env
+*.log
+test.db
+build/

+ 16 - 0
README.md

@@ -0,0 +1,16 @@
+```
+fpm -s dir -t deb \
+  -n php-api -v 1.0.0 \
+  -C build/php-api \
+  --prefix / \
+  --description "API PHP php-api (SmartPay)" \
+  --license "Proprietary" \
+  --vendor "SmartPay" \
+  --maintainer "lucas.joaquim@smartpay.com.vc" \
+  --architecture all \
+  --deb-no-default-config-files \
+  --config-files /etc/php-api \
+  --depends "php8.2-cli | php-cli (>= 8.2)" \
+  opt/php-api etc/php-api var/log/php-api
+Created package {:path=>"php-api_1.0.0_all.deb"}
+```

+ 53 - 0
bin/build_stage

@@ -0,0 +1,53 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+[ -f ./.env ] || { echo "Error: .env file not found"; exit 1; }
+source ./.env || { echo "Error: failed to load .env"; exit 1; }
+
+command -v fpm >/dev/null 2>&1 || { echo "Error: fpm not found in PATH"; exit 1; }
+
+rm -rf ./build
+rm -f "${BUILD_APP}_${BUILD_VER}_${BUILD_ARCH}.deb"
+
+rm -rf "$BUILD_STAGE"
+mkdir -p "$BUILD_STAGE"/{opt/$BUILD_APP,usr/bin,var/log/$BUILD_APP}
+
+for item in *; do
+  [ "$item" = "build" ] && continue
+  case "$item" in
+    .*|*.deb) continue ;;
+  esac
+  cp -a "$item" "$BUILD_STAGE/opt/$BUILD_APP/" || { echo "Error: failed to copy '$item' to stage"; exit 1; }
+done
+
+touch "$BUILD_STAGE/var/log/$BUILD_APP/app.log" || { echo "Error: unable to create log in $BUILD_STAGE/var/log/$BUILD_APP"; exit 1; }
+cat > "$BUILD_STAGE/usr/bin/$BUILD_APP" <<EOF
+#!/usr/bin/env bash
+exec php -S 127.0.0.1:8080 -t /opt/$BUILD_APP/public
+EOF
+chmod +x "$BUILD_STAGE/usr/bin/$BUILD_APP" || { echo "Error: unable to mark wrapper as executable"; exit 1; }
+
+echo "[ok] stage ready at: $BUILD_STAGE"
+
+FPM_DEPENDS=()
+if [ -n "${BUILD_DEPENDS:-}" ]; then
+  IFS=',' read -ra __DEPS <<< "$BUILD_DEPENDS"
+  for d in "${__DEPS[@]}"; do
+    d_trim="$(echo "$d" | xargs)"
+    [ -n "$d_trim" ] && FPM_DEPENDS+=(--depends "$d_trim")
+  done
+fi
+
+fpm -s dir -t deb \
+  -n "$BUILD_APP" -v "$BUILD_VER" \
+  -C "$BUILD_STAGE" \
+  --prefix / \
+  --description "$BUILD_DESCRIPTION" \
+  --license "$BUILD_LICENSE" \
+  --vendor "$BUILD_VENDOR" \
+  --maintainer "$BUILD_MAINTAINER" \
+  --architecture "$BUILD_ARCH" \
+  --deb-no-default-config-files \
+  "${FPM_DEPENDS[@]}" \
+  opt/$BUILD_APP var/log/$BUILD_APP usr/bin/$BUILD_APP \
+  || { echo "Error: fpm failed to build the package"; exit 1; }

+ 28 - 0
bin/deploy

@@ -0,0 +1,28 @@
+#!/bin/bash
+
+source .env
+REPO=$1
+
+ENDPOINT="https://$DEPLOY_HOST/api/$REPO/upload/"
+
+
+if [ ! -f "$DEPLOY_FILE" ]; then
+    echo "Error: file $DEPLOY_FILE not found"
+    exit 1
+fi
+
+RESPONSE=$(curl -s -X POST \
+     -H "Authorization: Token $DEPLOY_TOKEN" \
+     -F "package_file=@$DEPLOY_FILE" \
+     -w "%{http_code}" \
+     "$ENDPOINT")
+
+HTTP_CODE="${RESPONSE: -3}"
+
+if [ "$HTTP_CODE" -eq 200 ] || [ "$HTTP_CODE" -eq 201 ]; then
+    echo "Upload done to $REPO."
+else
+    echo "Error: failed to uploado to '$REPO'"
+    echo "$RESPONSE"
+    exit 1
+fi

+ 71 - 0
bin/setup

@@ -0,0 +1,71 @@
+#!/bin/bash
+
+# Diretório do projeto (um nível acima deste script)
+PROJECT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
+
+# Caminho absoluto do arquivo do banco de dados dentro do projeto
+DB_FILE="$PROJECT_DIR/test.db"
+
+# Executa comandos SQL no SQLite
+sqlite3 "$DB_FILE" <<EOF
+-- Cria tabela 'user' se não existir, com coluna 'user_password'
+CREATE TABLE IF NOT EXISTS user (
+    user_id INTEGER PRIMARY KEY AUTOINCREMENT,
+    user_name TEXT NOT NULL,
+    user_flag TEXT NOT NULL,
+    user_password TEXT NOT NULL  -- Nova coluna para senha hasheada
+);
+
+-- Cria tabela 'api_key' se não existir
+CREATE TABLE IF NOT EXISTS api_key (
+    api_key_id INTEGER PRIMARY KEY AUTOINCREMENT,
+    user_id INTEGER NOT NULL,
+    api_key_user TEXT NOT NULL,
+    api_key_secret TEXT NOT NULL,
+    FOREIGN KEY (user_id) REFERENCES user(user_id)
+);
+
+-- Cria tabela 'teste' se não existir
+CREATE TABLE IF NOT EXISTS teste (
+    teste_id INTEGER PRIMARY KEY AUTOINCREMENT,
+    field1 TEXT NOT NULL,
+    field2 TEXT NULL,
+    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
+);
+
+-- Cria tabela 'cpr_preregistration' se não existir
+CREATE TABLE IF NOT EXISTS cpr_preregistration (
+    cpr_id INTEGER PRIMARY KEY AUTOINCREMENT,
+    name TEXT NOT NULL,
+    contact_number TEXT NOT NULL,
+    email TEXT NOT NULL,
+    product_type TEXT NOT NULL,
+    internal_control_number TEXT NOT NULL,
+    product_quantity INTEGER NOT NULL,
+    property_name TEXT NOT NULL,
+    property_location TEXT NOT NULL,
+    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
+);
+
+-- Insere usuário de exemplo ('admin') com senha hasheada se não existir
+-- Hash de 'pass' (gere com user_password_hash em PHP e substitua)
+INSERT OR IGNORE INTO user (user_name, user_flag, user_password) VALUES ('admin', 'a', '$2y$10$K.0XhB3kXjZfZfZfZfZfZfZfZfZfZfZfZfZfZfZfZfZfZfZ');
+
+-- Insere chave API para o usuário 'admin' se não existir
+INSERT OR IGNORE INTO api_key (user_id, api_key_user, api_key_secret)
+SELECT user_id, 'myapikey', 'myapisecret' FROM user WHERE user_name = 'admin';
+
+-- Opcional: Insere mais um usuário de teste com senha hasheada
+-- Hash de 'testpass' (substitua pelo real)
+INSERT OR IGNORE INTO user (user_name, user_flag, user_password) VALUES ('testuser', 'a', '$2y$10$AnotherHashHereForTestPass');
+INSERT OR IGNORE INTO api_key (user_id, api_key_user, api_key_secret)
+SELECT user_id, 'testapikey', 'testapisecret' FROM user WHERE user_name = 'testuser';
+
+-- Exibe os dados inseridos para verificação (sem mostrar hash real por segurança)
+SELECT user_id, user_name, user_flag FROM user;
+SELECT * FROM api_key;
+SELECT * FROM teste;
+SELECT * FROM cpr_preregistration;
+EOF
+
+echo "Banco de dados '$DB_FILE' criado e populado com sucesso! Senhas estão hasheadas."

+ 25 - 0
bin/testgetjwt

@@ -0,0 +1,25 @@
+#!/bin/bash
+
+# Script 1: Obtém JWT via login
+# Configurações
+API_URL="http://localhost:8000/login"  # Rota de login
+USERNAME="du"
+PASSWORD="du123456"  # Substitua pela senha real
+
+# Body JSON para login
+BODY="{\"username\":\"${USERNAME}\",\"password\":\"${PASSWORD}\"}"
+
+# Faz POST para login e extrai o token JWT usando jq (instale jq se necessário: sudo apt install jq)
+JWT=$(curl -s -X POST "${API_URL}" \
+    -H "Content-Type: application/json" \
+    -d "${BODY}" | jq -r '.data.token')
+
+# Verifica se JWT foi obtido
+if [ -z "$JWT" ] || [ "$JWT" = "null" ]; then
+    echo "Erro ao obter JWT. Resposta do servidor:"
+    curl -v -X POST "${API_URL}" -H "Content-Type: application/json" -d "${BODY}"
+    exit 1
+fi
+
+echo "JWT obtido: ${JWT}"
+echo "Salve este JWT para usar no próximo script (ex: export JWT=${JWT})"

+ 33 - 0
bin/testhmac

@@ -0,0 +1,33 @@
+#!/bin/bash
+
+# Configurações da API e dados do usuário (do test.db)
+API_URL="http://localhost:8000/hmachelloworld"  # Ajuste a porta se necessário (ex: 8080)
+API_USER="admin"
+API_KEY="myapikey"
+API_SECRET="myapisecret"
+
+# Gera nonce (timestamp atual em segundos)
+NONCE=$(date +%s)
+
+# Para GET, body é vazio, então message = "::${NONCE}"
+MESSAGE="::${NONCE}"
+
+# Secret = API_KEY::API_SECRET
+SECRET="${API_KEY}::${API_SECRET}"
+
+# Calcula a assinatura HMAC-SHA256 (usando openssl)
+SIGNATURE=$(echo -n "${MESSAGE}" | openssl dgst -sha256 -hmac "${SECRET}" | awk '{print $2}')
+
+# Verifica se a assinatura foi gerada corretamente
+if [ -z "$SIGNATURE" ]; then
+    echo "Erro ao gerar assinatura HMAC."
+    exit 1
+fi
+
+# Envia a requisição curl com headers
+echo "Testando rota / com HMAC:"
+curl -v \
+    -H "x-user: ${API_USER}" \
+    -H "x-nonce: ${NONCE}" \
+    -H "x-signature: ${SIGNATURE}" \
+    "${API_URL}"

+ 18 - 0
bin/testusejwt

@@ -0,0 +1,18 @@
+#!/bin/bash
+
+# Script 2: Envia JWT para rota autenticada /jwthelloworld
+# Configurações
+API_URL="http://localhost:8000/jwthelloworld"  # Rota autenticada por JWT
+JWT="${JWT:-seu-jwt-aqui}"  # Use export JWT=... do script anterior ou cole aqui
+
+# Verifica se JWT está definido
+if [ -z "$JWT" ] || [ "$JWT" = "seu-jwt-aqui" ]; then
+    echo "Defina o JWT (ex: export JWT=seu-token) antes de rodar."
+    exit 1
+fi
+
+# Envia GET (ou ajuste para POST se necessário) com Authorization Bearer
+echo "Testando rota /jwthelloworld com JWT:"
+curl -v \
+    -H "Authorization: Bearer ${JWT}" \
+    "${API_URL}"

+ 26 - 0
composer.json

@@ -0,0 +1,26 @@
+{
+    "name": "lucas/bartender",
+    "autoload": {
+        "psr-4": {
+            "Lucas\\Bartender\\": "src/",
+            "Controllers\\": "controllers/",
+            "Middlewares\\": "middlewares/",
+            "Models\\": "models/",
+            "Services\\": "services/",
+            "Utils\\": "utils/",
+            "Libs\\": "libs/"
+        }
+    },
+    "authors": [
+        {
+            "name": "ljoaquim",
+            "email": "lucas.joaquim@smartpay.com.vc"
+        }
+    ],
+    "require": {
+        "clue/framework-x": "^0.17.0",
+        "vlucas/phpdotenv": "^5.6",
+        "firebase/php-jwt": "^6.11",
+        "respect/validation": "^2.4"
+    }
+}

+ 1559 - 0
composer.lock

@@ -0,0 +1,1559 @@
+{
+    "_readme": [
+        "This file locks the dependencies of your project to a known state",
+        "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
+        "This file is @generated automatically"
+    ],
+    "content-hash": "c4f488cdc234020e7137afdb7ac742d8",
+    "packages": [
+        {
+            "name": "clue/framework-x",
+            "version": "v0.17.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/clue/framework-x.git",
+                "reference": "9b3a8e6a981953bb0d6e4a4c3c219589a70521d0"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/clue/framework-x/zipball/9b3a8e6a981953bb0d6e4a4c3c219589a70521d0",
+                "reference": "9b3a8e6a981953bb0d6e4a4c3c219589a70521d0",
+                "shasum": ""
+            },
+            "require": {
+                "nikic/fast-route": "^1.3",
+                "php": ">=7.1",
+                "react/async": "^4.3 || ^3.1",
+                "react/http": "^1.11",
+                "react/promise": "^3.2",
+                "react/socket": "^1.15"
+            },
+            "require-dev": {
+                "phpstan/phpstan": "1.12.11 || 1.4.10",
+                "phpunit/phpunit": "^9.6 || ^7.5",
+                "psr/container": "^2 || ^1"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "FrameworkX\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Christian Lück",
+                    "email": "christian@clue.engineering"
+                }
+            ],
+            "description": "Framework X – the simple and fast micro framework for building reactive web applications that run anywhere.",
+            "homepage": "https://framework-x.org/",
+            "keywords": [
+                "async",
+                "event-driven",
+                "framework",
+                "http",
+                "micro",
+                "microframework",
+                "reactphp",
+                "web"
+            ],
+            "support": {
+                "issues": "https://github.com/clue/framework-x/issues",
+                "source": "https://github.com/clue/framework-x/tree/v0.17.0"
+            },
+            "funding": [
+                {
+                    "url": "https://clue.engineering/support",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/clue",
+                    "type": "github"
+                }
+            ],
+            "time": "2024-12-23T13:59:55+00:00"
+        },
+        {
+            "name": "evenement/evenement",
+            "version": "v3.0.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/igorw/evenement.git",
+                "reference": "0a16b0d71ab13284339abb99d9d2bd813640efbc"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/igorw/evenement/zipball/0a16b0d71ab13284339abb99d9d2bd813640efbc",
+                "reference": "0a16b0d71ab13284339abb99d9d2bd813640efbc",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9 || ^6"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Evenement\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Igor Wiedler",
+                    "email": "igor@wiedler.ch"
+                }
+            ],
+            "description": "Événement is a very simple event dispatching library for PHP",
+            "keywords": [
+                "event-dispatcher",
+                "event-emitter"
+            ],
+            "support": {
+                "issues": "https://github.com/igorw/evenement/issues",
+                "source": "https://github.com/igorw/evenement/tree/v3.0.2"
+            },
+            "time": "2023-08-08T05:53:35+00:00"
+        },
+        {
+            "name": "fig/http-message-util",
+            "version": "1.1.5",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/http-message-util.git",
+                "reference": "9d94dc0154230ac39e5bf89398b324a86f63f765"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/http-message-util/zipball/9d94dc0154230ac39e5bf89398b324a86f63f765",
+                "reference": "9d94dc0154230ac39e5bf89398b324a86f63f765",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.3 || ^7.0 || ^8.0"
+            },
+            "suggest": {
+                "psr/http-message": "The package containing the PSR-7 interfaces"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.1.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Fig\\Http\\Message\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "https://www.php-fig.org/"
+                }
+            ],
+            "description": "Utility classes and constants for use with PSR-7 (psr/http-message)",
+            "keywords": [
+                "http",
+                "http-message",
+                "psr",
+                "psr-7",
+                "request",
+                "response"
+            ],
+            "support": {
+                "issues": "https://github.com/php-fig/http-message-util/issues",
+                "source": "https://github.com/php-fig/http-message-util/tree/1.1.5"
+            },
+            "time": "2020-11-24T22:02:12+00:00"
+        },
+        {
+            "name": "firebase/php-jwt",
+            "version": "v6.11.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/firebase/php-jwt.git",
+                "reference": "d1e91ecf8c598d073d0995afa8cd5c75c6e19e66"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/firebase/php-jwt/zipball/d1e91ecf8c598d073d0995afa8cd5c75c6e19e66",
+                "reference": "d1e91ecf8c598d073d0995afa8cd5c75c6e19e66",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^8.0"
+            },
+            "require-dev": {
+                "guzzlehttp/guzzle": "^7.4",
+                "phpspec/prophecy-phpunit": "^2.0",
+                "phpunit/phpunit": "^9.5",
+                "psr/cache": "^2.0||^3.0",
+                "psr/http-client": "^1.0",
+                "psr/http-factory": "^1.0"
+            },
+            "suggest": {
+                "ext-sodium": "Support EdDSA (Ed25519) signatures",
+                "paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Firebase\\JWT\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Neuman Vong",
+                    "email": "neuman+pear@twilio.com",
+                    "role": "Developer"
+                },
+                {
+                    "name": "Anant Narayanan",
+                    "email": "anant@php.net",
+                    "role": "Developer"
+                }
+            ],
+            "description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.",
+            "homepage": "https://github.com/firebase/php-jwt",
+            "keywords": [
+                "jwt",
+                "php"
+            ],
+            "support": {
+                "issues": "https://github.com/firebase/php-jwt/issues",
+                "source": "https://github.com/firebase/php-jwt/tree/v6.11.1"
+            },
+            "time": "2025-04-09T20:32:01+00:00"
+        },
+        {
+            "name": "graham-campbell/result-type",
+            "version": "v1.1.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/GrahamCampbell/Result-Type.git",
+                "reference": "3ba905c11371512af9d9bdd27d99b782216b6945"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/3ba905c11371512af9d9bdd27d99b782216b6945",
+                "reference": "3ba905c11371512af9d9bdd27d99b782216b6945",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.2.5 || ^8.0",
+                "phpoption/phpoption": "^1.9.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "GrahamCampbell\\ResultType\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Graham Campbell",
+                    "email": "hello@gjcampbell.co.uk",
+                    "homepage": "https://github.com/GrahamCampbell"
+                }
+            ],
+            "description": "An Implementation Of The Result Type",
+            "keywords": [
+                "Graham Campbell",
+                "GrahamCampbell",
+                "Result Type",
+                "Result-Type",
+                "result"
+            ],
+            "support": {
+                "issues": "https://github.com/GrahamCampbell/Result-Type/issues",
+                "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.3"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/GrahamCampbell",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/graham-campbell/result-type",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2024-07-20T21:45:45+00:00"
+        },
+        {
+            "name": "nikic/fast-route",
+            "version": "v1.3.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/nikic/FastRoute.git",
+                "reference": "181d480e08d9476e61381e04a71b34dc0432e812"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/nikic/FastRoute/zipball/181d480e08d9476e61381e04a71b34dc0432e812",
+                "reference": "181d480e08d9476e61381e04a71b34dc0432e812",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.4.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^4.8.35|~5.7"
+            },
+            "type": "library",
+            "autoload": {
+                "files": [
+                    "src/functions.php"
+                ],
+                "psr-4": {
+                    "FastRoute\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Nikita Popov",
+                    "email": "nikic@php.net"
+                }
+            ],
+            "description": "Fast request router for PHP",
+            "keywords": [
+                "router",
+                "routing"
+            ],
+            "support": {
+                "issues": "https://github.com/nikic/FastRoute/issues",
+                "source": "https://github.com/nikic/FastRoute/tree/master"
+            },
+            "time": "2018-02-13T20:26:39+00:00"
+        },
+        {
+            "name": "phpoption/phpoption",
+            "version": "1.9.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/schmittjoh/php-option.git",
+                "reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/e3fac8b24f56113f7cb96af14958c0dd16330f54",
+                "reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.2.5 || ^8.0"
+            },
+            "require-dev": {
+                "bamarni/composer-bin-plugin": "^1.8.2",
+                "phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28"
+            },
+            "type": "library",
+            "extra": {
+                "bamarni-bin": {
+                    "bin-links": true,
+                    "forward-command": false
+                },
+                "branch-alias": {
+                    "dev-master": "1.9-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "PhpOption\\": "src/PhpOption/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "Apache-2.0"
+            ],
+            "authors": [
+                {
+                    "name": "Johannes M. Schmitt",
+                    "email": "schmittjoh@gmail.com",
+                    "homepage": "https://github.com/schmittjoh"
+                },
+                {
+                    "name": "Graham Campbell",
+                    "email": "hello@gjcampbell.co.uk",
+                    "homepage": "https://github.com/GrahamCampbell"
+                }
+            ],
+            "description": "Option Type for PHP",
+            "keywords": [
+                "language",
+                "option",
+                "php",
+                "type"
+            ],
+            "support": {
+                "issues": "https://github.com/schmittjoh/php-option/issues",
+                "source": "https://github.com/schmittjoh/php-option/tree/1.9.3"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/GrahamCampbell",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2024-07-20T21:41:07+00:00"
+        },
+        {
+            "name": "psr/http-message",
+            "version": "1.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/http-message.git",
+                "reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/http-message/zipball/cb6ce4845ce34a8ad9e68117c10ee90a29919eba",
+                "reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.2 || ^8.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.1.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Http\\Message\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "http://www.php-fig.org/"
+                }
+            ],
+            "description": "Common interface for HTTP messages",
+            "homepage": "https://github.com/php-fig/http-message",
+            "keywords": [
+                "http",
+                "http-message",
+                "psr",
+                "psr-7",
+                "request",
+                "response"
+            ],
+            "support": {
+                "source": "https://github.com/php-fig/http-message/tree/1.1"
+            },
+            "time": "2023-04-04T09:50:52+00:00"
+        },
+        {
+            "name": "react/async",
+            "version": "v4.3.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/reactphp/async.git",
+                "reference": "635d50e30844a484495713e8cb8d9e079c0008a5"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/reactphp/async/zipball/635d50e30844a484495713e8cb8d9e079c0008a5",
+                "reference": "635d50e30844a484495713e8cb8d9e079c0008a5",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.1",
+                "react/event-loop": "^1.2",
+                "react/promise": "^3.2 || ^2.8 || ^1.2.1"
+            },
+            "require-dev": {
+                "phpstan/phpstan": "1.10.39",
+                "phpunit/phpunit": "^9.6"
+            },
+            "type": "library",
+            "autoload": {
+                "files": [
+                    "src/functions_include.php"
+                ],
+                "psr-4": {
+                    "React\\Async\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Christian Lück",
+                    "email": "christian@clue.engineering",
+                    "homepage": "https://clue.engineering/"
+                },
+                {
+                    "name": "Cees-Jan Kiewiet",
+                    "email": "reactphp@ceesjankiewiet.nl",
+                    "homepage": "https://wyrihaximus.net/"
+                },
+                {
+                    "name": "Jan Sorgalla",
+                    "email": "jsorgalla@gmail.com",
+                    "homepage": "https://sorgalla.com/"
+                },
+                {
+                    "name": "Chris Boden",
+                    "email": "cboden@gmail.com",
+                    "homepage": "https://cboden.dev/"
+                }
+            ],
+            "description": "Async utilities and fibers for ReactPHP",
+            "keywords": [
+                "async",
+                "reactphp"
+            ],
+            "support": {
+                "issues": "https://github.com/reactphp/async/issues",
+                "source": "https://github.com/reactphp/async/tree/v4.3.0"
+            },
+            "funding": [
+                {
+                    "url": "https://opencollective.com/reactphp",
+                    "type": "open_collective"
+                }
+            ],
+            "time": "2024-06-04T14:40:02+00:00"
+        },
+        {
+            "name": "react/cache",
+            "version": "v1.2.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/reactphp/cache.git",
+                "reference": "d47c472b64aa5608225f47965a484b75c7817d5b"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/reactphp/cache/zipball/d47c472b64aa5608225f47965a484b75c7817d5b",
+                "reference": "d47c472b64aa5608225f47965a484b75c7817d5b",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.0",
+                "react/promise": "^3.0 || ^2.0 || ^1.1"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9.5 || ^5.7 || ^4.8.35"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "React\\Cache\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Christian Lück",
+                    "email": "christian@clue.engineering",
+                    "homepage": "https://clue.engineering/"
+                },
+                {
+                    "name": "Cees-Jan Kiewiet",
+                    "email": "reactphp@ceesjankiewiet.nl",
+                    "homepage": "https://wyrihaximus.net/"
+                },
+                {
+                    "name": "Jan Sorgalla",
+                    "email": "jsorgalla@gmail.com",
+                    "homepage": "https://sorgalla.com/"
+                },
+                {
+                    "name": "Chris Boden",
+                    "email": "cboden@gmail.com",
+                    "homepage": "https://cboden.dev/"
+                }
+            ],
+            "description": "Async, Promise-based cache interface for ReactPHP",
+            "keywords": [
+                "cache",
+                "caching",
+                "promise",
+                "reactphp"
+            ],
+            "support": {
+                "issues": "https://github.com/reactphp/cache/issues",
+                "source": "https://github.com/reactphp/cache/tree/v1.2.0"
+            },
+            "funding": [
+                {
+                    "url": "https://opencollective.com/reactphp",
+                    "type": "open_collective"
+                }
+            ],
+            "time": "2022-11-30T15:59:55+00:00"
+        },
+        {
+            "name": "react/dns",
+            "version": "v1.13.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/reactphp/dns.git",
+                "reference": "eb8ae001b5a455665c89c1df97f6fb682f8fb0f5"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/reactphp/dns/zipball/eb8ae001b5a455665c89c1df97f6fb682f8fb0f5",
+                "reference": "eb8ae001b5a455665c89c1df97f6fb682f8fb0f5",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.0",
+                "react/cache": "^1.0 || ^0.6 || ^0.5",
+                "react/event-loop": "^1.2",
+                "react/promise": "^3.2 || ^2.7 || ^1.2.1"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36",
+                "react/async": "^4.3 || ^3 || ^2",
+                "react/promise-timer": "^1.11"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "React\\Dns\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Christian Lück",
+                    "email": "christian@clue.engineering",
+                    "homepage": "https://clue.engineering/"
+                },
+                {
+                    "name": "Cees-Jan Kiewiet",
+                    "email": "reactphp@ceesjankiewiet.nl",
+                    "homepage": "https://wyrihaximus.net/"
+                },
+                {
+                    "name": "Jan Sorgalla",
+                    "email": "jsorgalla@gmail.com",
+                    "homepage": "https://sorgalla.com/"
+                },
+                {
+                    "name": "Chris Boden",
+                    "email": "cboden@gmail.com",
+                    "homepage": "https://cboden.dev/"
+                }
+            ],
+            "description": "Async DNS resolver for ReactPHP",
+            "keywords": [
+                "async",
+                "dns",
+                "dns-resolver",
+                "reactphp"
+            ],
+            "support": {
+                "issues": "https://github.com/reactphp/dns/issues",
+                "source": "https://github.com/reactphp/dns/tree/v1.13.0"
+            },
+            "funding": [
+                {
+                    "url": "https://opencollective.com/reactphp",
+                    "type": "open_collective"
+                }
+            ],
+            "time": "2024-06-13T14:18:03+00:00"
+        },
+        {
+            "name": "react/event-loop",
+            "version": "v1.5.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/reactphp/event-loop.git",
+                "reference": "bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/reactphp/event-loop/zipball/bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354",
+                "reference": "bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36"
+            },
+            "suggest": {
+                "ext-pcntl": "For signal handling support when using the StreamSelectLoop"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "React\\EventLoop\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Christian Lück",
+                    "email": "christian@clue.engineering",
+                    "homepage": "https://clue.engineering/"
+                },
+                {
+                    "name": "Cees-Jan Kiewiet",
+                    "email": "reactphp@ceesjankiewiet.nl",
+                    "homepage": "https://wyrihaximus.net/"
+                },
+                {
+                    "name": "Jan Sorgalla",
+                    "email": "jsorgalla@gmail.com",
+                    "homepage": "https://sorgalla.com/"
+                },
+                {
+                    "name": "Chris Boden",
+                    "email": "cboden@gmail.com",
+                    "homepage": "https://cboden.dev/"
+                }
+            ],
+            "description": "ReactPHP's core reactor event loop that libraries can use for evented I/O.",
+            "keywords": [
+                "asynchronous",
+                "event-loop"
+            ],
+            "support": {
+                "issues": "https://github.com/reactphp/event-loop/issues",
+                "source": "https://github.com/reactphp/event-loop/tree/v1.5.0"
+            },
+            "funding": [
+                {
+                    "url": "https://opencollective.com/reactphp",
+                    "type": "open_collective"
+                }
+            ],
+            "time": "2023-11-13T13:48:05+00:00"
+        },
+        {
+            "name": "react/http",
+            "version": "v1.11.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/reactphp/http.git",
+                "reference": "8db02de41dcca82037367f67a2d4be365b1c4db9"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/reactphp/http/zipball/8db02de41dcca82037367f67a2d4be365b1c4db9",
+                "reference": "8db02de41dcca82037367f67a2d4be365b1c4db9",
+                "shasum": ""
+            },
+            "require": {
+                "evenement/evenement": "^3.0 || ^2.0 || ^1.0",
+                "fig/http-message-util": "^1.1",
+                "php": ">=5.3.0",
+                "psr/http-message": "^1.0",
+                "react/event-loop": "^1.2",
+                "react/promise": "^3.2 || ^2.3 || ^1.2.1",
+                "react/socket": "^1.16",
+                "react/stream": "^1.4"
+            },
+            "require-dev": {
+                "clue/http-proxy-react": "^1.8",
+                "clue/reactphp-ssh-proxy": "^1.4",
+                "clue/socks-react": "^1.4",
+                "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36",
+                "react/async": "^4.2 || ^3 || ^2",
+                "react/promise-stream": "^1.4",
+                "react/promise-timer": "^1.11"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "React\\Http\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Christian Lück",
+                    "email": "christian@clue.engineering",
+                    "homepage": "https://clue.engineering/"
+                },
+                {
+                    "name": "Cees-Jan Kiewiet",
+                    "email": "reactphp@ceesjankiewiet.nl",
+                    "homepage": "https://wyrihaximus.net/"
+                },
+                {
+                    "name": "Jan Sorgalla",
+                    "email": "jsorgalla@gmail.com",
+                    "homepage": "https://sorgalla.com/"
+                },
+                {
+                    "name": "Chris Boden",
+                    "email": "cboden@gmail.com",
+                    "homepage": "https://cboden.dev/"
+                }
+            ],
+            "description": "Event-driven, streaming HTTP client and server implementation for ReactPHP",
+            "keywords": [
+                "async",
+                "client",
+                "event-driven",
+                "http",
+                "http client",
+                "http server",
+                "https",
+                "psr-7",
+                "reactphp",
+                "server",
+                "streaming"
+            ],
+            "support": {
+                "issues": "https://github.com/reactphp/http/issues",
+                "source": "https://github.com/reactphp/http/tree/v1.11.0"
+            },
+            "funding": [
+                {
+                    "url": "https://opencollective.com/reactphp",
+                    "type": "open_collective"
+                }
+            ],
+            "time": "2024-11-20T15:24:08+00:00"
+        },
+        {
+            "name": "react/promise",
+            "version": "v3.2.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/reactphp/promise.git",
+                "reference": "8a164643313c71354582dc850b42b33fa12a4b63"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/reactphp/promise/zipball/8a164643313c71354582dc850b42b33fa12a4b63",
+                "reference": "8a164643313c71354582dc850b42b33fa12a4b63",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.1.0"
+            },
+            "require-dev": {
+                "phpstan/phpstan": "1.10.39 || 1.4.10",
+                "phpunit/phpunit": "^9.6 || ^7.5"
+            },
+            "type": "library",
+            "autoload": {
+                "files": [
+                    "src/functions_include.php"
+                ],
+                "psr-4": {
+                    "React\\Promise\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Jan Sorgalla",
+                    "email": "jsorgalla@gmail.com",
+                    "homepage": "https://sorgalla.com/"
+                },
+                {
+                    "name": "Christian Lück",
+                    "email": "christian@clue.engineering",
+                    "homepage": "https://clue.engineering/"
+                },
+                {
+                    "name": "Cees-Jan Kiewiet",
+                    "email": "reactphp@ceesjankiewiet.nl",
+                    "homepage": "https://wyrihaximus.net/"
+                },
+                {
+                    "name": "Chris Boden",
+                    "email": "cboden@gmail.com",
+                    "homepage": "https://cboden.dev/"
+                }
+            ],
+            "description": "A lightweight implementation of CommonJS Promises/A for PHP",
+            "keywords": [
+                "promise",
+                "promises"
+            ],
+            "support": {
+                "issues": "https://github.com/reactphp/promise/issues",
+                "source": "https://github.com/reactphp/promise/tree/v3.2.0"
+            },
+            "funding": [
+                {
+                    "url": "https://opencollective.com/reactphp",
+                    "type": "open_collective"
+                }
+            ],
+            "time": "2024-05-24T10:39:05+00:00"
+        },
+        {
+            "name": "react/socket",
+            "version": "v1.16.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/reactphp/socket.git",
+                "reference": "23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/reactphp/socket/zipball/23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1",
+                "reference": "23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1",
+                "shasum": ""
+            },
+            "require": {
+                "evenement/evenement": "^3.0 || ^2.0 || ^1.0",
+                "php": ">=5.3.0",
+                "react/dns": "^1.13",
+                "react/event-loop": "^1.2",
+                "react/promise": "^3.2 || ^2.6 || ^1.2.1",
+                "react/stream": "^1.4"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36",
+                "react/async": "^4.3 || ^3.3 || ^2",
+                "react/promise-stream": "^1.4",
+                "react/promise-timer": "^1.11"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "React\\Socket\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Christian Lück",
+                    "email": "christian@clue.engineering",
+                    "homepage": "https://clue.engineering/"
+                },
+                {
+                    "name": "Cees-Jan Kiewiet",
+                    "email": "reactphp@ceesjankiewiet.nl",
+                    "homepage": "https://wyrihaximus.net/"
+                },
+                {
+                    "name": "Jan Sorgalla",
+                    "email": "jsorgalla@gmail.com",
+                    "homepage": "https://sorgalla.com/"
+                },
+                {
+                    "name": "Chris Boden",
+                    "email": "cboden@gmail.com",
+                    "homepage": "https://cboden.dev/"
+                }
+            ],
+            "description": "Async, streaming plaintext TCP/IP and secure TLS socket server and client connections for ReactPHP",
+            "keywords": [
+                "Connection",
+                "Socket",
+                "async",
+                "reactphp",
+                "stream"
+            ],
+            "support": {
+                "issues": "https://github.com/reactphp/socket/issues",
+                "source": "https://github.com/reactphp/socket/tree/v1.16.0"
+            },
+            "funding": [
+                {
+                    "url": "https://opencollective.com/reactphp",
+                    "type": "open_collective"
+                }
+            ],
+            "time": "2024-07-26T10:38:09+00:00"
+        },
+        {
+            "name": "react/stream",
+            "version": "v1.4.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/reactphp/stream.git",
+                "reference": "1e5b0acb8fe55143b5b426817155190eb6f5b18d"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/reactphp/stream/zipball/1e5b0acb8fe55143b5b426817155190eb6f5b18d",
+                "reference": "1e5b0acb8fe55143b5b426817155190eb6f5b18d",
+                "shasum": ""
+            },
+            "require": {
+                "evenement/evenement": "^3.0 || ^2.0 || ^1.0",
+                "php": ">=5.3.8",
+                "react/event-loop": "^1.2"
+            },
+            "require-dev": {
+                "clue/stream-filter": "~1.2",
+                "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "React\\Stream\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Christian Lück",
+                    "email": "christian@clue.engineering",
+                    "homepage": "https://clue.engineering/"
+                },
+                {
+                    "name": "Cees-Jan Kiewiet",
+                    "email": "reactphp@ceesjankiewiet.nl",
+                    "homepage": "https://wyrihaximus.net/"
+                },
+                {
+                    "name": "Jan Sorgalla",
+                    "email": "jsorgalla@gmail.com",
+                    "homepage": "https://sorgalla.com/"
+                },
+                {
+                    "name": "Chris Boden",
+                    "email": "cboden@gmail.com",
+                    "homepage": "https://cboden.dev/"
+                }
+            ],
+            "description": "Event-driven readable and writable streams for non-blocking I/O in ReactPHP",
+            "keywords": [
+                "event-driven",
+                "io",
+                "non-blocking",
+                "pipe",
+                "reactphp",
+                "readable",
+                "stream",
+                "writable"
+            ],
+            "support": {
+                "issues": "https://github.com/reactphp/stream/issues",
+                "source": "https://github.com/reactphp/stream/tree/v1.4.0"
+            },
+            "funding": [
+                {
+                    "url": "https://opencollective.com/reactphp",
+                    "type": "open_collective"
+                }
+            ],
+            "time": "2024-06-11T12:45:25+00:00"
+        },
+        {
+            "name": "respect/stringifier",
+            "version": "0.2.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/Respect/Stringifier.git",
+                "reference": "e55af3c8aeaeaa2abb5fa47a58a8e9688cc23b59"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/Respect/Stringifier/zipball/e55af3c8aeaeaa2abb5fa47a58a8e9688cc23b59",
+                "reference": "e55af3c8aeaeaa2abb5fa47a58a8e9688cc23b59",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.1"
+            },
+            "require-dev": {
+                "friendsofphp/php-cs-fixer": "^2.8",
+                "malukenho/docheader": "^0.1.7",
+                "phpunit/phpunit": "^6.4"
+            },
+            "type": "library",
+            "autoload": {
+                "files": [
+                    "src/stringify.php"
+                ],
+                "psr-4": {
+                    "Respect\\Stringifier\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Respect/Stringifier Contributors",
+                    "homepage": "https://github.com/Respect/Stringifier/graphs/contributors"
+                }
+            ],
+            "description": "Converts any value to a string",
+            "homepage": "http://respect.github.io/Stringifier/",
+            "keywords": [
+                "respect",
+                "stringifier",
+                "stringify"
+            ],
+            "support": {
+                "issues": "https://github.com/Respect/Stringifier/issues",
+                "source": "https://github.com/Respect/Stringifier/tree/0.2.0"
+            },
+            "time": "2017-12-29T19:39:25+00:00"
+        },
+        {
+            "name": "respect/validation",
+            "version": "2.4.4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/Respect/Validation.git",
+                "reference": "f13f10f19978aea33af2a102a2f58f2db1e63619"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/Respect/Validation/zipball/f13f10f19978aea33af2a102a2f58f2db1e63619",
+                "reference": "f13f10f19978aea33af2a102a2f58f2db1e63619",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.1",
+                "respect/stringifier": "^0.2.0",
+                "symfony/polyfill-mbstring": "^1.2"
+            },
+            "require-dev": {
+                "egulias/email-validator": "^3.0",
+                "giggsey/libphonenumber-for-php-lite": "^8.13 || ^9.0",
+                "malukenho/docheader": "^1.0",
+                "mikey179/vfsstream": "^1.6",
+                "phpstan/phpstan": "^1.9",
+                "phpstan/phpstan-deprecation-rules": "^1.1",
+                "phpstan/phpstan-phpunit": "^1.3",
+                "phpunit/phpunit": "^9.6",
+                "psr/http-message": "^1.0",
+                "respect/coding-standard": "^4.0",
+                "squizlabs/php_codesniffer": "^3.7"
+            },
+            "suggest": {
+                "egulias/email-validator": "Improves the Email rule if available",
+                "ext-bcmath": "Arbitrary Precision Mathematics",
+                "ext-fileinfo": "File Information",
+                "ext-mbstring": "Multibyte String Functions",
+                "giggsey/libphonenumber-for-php-lite": "Enables the phone rule if available"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Respect\\Validation\\": "library/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Respect/Validation Contributors",
+                    "homepage": "https://github.com/Respect/Validation/graphs/contributors"
+                }
+            ],
+            "description": "The most awesome validation engine ever created for PHP",
+            "homepage": "http://respect.github.io/Validation/",
+            "keywords": [
+                "respect",
+                "validation",
+                "validator"
+            ],
+            "support": {
+                "issues": "https://github.com/Respect/Validation/issues",
+                "source": "https://github.com/Respect/Validation/tree/2.4.4"
+            },
+            "time": "2025-06-07T00:07:21+00:00"
+        },
+        {
+            "name": "symfony/polyfill-ctype",
+            "version": "v1.32.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-ctype.git",
+                "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638",
+                "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.2"
+            },
+            "provide": {
+                "ext-ctype": "*"
+            },
+            "suggest": {
+                "ext-ctype": "For best performance"
+            },
+            "type": "library",
+            "extra": {
+                "thanks": {
+                    "url": "https://github.com/symfony/polyfill",
+                    "name": "symfony/polyfill"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "bootstrap.php"
+                ],
+                "psr-4": {
+                    "Symfony\\Polyfill\\Ctype\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Gert de Pagter",
+                    "email": "BackEndTea@gmail.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill for ctype functions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "ctype",
+                "polyfill",
+                "portable"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/polyfill-ctype/tree/v1.32.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2024-09-09T11:45:10+00:00"
+        },
+        {
+            "name": "symfony/polyfill-mbstring",
+            "version": "v1.32.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-mbstring.git",
+                "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493",
+                "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493",
+                "shasum": ""
+            },
+            "require": {
+                "ext-iconv": "*",
+                "php": ">=7.2"
+            },
+            "provide": {
+                "ext-mbstring": "*"
+            },
+            "suggest": {
+                "ext-mbstring": "For best performance"
+            },
+            "type": "library",
+            "extra": {
+                "thanks": {
+                    "url": "https://github.com/symfony/polyfill",
+                    "name": "symfony/polyfill"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "bootstrap.php"
+                ],
+                "psr-4": {
+                    "Symfony\\Polyfill\\Mbstring\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill for the Mbstring extension",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "mbstring",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.32.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2024-12-23T08:48:59+00:00"
+        },
+        {
+            "name": "symfony/polyfill-php80",
+            "version": "v1.32.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-php80.git",
+                "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/0cc9dd0f17f61d8131e7df6b84bd344899fe2608",
+                "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.2"
+            },
+            "type": "library",
+            "extra": {
+                "thanks": {
+                    "url": "https://github.com/symfony/polyfill",
+                    "name": "symfony/polyfill"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "bootstrap.php"
+                ],
+                "psr-4": {
+                    "Symfony\\Polyfill\\Php80\\": ""
+                },
+                "classmap": [
+                    "Resources/stubs"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Ion Bazan",
+                    "email": "ion.bazan@gmail.com"
+                },
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/polyfill-php80/tree/v1.32.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2025-01-02T08:10:11+00:00"
+        },
+        {
+            "name": "vlucas/phpdotenv",
+            "version": "v5.6.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/vlucas/phpdotenv.git",
+                "reference": "24ac4c74f91ee2c193fa1aaa5c249cb0822809af"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/24ac4c74f91ee2c193fa1aaa5c249cb0822809af",
+                "reference": "24ac4c74f91ee2c193fa1aaa5c249cb0822809af",
+                "shasum": ""
+            },
+            "require": {
+                "ext-pcre": "*",
+                "graham-campbell/result-type": "^1.1.3",
+                "php": "^7.2.5 || ^8.0",
+                "phpoption/phpoption": "^1.9.3",
+                "symfony/polyfill-ctype": "^1.24",
+                "symfony/polyfill-mbstring": "^1.24",
+                "symfony/polyfill-php80": "^1.24"
+            },
+            "require-dev": {
+                "bamarni/composer-bin-plugin": "^1.8.2",
+                "ext-filter": "*",
+                "phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2"
+            },
+            "suggest": {
+                "ext-filter": "Required to use the boolean validator."
+            },
+            "type": "library",
+            "extra": {
+                "bamarni-bin": {
+                    "bin-links": true,
+                    "forward-command": false
+                },
+                "branch-alias": {
+                    "dev-master": "5.6-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Dotenv\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Graham Campbell",
+                    "email": "hello@gjcampbell.co.uk",
+                    "homepage": "https://github.com/GrahamCampbell"
+                },
+                {
+                    "name": "Vance Lucas",
+                    "email": "vance@vancelucas.com",
+                    "homepage": "https://github.com/vlucas"
+                }
+            ],
+            "description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.",
+            "keywords": [
+                "dotenv",
+                "env",
+                "environment"
+            ],
+            "support": {
+                "issues": "https://github.com/vlucas/phpdotenv/issues",
+                "source": "https://github.com/vlucas/phpdotenv/tree/v5.6.2"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/GrahamCampbell",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2025-04-30T23:37:27+00:00"
+        }
+    ],
+    "packages-dev": [],
+    "aliases": [],
+    "minimum-stability": "stable",
+    "stability-flags": {},
+    "prefer-stable": false,
+    "prefer-lowest": false,
+    "platform": {},
+    "platform-dev": {},
+    "plugin-api-version": "2.6.0"
+}

+ 84 - 0
controllers/CprPreregistrationController.php

@@ -0,0 +1,84 @@
+<?php
+
+namespace Controllers;
+
+use Libs\ResponseLib;
+use Models\CprPreregistrationModel;
+use Psr\Http\Message\ServerRequestInterface;
+use Respect\Validation\Validator as v;
+
+class CprPreregistrationController
+{
+    public function __invoke(ServerRequestInterface $request)
+    {
+        $body = json_decode((string) $request->getBody(), true) ?? [];
+
+        // Extract and trim inputs
+        $name = isset($body['name']) ? trim((string)$body['name']) : '';
+        $contactNumber = isset($body['contact_number']) ? trim((string)$body['contact_number']) : '';
+        $email = isset($body['email']) ? trim((string)$body['email']) : '';
+        $productType = isset($body['product_type']) ? trim((string)$body['product_type']) : '';
+        $internalControlNumber = isset($body['internal_control_number']) ? trim((string)$body['internal_control_number']) : '';
+        $productQuantityRaw = $body['product_quantity'] ?? null;
+        $propertyName = isset($body['property_name']) ? trim((string)$body['property_name']) : '';
+        $propertyLocation = isset($body['property_location']) ? trim((string)$body['property_location']) : '';
+
+        // Define validation rules
+        $rules = [
+            'name' => v::stringType()->notEmpty()->length(2, 120),
+            // Accept digits, spaces, plus, dash, parentheses
+            'contact_number' => v::stringType()->notEmpty()->regex('/^[0-9+()\\s-]{8,25}$/'),
+            'email' => v::email()->notEmpty(),
+            'product_type' => v::stringType()->notEmpty()->alnum(" -_/.,")->length(1, 100),
+            'internal_control_number' => v::stringType()->notEmpty()->alnum("-_/.")->length(1, 100),
+            // Accept numeric strings convertible to int
+            'product_quantity' => v::intVal()->min(1),
+            'property_name' => v::stringType()->notEmpty()->alnum(" -_/.,")->length(1, 150),
+            'property_location' => v::stringType()->notEmpty()->alnum(" -_/.,")->length(1, 200),
+        ];
+
+        $inputs = [
+            'name' => $name,
+            'contact_number' => $contactNumber,
+            'email' => $email,
+            'product_type' => $productType,
+            'internal_control_number' => $internalControlNumber,
+            'product_quantity' => $productQuantityRaw,
+            'property_name' => $propertyName,
+            'property_location' => $propertyLocation,
+        ];
+
+        $errors = [];
+        foreach ($inputs as $field => $value) {
+            try {
+                $rules[$field]->assert($value);
+            } catch (\Throwable $e) {
+                $errors[$field] = $e->getMessage();
+            }
+        }
+
+        if (!empty($errors)) {
+            return ResponseLib::sendFail("Validation failed", ['errors' => $errors], "E_VALIDATE")->withStatus(400);
+        }
+
+        // Safe casting after validation
+        $productQuantity = (int)$productQuantityRaw;
+
+        try {
+            $model = new CprPreregistrationModel();
+            $saved = $model->insert(
+                $name,
+                $contactNumber,
+                $email,
+                $productType,
+                $internalControlNumber,
+                $productQuantity,
+                $propertyName,
+                $propertyLocation
+            );
+            return ResponseLib::sendOk($saved, "S_CREATED");
+        } catch (\Throwable $e) {
+            return ResponseLib::sendFail("Database error: " . $e->getMessage(), [], "E_DB")->withStatus(500);
+        }
+    }
+}

+ 16 - 0
controllers/HelloController.php

@@ -0,0 +1,16 @@
+<?php
+
+namespace Controllers;
+
+use Libs\ResponseLib;
+use Psr\Http\Message\ServerRequestInterface;
+
+class HelloController
+{
+    public function __invoke(ServerRequestInterface $request)
+    {
+        //$apiUser = $request->getAttribute('api_user');  // Exemplo: usa atributo do middleware
+        $data = ["message" => "Hello World!"];
+        return ResponseLib::sendOk($data);
+    }
+}

+ 40 - 0
controllers/LoginController.php

@@ -0,0 +1,40 @@
+<?php
+
+namespace Controllers;
+
+use Firebase\JWT\JWT;
+use Libs\ResponseLib;
+use Models\UserModel;
+use Psr\Http\Message\ServerRequestInterface;
+
+class LoginController
+{
+    public function __invoke(ServerRequestInterface $request)
+    {
+        $body = json_decode((string) $request->getBody(), true);
+        $username = $body['username'] ?? '';
+        $password = $body['password'] ?? '';
+
+        if (empty($username) || empty($password)) {
+            return ResponseLib::sendFail("Missing username or password", [], "E_VALIDATE")->withStatus(401);
+        }
+
+        $userModel = new UserModel();
+        $user = $userModel->validateLogin($username, $password);
+
+        if (!$user) {
+            return ResponseLib::sendFail("Invalid credentials", [], "E_VALIDATE")->withStatus(401);
+        }
+
+        // Gera JWT
+        $payload = [
+            'sub' => $user['user_id'],
+            'username' => $user['user_name'],
+            'iat' => time(),
+            'exp' => time() + 3600  // 1 hora
+        ];
+        $jwt = JWT::encode($payload, $_ENV['JWT_SECRET'], 'HS256');
+
+        return ResponseLib::sendOk(['token' => $jwt, 'user_id' => $user['user_id']]);
+    }
+}   

+ 35 - 0
controllers/RegisterController.php

@@ -0,0 +1,35 @@
+<?php
+
+namespace Controllers;
+
+use Libs\ResponseLib;
+use Models\UserModel;
+use Psr\Http\Message\ServerRequestInterface;
+
+class RegisterController
+{
+    public function __invoke(ServerRequestInterface $request)
+    {
+        $body = json_decode((string) $request->getBody(), true);
+        $username = $body['username'] ?? '';
+        $password = $body['password'] ?? '';
+
+        if (empty($username) || empty($password)) {
+            return ResponseLib::sendFail("Missing username or password", [], "E_VALIDATE")->withStatus(400);
+        }
+
+        // Validação básica (ex: comprimento mínimo)
+        if (strlen($password) < 8) {
+            return ResponseLib::sendFail("Password must be at least 8 characters", [], "E_VALIDATE")->withStatus(400);
+        }
+
+        $userModel = new UserModel();
+        $userData = $userModel->createUser($username, $password);
+
+        if (!$userData) {
+            return ResponseLib::sendFail("Username already exists or creation failed", [], "E_VALIDATE")->withStatus(400);
+        }
+
+        return ResponseLib::sendOk($userData, "S_CREATED");
+    }
+}

+ 29 - 0
controllers/TesteController.php

@@ -0,0 +1,29 @@
+<?php
+
+namespace Controllers;
+
+use Libs\ResponseLib;
+use Models\TesteModel;
+use Psr\Http\Message\ServerRequestInterface;
+
+class TesteController
+{
+    public function __invoke(ServerRequestInterface $request)
+    {
+        $body = json_decode((string) $request->getBody(), true) ?? [];
+        $field1 = $body['field1'] ?? '';
+        $field2 = $body['field2'] ?? null;
+
+        if (empty($field1)) {
+            return ResponseLib::sendFail("Missing required field: field1", [], "E_VALIDATE")->withStatus(400);
+        }
+
+        try {
+            $model = new TesteModel();
+            $saved = $model->insert((string)$field1, $field2 !== null ? (string)$field2 : null);
+            return ResponseLib::sendOk($saved, "S_CREATED");
+        } catch (\Throwable $e) {
+            return ResponseLib::sendFail("Database error: " . $e->getMessage(), [], "E_DB")->withStatus(500);
+        }
+    }
+}

+ 31 - 0
libs/ResponseLib.php

@@ -0,0 +1,31 @@
+<?php
+
+namespace Libs;
+
+use React\Http\Message\Response;
+
+class ResponseLib
+{
+    public static function sendOk($data, $code = "S_OK")
+    {
+        $reply = [
+            'status' => 'ok',
+            'msg' => '[100] Request ok.',
+            'code' => $code,
+            'data' => $data
+        ];
+        return Response::json($reply);
+    }
+
+    public static function sendFail($message, $data = [], $code = "E_GENERIC")
+    {
+        $reply = [
+            'status' => 'failed',
+            'msg' => $message,
+            'code' => $code,
+            'data' => $data
+        ];
+        return Response::json($reply);
+    }
+}
+

+ 66 - 0
middlewares/HmacAuthMiddleware.php

@@ -0,0 +1,66 @@
+<?php
+
+namespace Middlewares;
+
+use Libs\ResponseLib;
+use Models\ApiUserModel;
+use Psr\Http\Message\ServerRequestInterface;
+use React\Http\Message\Response;
+
+class HmacAuthMiddleware
+{
+    private array $api_Key;
+
+    public function __construct()
+    {
+        // Instancia a model e carrega as chaves API
+        $apiUserModel = new ApiUserModel();
+        $this->api_Key = $apiUserModel->getApiKeys();
+    }
+
+    public function __invoke(ServerRequestInterface $request, callable $next)
+    {
+        // 1. Extrai headers
+        $signature = $request->getHeaderLine('x-signature');
+        $apiUser = $request->getHeaderLine('x-user');
+        $nonce = $request->getHeaderLine('x-nonce');
+
+        if (empty($signature) || empty($apiUser) || empty($nonce)) {
+            return ResponseLib::sendFail("Unauthorized: Missing signature or headers", [], "E_VALIDATE")->withStatus(401);
+        }
+
+        // 2. Verifica se nonce está dentro do intervalo
+        $currentTime = time();
+        if (abs($currentTime - (int) $nonce) > 2) {
+            return ResponseLib::sendFail("Unauthorized: Invalid or expired nonce", [], "E_VALIDATE")->withStatus(401);
+        }
+
+        // 3. Verifica se o usuário é válido
+        if (!isset($this->api_Key[$apiUser])) {
+            return ResponseLib::sendFail("Unauthorized: Invalid API User", [], "E_VALIDATE")->withStatus(401);
+        }
+
+        $apiKey = $this->api_Key[$apiUser]['user_apikey'];
+        $apiSecret = $this->api_Key[$apiUser]['user_apisecret'];
+        $secret = $apiKey . "::" . $apiSecret;
+
+        // 4. Monta mensagem para HMAC: <jsonBody>::<nonce>
+        $rawBody = (string) $request->getBody();
+        $message = $rawBody . "::" . $nonce;
+
+        // 5. Calcula assinatura esperada
+        $expectedSignature = hash_hmac('sha256', $message, $secret);
+
+        // 6. Verifica assinatura
+        if (!hash_equals($expectedSignature, $signature)) {
+            return ResponseLib::sendFail("Unauthorized: Signature mismatch", [], "E_VALIDATE")->withStatus(401);
+        }
+
+        // 7. Tudo certo, adiciona atributos ao request e segue
+        $request = $request
+            ->withAttribute('api_user', $apiUser)
+            ->withAttribute('api_user_id', $this->api_Key[$apiUser]['user_id']);
+
+        return $next($request);
+    }
+}

+ 62 - 0
middlewares/JwtAuthMiddleware.php

@@ -0,0 +1,62 @@
+<?php
+
+namespace Middlewares;
+
+use Firebase\JWT\JWT;
+use Firebase\JWT\Key;
+use Libs\ResponseLib;
+use Psr\Http\Message\ServerRequestInterface;
+use React\Http\Message\Response;
+
+class JwtAuthMiddleware
+{
+    private string $jwtSecret;
+
+    public function __construct()
+    {
+        // Carrega a chave secreta do .env (ex: JWT_SECRET=seu-segredo-aqui)
+        $this->jwtSecret = $_ENV['JWT_SECRET'] ?? 'default-secret-fallback';  // Use um fallback seguro em dev
+    }
+
+    public function __invoke(ServerRequestInterface $request, callable $next)
+    {
+        $authHeader = $request->getHeaderLine('Authorization');
+        if (empty($authHeader) || !preg_match('/Bearer\s+(.*)/', $authHeader, $matches)) {
+            return ResponseLib::sendFail("Unauthorized: Missing or invalid Authorization header", [], "E_VALIDATE")->withStatus(401);
+        }
+
+        $token = $matches[1];
+
+        try {
+            $decoded = JWT::decode($token, new Key($this->jwtSecret, 'HS256'));
+            $userId = $decoded->sub ?? null;
+            $apiUser = $decoded->username ?? null;
+
+            if (empty($userId) || empty($apiUser)) {
+                return ResponseLib::sendFail("Unauthorized: Invalid JWT claims", [], "E_VALIDATE")->withStatus(401);
+            }
+
+            $dbFile = $_ENV['DB_FILE'] ?? 'bridge.db';
+            $dbPath = __DIR__ . '/../' . $dbFile;
+            $pdo = new \PDO("sqlite:" . $dbPath);
+            $pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
+
+            $stmt = $pdo->prepare("SELECT user_id FROM user WHERE user_id = :user_id AND user_name = :user_name AND user_flag = 'a'");
+            $stmt->execute(['user_id' => $userId, 'user_name' => $apiUser]);
+            $user = $stmt->fetch(\PDO::FETCH_ASSOC);
+
+            if (!$user) {
+                return ResponseLib::sendFail("Unauthorized: Invalid or inactive user", [], "E_VALIDATE")->withStatus(401);
+            }
+
+            $request = $request
+                ->withAttribute('api_user', $apiUser)
+                ->withAttribute('api_user_id', $userId);
+
+            return $next($request);
+
+        } catch (\Exception $e) {
+            return ResponseLib::sendFail("Unauthorized: " . $e->getMessage(), [], "E_VALIDATE")->withStatus(401);
+        }
+    }
+}

+ 16 - 0
migrations/migrations_v1.sql

@@ -0,0 +1,16 @@
+PRAGMA foreign_keys=ON;
+
+CREATE TABLE user (
+    user_id INTEGER PRIMARY KEY AUTOINCREMENT,
+    user_name TEXT NOT NULL,
+    user_flag TEXT NOT NULL,
+    user_password TEXT NOT NULL  -- Nova coluna para senha hasheada
+);
+
+CREATE TABLE api_key (
+    api_key_id INTEGER PRIMARY KEY AUTOINCREMENT,
+    user_id INTEGER NOT NULL,
+    api_key_user TEXT NOT NULL,
+    api_key_secret TEXT NOT NULL,
+    FOREIGN KEY (user_id) REFERENCES user(user_id)
+);

+ 9 - 0
migrations/migrations_v2.sql

@@ -0,0 +1,9 @@
+-- Migration v2: create 'teste' table
+PRAGMA foreign_keys=ON;
+
+CREATE TABLE IF NOT EXISTS teste (
+    teste_id INTEGER PRIMARY KEY AUTOINCREMENT,
+    field1 TEXT NOT NULL,
+    field2 TEXT NULL,
+    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
+);

+ 15 - 0
migrations/migrations_v3.sql

@@ -0,0 +1,15 @@
+-- Migration v3: create 'cpr_preregistration' table for CPR pre-registration form
+PRAGMA foreign_keys=ON;
+
+CREATE TABLE IF NOT EXISTS cpr_preregistration (
+    cpr_id INTEGER PRIMARY KEY AUTOINCREMENT,
+    name TEXT NOT NULL,
+    contact_number TEXT NOT NULL,
+    email TEXT NOT NULL,
+    product_type TEXT NOT NULL,
+    internal_control_number TEXT NOT NULL,
+    product_quantity INTEGER NOT NULL,
+    property_name TEXT NOT NULL,
+    property_location TEXT NOT NULL,
+    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
+);

+ 58 - 0
models/ApiUserModel.php

@@ -0,0 +1,58 @@
+<?php
+
+namespace Models;
+
+class ApiUserModel
+{
+    private \PDO $pdo;
+    private array $api_Key = [];
+
+    public function __construct()
+    {
+        // Conecta ao DB usando variável do .env
+        $dbFile = $_ENV['DB_FILE'];
+        $dbPath = __DIR__ . '/../' . $dbFile;
+        $this->pdo = new \PDO("sqlite:" . $dbPath);
+        $this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
+
+        $this->loadApiKeys();
+    }
+
+    /**
+     * Carrega as chaves API dos usuários ativos do banco de dados.
+     */
+    private function loadApiKeys(): void
+    {
+        try {
+            $stmt = $this->pdo->query("SELECT user_id, user_name, api_key_user, api_key_secret FROM user NATURAL JOIN api_key WHERE user_flag = 'a'");
+            $users = $stmt->fetchAll(\PDO::FETCH_ASSOC);
+
+            foreach ($users as $user) {
+                $this->api_Key[$user['user_name']] = [
+                    'user_apikey' => $user['api_key_user'],
+                    'user_apisecret' => $user['api_key_secret'],
+                    'user_id' => $user['user_id']
+                ];
+            }
+        } catch (\PDOException $e) {
+            error_log("Erro ao carregar chaves API: " . $e->getMessage());
+        }
+    }
+
+    /**
+     * Retorna o array de chaves API carregadas.
+     *
+     * @return array
+     */
+    public function getApiKeys(): array
+    {
+        return $this->api_Key;
+    }
+
+    // Opcional: Método para recarregar as chaves se necessário
+    public function reloadApiKeys(): void
+    {
+        $this->api_Key = [];
+        $this->loadApiKeys();
+    }
+}

+ 55 - 0
models/CprPreregistrationModel.php

@@ -0,0 +1,55 @@
+<?php
+
+namespace Models;
+
+class CprPreregistrationModel
+{
+    private \PDO $pdo;
+
+    public function __construct()
+    {
+        $dbFile = $_ENV['DB_FILE'];
+        $dbPath = __DIR__ . '/../' . $dbFile;
+        $this->pdo = new \PDO("sqlite:" . $dbPath);
+        $this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
+    }
+
+    public function insert(
+        string $name,
+        string $contactNumber,
+        string $email,
+        string $productType,
+        string $internalControlNumber,
+        int $productQuantity,
+        string $propertyName,
+        string $propertyLocation
+    ): array {
+        $stmt = $this->pdo->prepare(
+            "INSERT INTO cpr_preregistration (
+                name, contact_number, email, product_type, internal_control_number, product_quantity, property_name, property_location
+            ) VALUES (
+                :name, :contact_number, :email, :product_type, :internal_control_number, :product_quantity, :property_name, :property_location
+            )"
+        );
+
+        $stmt->execute([
+            'name' => $name,
+            'contact_number' => $contactNumber,
+            'email' => $email,
+            'product_type' => $productType,
+            'internal_control_number' => $internalControlNumber,
+            'product_quantity' => $productQuantity,
+            'property_name' => $propertyName,
+            'property_location' => $propertyLocation,
+        ]);
+
+        $id = (int)$this->pdo->lastInsertId();
+        $stmt = $this->pdo->prepare("SELECT * FROM cpr_preregistration WHERE cpr_id = :id");
+        $stmt->execute(['id' => $id]);
+        $row = $stmt->fetch(\PDO::FETCH_ASSOC);
+        if (!$row) {
+            throw new \RuntimeException('Failed to fetch inserted CPR pre-registration record');
+        }
+        return $row;
+    }
+}

+ 34 - 0
models/TesteModel.php

@@ -0,0 +1,34 @@
+<?php
+
+namespace Models;
+
+class TesteModel
+{
+    private \PDO $pdo;
+
+    public function __construct()
+    {
+        $dbFile = $_ENV['DB_FILE'];
+        $dbPath = __DIR__ . '/../' . $dbFile;
+        $this->pdo = new \PDO("sqlite:" . $dbPath);
+        $this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
+    }
+
+    public function insert(string $field1, ?string $field2 = null): array
+    {
+        $stmt = $this->pdo->prepare("INSERT INTO teste (field1, field2) VALUES (:field1, :field2)");
+        $stmt->execute([
+            'field1' => $field1,
+            'field2' => $field2
+        ]);
+
+        $id = (int)$this->pdo->lastInsertId();
+        $stmt = $this->pdo->prepare("SELECT teste_id, field1, field2, created_at FROM teste WHERE teste_id = :id");
+        $stmt->execute(['id' => $id]);
+        $row = $stmt->fetch(\PDO::FETCH_ASSOC);
+        if (!$row) {
+            throw new \RuntimeException('Failed to fetch inserted record');
+        }
+        return $row;
+    }
+}

+ 81 - 0
models/UserModel.php

@@ -0,0 +1,81 @@
+<?php
+
+namespace Models;
+
+class UserModel
+{
+    private \PDO $pdo;
+
+    public function __construct()
+    {
+        // Conecta ao DB usando variável do .env
+        $dbFile = $_ENV['DB_FILE'];
+        $dbPath = __DIR__ . '/../' . $dbFile;
+        $this->pdo = new \PDO("sqlite:" . $dbPath);
+        $this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
+    }
+
+    /**
+     * Valida credenciais de login e retorna dados do usuário se válido.
+     *
+     * @param string $username
+     * @param string $password Plain-text password para verificar
+     * @return array|null Dados do usuário (user_id, user_name, etc.) ou null se inválido
+     */
+    public function validateLogin(string $username, string $password): ?array
+    {
+        $stmt = $this->pdo->prepare("SELECT user_id, user_name, user_password FROM user WHERE user_name = :username AND user_flag = 'a'");
+        $stmt->execute(['username' => $username]);
+        $user = $stmt->fetch(\PDO::FETCH_ASSOC);
+
+        if ($user && password_verify($password, $user['user_password'])) {
+            unset($user['password']);  // Remove hash por segurança
+            return $user;
+        }
+
+        return null;
+    }
+
+    /**
+     * Cria um novo usuário com senha hasheada e gera chaves API.
+     *
+     * @param string $username
+     * @param string $password Plain-text password
+     * @param string $flag Default 'a' para ativo
+     * @return array|bool Dados do usuário criado (incluindo api_key) ou false em erro
+     */
+    public function createUser(string $username, string $password, string $flag = 'a')
+    {
+        // Verifica se username já existe
+        $stmt = $this->pdo->prepare("SELECT user_id FROM user WHERE user_name = :username");
+        $stmt->execute(['username' => $username]);
+        if ($stmt->fetch()) {
+            return false;  // Já existe
+        }
+
+        $hash = password_hash($password, PASSWORD_DEFAULT);
+
+        // Insere usuário
+        $stmt = $this->pdo->prepare("INSERT INTO user (user_name, user_flag, user_password) VALUES (:username, :flag, :hash)");
+        if (!$stmt->execute(['username' => $username, 'flag' => $flag, 'hash' => $hash])) {
+            return false;
+        }
+
+        $userId = $this->pdo->lastInsertId();
+
+        // Gera e insere chaves API (random para HMAC)
+        $apiKey = bin2hex(random_bytes(16));  // Ex: 32 chars hex
+        $apiSecret = bin2hex(random_bytes(32));  // Mais longo para secret
+        $stmt = $this->pdo->prepare("INSERT INTO api_key (user_id, api_key_user, api_key_secret) VALUES (:user_id, :api_key, :api_secret)");
+        if (!$stmt->execute(['user_id' => $userId, 'api_key' => $apiKey, 'api_secret' => $apiSecret])) {
+            return false;
+        }
+
+        return [
+            'user_id' => $userId,
+            'user_name' => $username,
+            'api_key_user' => $apiKey,
+            'api_key_secret' => $apiSecret  // Retorne para o usuário (apenas uma vez!)
+        ];
+    }
+}

BIN=BIN
php-api_1.0.0_all.deb


+ 30 - 0
public/index.php

@@ -0,0 +1,30 @@
+<?php
+
+require __DIR__ . '/../vendor/autoload.php';
+
+$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
+$file = __DIR__ . $path;
+if (php_sapi_name() === 'cli-server' && is_file($file)) {
+    return false;
+}
+
+if (class_exists(Dotenv\Dotenv::class) && file_exists(__DIR__ . '/../.env')) {
+    Dotenv\Dotenv::createImmutable(
+        dirname(__DIR__),
+        null,
+        true
+    )->safeLoad();
+}
+
+error_reporting(E_ALL);
+
+use FrameworkX\App;
+use Middlewares\HmacAuthMiddleware;
+
+$app = new App();
+
+
+// CPR pre-registration endpoint
+$app->post('/cpr/preregistration', \Controllers\CprPreregistrationController::class);
+
+$app->run();