routerCachePath = WEBROOT . DIRECTORY_SEPARATOR . getClassPath(self::ROUTER_CACHE_CLASS); } protected function toggleRoute(int $id, bool $active): bool { $sql = $this->context->getSQL(); $route = Route::find($sql, $id); if ($route === false) { return false; } else if ($route === null) { return $this->createError("Route not found"); } else if ($route instanceof ApiRoute) { return $this->createError("This route cannot be modified"); } $route->setActive($active); $this->success = $route->save($sql, ["active"]); $this->lastError = $sql->getLastError(); return $this->success && $this->regenerateCache(); } protected function regenerateCache(): bool { $req = new GenerateCache($this->context); $this->success = $req->execute(); $this->lastError = $req->getLastError(); return $this->success; } protected function createRoute(string $type, string $pattern, string $target, ?string $extra, bool $exact, bool $active = true): ?Route { $routeClass = Route::ROUTE_TYPES[$type] ?? null; if (!$routeClass) { $this->createError("Invalid type: $type"); return null; } try { $routeClass = new \ReflectionClass($routeClass); $routeObj = $routeClass->newInstance($pattern, $exact, $target); $routeObj->setExtra($extra); $routeObj->setActive($active); return $routeObj; } catch (\ReflectionException $exception) { $this->createError("Error instantiating route class: " . $exception->getMessage()); return null; } } } } namespace Core\API\Routes { use Core\API\Parameter\Parameter; use Core\API\Parameter\StringType; use Core\API\RoutesAPI; use Core\Objects\Context; use Core\Objects\DatabaseEntity\Group; use Core\Objects\DatabaseEntity\Route; use Core\Objects\Router\ApiRoute; use Core\Objects\Router\EmptyRoute; use Core\Objects\Router\Router; class Fetch extends RoutesAPI { public function __construct(Context $context, $externalCall = false) { parent::__construct($context, $externalCall, array()); } public function _execute(): bool { $sql = $this->context->getSQL(); $routes = Route::findAll($sql); $this->lastError = $sql->getLastError(); $this->success = ($routes !== FALSE); if ($this->success) { $this->result["routes"] = $routes; } return $this->success; } public static function getDescription(): string { return "Allows users to fetch site routing."; } public static function getDefaultPermittedGroups(): array { return [Group::ADMIN]; } } class Get extends RoutesAPI { public function __construct(Context $context, $externalCall = false) { parent::__construct($context, $externalCall, [ "id" => new Parameter("id", Parameter::TYPE_INT) ]); } public function _execute(): bool { $sql = $this->context->getSQL(); $routeId = $this->getParam("id"); $route = Route::find($sql, $routeId); $this->lastError = $sql->getLastError(); $this->success = ($route !== FALSE); if ($this->success) { if ($route === null) { return $this->createError("Route not found"); } else { $this->result["route"] = $route; } } return $this->success; } public static function getDescription(): string { return "Allows users to fetch a single route"; } public static function getDefaultPermittedGroups(): array { return [Group::ADMIN, Group::MODERATOR]; } } class Add extends RoutesAPI { public function __construct(Context $context, bool $externalCall = false) { parent::__construct($context, $externalCall, array( "pattern" => new StringType("pattern", 128), "type" => new StringType("type"), "target" => new StringType("target", 128), "extra" => new StringType("extra", 64, true, ""), "exact" => new Parameter("exact", Parameter::TYPE_BOOLEAN), "active" => new Parameter("active", Parameter::TYPE_BOOLEAN, true, true), )); } public function _execute(): bool { $pattern = $this->getParam("pattern"); $type = $this->getParam("type"); $target = $this->getParam("target"); $extra = $this->getParam("extra"); $exact = $this->getParam("exact"); $active = $this->getParam("active"); $route = $this->createRoute($type, $pattern, $target, $extra, $exact, $active); if ($route === null) { return false; } $sql = $this->context->getSQL(); $this->success = $route->save($sql) !== false; $this->lastError = $sql->getLastError(); if ($this->success) { $this->result["routeId"] = $route->getId(); } return $this->success && $this->regenerateCache(); } public static function getDescription(): string { return "Allows users to add new routes"; } public static function getDefaultPermittedGroups(): array { return [Group::ADMIN]; } } class Update extends RoutesAPI { public function __construct(Context $context, bool $externalCall = false) { parent::__construct($context, $externalCall, array( "id" => new Parameter("id", Parameter::TYPE_INT), "pattern" => new StringType("pattern", 128), "type" => new StringType("type"), "target" => new StringType("target", 128), "extra" => new StringType("extra", 64, true, ""), "exact" => new Parameter("exact", Parameter::TYPE_BOOLEAN), "active" => new Parameter("active", Parameter::TYPE_BOOLEAN, true, true), )); } public function _execute(): bool { $id = $this->getParam("id"); $sql = $this->context->getSQL(); $route = Route::find($sql, $id); if ($route === false) { return $this->createError("Error fetching route: " . $sql->getLastError()); } else if ($route === null) { return $this->createError("Route not found"); } $target = $this->getParam("target"); $extra = $this->getParam("extra"); $type = $this->getParam("type"); $pattern = $this->getParam("pattern"); $exact = $this->getParam("exact"); $active = $this->getParam("active"); if ($route->getType() !== $type) { if (!$route->delete($sql)) { return false; } else { $route = $this->createRoute($type, $pattern, $target, $extra, $exact, $active); if ($route === null) { return false; } } } else { $route->setPattern($pattern); $route->setActive($active); $route->setExtra($extra); $route->setTarget($target); $route->setExact($exact); } $this->success = $route->save($sql) !== false; $this->lastError = $sql->getLastError(); return $this->success && $this->regenerateCache(); } public static function getDescription(): string { return "Allows users to update existing routes"; } public static function getDefaultPermittedGroups(): array { return [Group::ADMIN]; } } class Remove extends RoutesAPI { public function __construct(Context $context, bool $externalCall = false) { parent::__construct($context, $externalCall, array( "id" => new Parameter("id", Parameter::TYPE_INT) )); } public function _execute(): bool { $sql = $this->context->getSQL(); $id = $this->getParam("id"); $route = Route::find($sql, $id); if ($route === false) { return $this->createError("Error fetching route: " . $sql->getLastError()); } else if ($route === null) { return $this->createError("Route not found"); } else if ($route instanceof ApiRoute) { return $this->createError("This route cannot be deleted"); } $this->success = $route->delete($sql) !== false; $this->lastError = $sql->getLastError(); return $this->success && $this->regenerateCache(); } public static function getDescription(): string { return "Allows users to remove routes"; } public static function getDefaultPermittedGroups(): array { return [Group::ADMIN]; } } class Enable extends RoutesAPI { public function __construct(Context $context, bool $externalCall = false) { parent::__construct($context, $externalCall, array( "id" => new Parameter("id", Parameter::TYPE_INT) )); } public function _execute(): bool { $id = $this->getParam("id"); return $this->toggleRoute($id, true); } public static function getDescription(): string { return "Allows users to enable a route"; } public static function getDefaultPermittedGroups(): array { return [Group::ADMIN]; } } class Disable extends RoutesAPI { public function __construct(Context $context, bool $externalCall = false) { parent::__construct($context, $externalCall, array( "id" => new Parameter("id", Parameter::TYPE_INT) )); } public function _execute(): bool { $id = $this->getParam("id"); return $this->toggleRoute($id, false); } public static function getDescription(): string { return "Allows users to disable a route"; } public static function getDefaultPermittedGroups(): array { return [Group::ADMIN]; } } class GenerateCache extends RoutesAPI { private ?Router $router; public function __construct(Context $context, bool $externalCall = false) { parent::__construct($context, $externalCall, []); $this->router = null; } protected function _execute(): bool { $sql = $this->context->getSQL(); $routes = Route::findBy(Route::createBuilder($sql, false) ->whereTrue("active") ->orderBy("id") ->ascending()); $this->success = $routes !== false; $this->lastError = $sql->getLastError(); if (!$this->success) { return false; } $this->router = new Router($this->context); foreach ($routes as $route) { $this->router->addRoute($route); } $this->success = $this->router->writeCache($this->routerCachePath); if (!$this->success) { return $this->createError("Error saving router cache file: " . $this->routerCachePath); } return $this->success; } public function getRouter(): ?Router { return $this->router; } public static function getDescription(): string { return "Allows users to regenerate the routing cache"; } public static function getDefaultPermittedGroups(): array { return [Group::ADMIN, Group::SUPPORT]; } } class Check extends RoutesAPI { public function __construct(Context $context, bool $externalCall) { parent::__construct($context, $externalCall, [ "pattern" => new StringType("pattern", 128), "path" => new StringType("path"), "exact" => new Parameter("exact", Parameter::TYPE_BOOLEAN, true, true) ]); } protected function _execute(): bool { $path = $this->getParam("path"); $pattern = $this->getParam("pattern"); $exact = $this->getParam("exact"); $route = new EmptyRoute($pattern, $exact, ""); $this->result["match"] = $route->match($path); return $this->success; } public static function getDescription(): string { return "This endpoint grants the ability to check, if a route pattern is matched with the given path for debugging purposes"; } public static function getDefaultPermittedGroups(): array { return [Group::ADMIN, Group::MODERATOR]; } } }