Slim API deployment - composer install + full CRUD endpoints

This commit is contained in:
Hermes Agent
2026-05-20 14:36:05 +02:00
parent 48bf9c7088
commit 8ab4e532fd
1727 changed files with 7746 additions and 7 deletions

View File

@@ -0,0 +1,42 @@
<?php
declare(strict_types=1);
namespace App\Support;
use App\Repositories\UserRepository;
final class Auth
{
public static function user(): ?array
{
$userId = $_SESSION['user_id'] ?? null;
if (!$userId) {
return null;
}
return (new UserRepository())->findById((int) $userId);
}
public static function requireUser(): array
{
$user = self::user();
if ($user === null) {
Response::json(['message' => 'Unauthenticated'], 401);
}
return $user;
}
public static function requireRole(array|string $roles): array
{
$user = self::requireUser();
$roles = (array) $roles;
if (!in_array($user['role'], $roles, true)) {
Response::json(['message' => 'Forbidden'], 403);
}
return $user;
}
}

View File

@@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
namespace App\Support;
use PDO;
final class Database
{
private static string $path;
private static ?PDO $connection = null;
public static function configure(string $path): void
{
self::$path = $path;
}
public static function connection(): PDO
{
if (self::$connection instanceof PDO) {
return self::$connection;
}
$directory = dirname(self::$path);
if (!is_dir($directory)) {
mkdir($directory, 0775, true);
}
self::$connection = new PDO('sqlite:' . self::$path);
self::$connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
self::$connection->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
self::$connection->exec('PRAGMA foreign_keys = ON');
return self::$connection;
}
}

View File

@@ -0,0 +1,40 @@
<?php
declare(strict_types=1);
namespace App\Support;
final class Request
{
public function method(): string
{
return strtoupper($_SERVER['REQUEST_METHOD'] ?? 'GET');
}
public function path(): string
{
$uri = $_SERVER['REQUEST_URI'] ?? '/';
$path = parse_url($uri, PHP_URL_PATH) ?: '/';
return rtrim($path, '/') ?: '/';
}
public function query(string $key, mixed $default = null): mixed
{
return $_GET[$key] ?? $default;
}
public function input(): array
{
$contentType = $_SERVER['CONTENT_TYPE'] ?? '';
if (str_contains($contentType, 'application/json')) {
$raw = file_get_contents('php://input');
$decoded = json_decode($raw ?: '[]', true);
return is_array($decoded) ? $decoded : [];
}
return $_POST;
}
}

View File

@@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
namespace App\Support;
final class Response
{
public static function json(array $data, int $status = 200): never
{
http_response_code($status);
header('Content-Type: application/json; charset=utf-8');
echo json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
exit;
}
public static function noContent(): never
{
http_response_code(204);
exit;
}
}

View File

@@ -0,0 +1,63 @@
<?php
declare(strict_types=1);
namespace App\Support;
final class Router
{
private array $routes = [];
public function __construct(private readonly Request $request)
{
}
public function get(string $path, callable $handler): void
{
$this->map('GET', $path, $handler);
}
public function post(string $path, callable $handler): void
{
$this->map('POST', $path, $handler);
}
public function patch(string $path, callable $handler): void
{
$this->map('PATCH', $path, $handler);
}
public function delete(string $path, callable $handler): void
{
$this->map('DELETE', $path, $handler);
}
public function dispatch(): void
{
$method = $this->request->method();
$path = $this->request->path();
foreach ($this->routes[$method] ?? [] as $route) {
$pattern = preg_replace('#\{([a-zA-Z_][a-zA-Z0-9_]*)\}#', '(?P<$1>[^/]+)', $route['path']);
$pattern = '#^' . $pattern . '$#';
if (!preg_match($pattern, $path, $matches)) {
continue;
}
$params = array_filter($matches, static fn (string|int $key): bool => is_string($key), ARRAY_FILTER_USE_KEY);
$route['handler']($this->request, $params);
return;
}
Response::json(['message' => 'Route not found', 'path' => $path], 404);
}
private function map(string $method, string $path, callable $handler): void
{
$this->routes[$method][] = [
'path' => rtrim($path, '/') ?: '/',
'handler' => $handler,
];
}
}