This commit is contained in:
Elias Kleppinger 2024-11-09 19:09:39 +01:00
parent d46d8e8463
commit d6d4be762a
No known key found for this signature in database
GPG Key ID: 1B7744E660C00214
27 changed files with 922 additions and 25 deletions

9
README.md Normal file
View File

@ -0,0 +1,9 @@
# versa digitale
*versa digitale* is an modern high-performance application platform, with an modular development approach.
The platform has the following features:
- HTTP Server written with ReactPHP
- high-performant async handling of requests
- Caching
-

View File

@ -3,7 +3,6 @@
"description": "Anwendungsserver der modularen versa digitale Anwendungsplatform",
"type": "platform",
"license": "MIT",
"version": "1.0",
"autoload": {
"psr-4": {
"VersaDigitale\\Platform\\": "src/"
@ -19,6 +18,16 @@
"symfony/console": "^7.1",
"react/http": "^3@dev",
"react/async": "^3@dev",
"secondtruth/phar-compiler": "^1.2"
"secondtruth/phar-compiler": "^1.2",
"symfony/http-kernel": "^7.1",
"symfony/http-foundation": "^7.1",
"doctrine/orm": "^3.3",
"doctrine/dbal": "^4.2",
"symfony/cache": "^7.1",
"nilportugues/sql-query-builder": "^1.8",
"bramus/router": "^1.6",
"symfony/routing": "^7.1",
"symfony/psr-http-message-bridge": "^7.1",
"nyholm/psr7": "^1.8"
}
}

View File

@ -0,0 +1,15 @@
<?php
use VersaDigitale\Platform\Logging\Log;
use VersaDigitale\Platform\Module\AbstractModule;
use VersaDigitale\Platform\Module\Module;
class DashboardModule extends AbstractModule
{
public function init(): void
{
// TODO: Implement init() method.
Log::info("Hallo aus dem Dashboard-Modul!");
}
}

View File

@ -0,0 +1,4 @@
{
"host": "0.0.0.0",
"port": 8080
}

View File

@ -0,0 +1,84 @@
<?php
namespace VersaDigitale\Platform\Auth;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use VersaDigitale\Platform\Database\Model\BaseModel;
#[ORM\Entity]
#[ORM\Table(name: 'permission_nodes')]
class PermissionNode extends BaseModel
{
#[ORM\Column(type: 'string', length: 255)]
private string $name;
#[ORM\ManyToOne(targetEntity: self::class, inversedBy: 'children')]
private ?self $parent = null;
#[ORM\OneToMany(targetEntity: self::class, mappedBy: 'parent')]
private Collection $children;
#[ORM\ManyToMany(targetEntity: UserGroup::class, mappedBy: 'permissions')]
private Collection $userGroups;
#[ORM\ManyToMany(targetEntity: User::class, mappedBy: 'permissions')]
private Collection $users;
public function __construct(string $name, ?self $parent = null)
{
$this->name = $name;
$this->parent = $parent;
$this->children = new ArrayCollection();
$this->userGroups = new ArrayCollection();
$this->users = new ArrayCollection();
parent::__construct();
}
public function getId(): int
{
return $this->id;
}
public function getName(): string
{
return $this->name;
}
public function getParent(): ?self
{
return $this->parent;
}
public function getChildren(): Collection
{
return $this->children;
}
public function addChild(PermissionNode $child): void
{
if (!$this->children->contains($child)) {
$this->children->add($child);
$child->parent = $this;
}
}
public function removeChild(PermissionNode $child): void
{
if ($this->children->removeElement($child)) {
$child->parent = null;
}
}
public function getUserGroups(): Collection
{
return $this->userGroups;
}
public function getUsers(): Collection
{
return $this->users;
}
}

31
src/Auth/Session.php Normal file
View File

@ -0,0 +1,31 @@
<?php
namespace VersaDigitale\Platform\Auth;
class Session
{
private string $authToken = "";
private User $user;
public function __construct(User $user) {
$this->user = $user;
$authToken = md5(uniqid(rand(), true));
}
public function getAuthToken(): string
{
return $this->authToken;
}
/**
* @return User
*/
public function getUser(): User
{
return $this->user;
}
}

View File

@ -0,0 +1,31 @@
<?php
namespace VersaDigitale\Platform\Auth;
class SessionManager
{
/**
* @var Session[]
*/
private static array $sessions = [];
public static function getSession(string $authToken): ?Session
{
foreach (self::$sessions as $session) {
if ($session->getAuthToken() === $authToken) {
return $session;
}
}
}
public static function createSession(User $user): Session
{
$session = new Session($user);
self::$sessions[] = $session;
return $session;
}
}

181
src/Auth/User.php Normal file
View File

@ -0,0 +1,181 @@
<?php
namespace VersaDigitale\Platform\Auth;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use VersaDigitale\Platform\Database\Model\BaseModel;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\DBAL\Types\Types;
use DateTimeImmutable;
use Exception;
#[ORM\Entity]
#[ORM\Table(name: 'users')]
class User extends BaseModel
{
#[ORM\Column(type: Types::STRING, length: 180, unique: true)]
private string $username;
#[ORM\Column(type: Types::STRING, length: 255)]
private string $email;
#[ORM\Column(type: Types::STRING)]
private string $password;
#[ORM\Column(type: Types::BOOLEAN)]
private bool $isActive = true;
#[ORM\Column(type: Types::DATETIME_IMMUTABLE, nullable: true)]
private ?DateTimeImmutable $lastLoginAt = null;
#[ORM\ManyToMany(targetEntity: UserGroup::class, inversedBy: 'users')]
#[ORM\JoinTable(name: 'user_user_groups')]
private Collection $userGroups;
#[ORM\ManyToMany(targetEntity: PermissionNode::class, inversedBy: 'users')]
#[ORM\JoinTable(name: 'user_permissions')]
private Collection $permissions;
public function __construct(string $username, string $email, string $password)
{
$this->username = $username;
$this->email = $email;
$this->password = $this->hashPassword($password);
$this->userGroups = new ArrayCollection();
$this->permissions = new ArrayCollection();
parent::__construct();
}
public function getUsername(): string
{
return $this->username;
}
public function setUsername(string $username): void
{
$this->username = $username;
}
public function getEmail(): string
{
return $this->email;
}
public function setEmail(string $email): void
{
$this->email = $email;
}
public function getPassword(): string
{
return $this->password;
}
public function setPassword(string $password): void
{
$this->password = $this->hashPassword($password);
}
public function isActive(): bool
{
return $this->isActive;
}
public function activate(): void
{
$this->isActive = true;
}
public function deactivate(): void
{
$this->isActive = false;
}
public function getLastLoginAt(): ?DateTimeImmutable
{
return $this->lastLoginAt;
}
public function setLastLoginAt(DateTimeImmutable $lastLoginAt): void
{
$this->lastLoginAt = $lastLoginAt;
}
public function login(): void
{
$this->lastLoginAt = new DateTimeImmutable();
$this->updateTimestamps();
}
public function getUserGroups(): Collection
{
return $this->userGroups;
}
public function addUserGroup(UserGroup $group): void
{
if (!$this->userGroups->contains($group)) {
$this->userGroups->add($group);
}
}
public function removeUserGroup(UserGroup $group): void
{
$this->userGroups->removeElement($group);
}
public function getPermissions(): Collection
{
return $this->permissions;
}
public function addPermission(PermissionNode $permission): void
{
if (!$this->permissions->contains($permission)) {
$this->permissions->add($permission);
}
}
public function removePermission(PermissionNode $permission): void
{
$this->permissions->removeElement($permission);
}
public function getEffectivePermissions(): Collection
{
$effectivePermissions = new ArrayCollection();
// Direct permissions
foreach ($this->permissions as $permission) {
if (!$effectivePermissions->contains($permission)) {
$effectivePermissions->add($permission);
}
}
// Group permissions
foreach ($this->userGroups as $group) {
foreach ($group->getPermissions() as $permission) {
if (!$effectivePermissions->contains($permission)) {
$effectivePermissions->add($permission);
}
}
}
return $effectivePermissions;
}
private function hashPassword(string $password): string
{
if (strlen($password) < 8) {
throw new Exception('Password must be at least 8 characters long.');
}
return password_hash($password, PASSWORD_BCRYPT);
}
public function verifyPassword(string $password): bool
{
return password_verify($password, $this->password);
}
}

67
src/Auth/UserGroup.php Normal file
View File

@ -0,0 +1,67 @@
<?php
namespace VersaDigitale\Platform\Auth;
use VersaDigitale\Platform\Database\Model\BaseModel;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
#[ORM\Entity]
#[ORM\Table(name: 'user_groups')]
class UserGroup extends BaseModel
{
#[ORM\Column(type: 'string', length: 255)]
private string $name;
#[ORM\ManyToMany(targetEntity: PermissionNode::class, inversedBy: 'userGroups')]
#[ORM\JoinTable(name: 'user_group_permissions')]
private Collection $permissions;
#[ORM\ManyToMany(targetEntity: User::class, mappedBy: 'userGroups')]
private Collection $users;
public function __construct(string $name)
{
$this->name = $name;
$this->permissions = new ArrayCollection();
$this->users = new ArrayCollection();
parent::__construct();
}
public function getId(): int
{
return $this->id;
}
public function getName(): string
{
return $this->name;
}
public function getPermissions(): Collection
{
return $this->permissions;
}
public function addPermission(PermissionNode $permission): void
{
if (!$this->permissions->contains($permission)) {
$this->permissions->add($permission);
}
}
public function removePermission(PermissionNode $permission): void
{
$this->permissions->removeElement($permission);
}
public function getUsers(): Collection
{
return $this->users;
}
}

View File

@ -6,6 +6,7 @@ use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use VersaDigitale\Platform\Kernel\VersaKernel;
use VersaDigitale\Platform\Logging\Log;
use VersaDigitale\Platform\Server\VersaServer;
@ -23,12 +24,7 @@ class StartServerCommand extends Command
// Deine Logik für den Compiler
$ver = phpversion();
Log::info("Starte den versa digitale Platformserver - powered by ReactPHP");
$server = new VersaServer(
host: "0.0.0.0",
port: 8080
);
$server->start();
VersaKernel::__startServer();
return Command::SUCCESS;
}

View File

@ -0,0 +1,63 @@
<?php
namespace VersaDigitale\Platform\Console\Commands\User;
use Symfony\Component\Console\Attribute\AsCommand;
use VersaDigitale\Platform\Auth\User;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use VersaDigitale\Platform\Database\VersaBase;
#[AsCommand(name: "user:create")]
class CreateUserCommand extends Command
{
private EntityManagerInterface $entityManager;
public function __construct()
{
$this->entityManager = VersaBase::$em;
parent::__construct();
}
protected function configure()
{
$this
->setDescription('Creates a new user')
->setHelp('This command allows you to create a user...')
->addArgument('username', InputArgument::REQUIRED, 'The username of the user')
->addArgument('email', InputArgument::REQUIRED, 'The email of the user')
->addArgument('password', InputArgument::REQUIRED, 'The password of the user');
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
// Retrieve input arguments
$username = $input->getArgument('username');
$email = $input->getArgument('email');
$password = $input->getArgument('password');
try {
// Create new user instance
$user = new User($username, $email, $password);
// Persist the user to the database
$this->entityManager->persist($user);
$this->entityManager->flush();
$io->success('User created successfully.');
return Command::SUCCESS;
} catch (\Exception $e) {
$io->error('Error creating user: ' . $e->getMessage());
return Command::FAILURE;
}
}
}

View File

@ -7,6 +7,8 @@ use Symfony\Component\Console\Command\ListCommand;
use VersaDigitale\Platform\Console\Commands\Development\CompilePlatformCommand;
use VersaDigitale\Platform\Console\Commands\Module\CompileModuleCommand;
use VersaDigitale\Platform\Console\Commands\Server\StartServerCommand;
use VersaDigitale\Platform\Console\Commands\User\CreateUserCommand;
use VersaDigitale\Platform\Kernel\VersaKernel;
/**
* Class VersaCLI
@ -22,19 +24,6 @@ class VersaCLI {
public ?ConsoleApplication $application = null
)
{
$this->application = new ConsoleApplication();
$this->application->addCommands([
new CompilePlatformCommand(),
new CompileModuleCommand(),
new StartServerCommand()
]);
}
public function run(): void
{
// Output pink text to stdout using fwrite
$logo = <<<LOGO
_ _ _ _ _
@ -47,6 +36,24 @@ class VersaCLI {
fwrite(STDOUT, "\033[35m". $logo. "\033[0m\n\n");
VersaKernel::__initKernel();
$this->application = new ConsoleApplication();
$this->application->addCommands([
new CompilePlatformCommand(),
new CompileModuleCommand(),
new StartServerCommand(),
new CreateUserCommand()
]);
}
public function run(): void
{
// Output pink text to stdout using fwrite
$this->application->run();
}

View File

@ -0,0 +1,49 @@
<?php
namespace VersaDigitale\Platform\Database\Model;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\DBAL\Types\Types;
use DateTimeImmutable;
use DateTimeInterface;
#[ORM\MappedSuperclass]
abstract class BaseModel
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: Types::INTEGER)]
protected ?int $id = null;
#[ORM\Column(type: Types::DATETIME_IMMUTABLE)]
protected DateTimeInterface $createdAt;
#[ORM\Column(type: Types::DATETIME_IMMUTABLE)]
protected DateTimeInterface $updatedAt;
public function __construct()
{
$this->createdAt = new DateTimeImmutable();
$this->updatedAt = new DateTimeImmutable();
}
public function getId(): ?int
{
return $this->id;
}
public function getCreatedAt(): DateTimeInterface
{
return $this->createdAt;
}
public function getUpdatedAt(): DateTimeInterface
{
return $this->updatedAt;
}
public function updateTimestamps(): void
{
$this->updatedAt = new DateTimeImmutable();
}
}

View File

@ -0,0 +1,52 @@
<?php
namespace VersaDigitale\Platform\Database;
use Doctrine\DBAL\DriverManager;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\ORMSetup;
use Doctrine\ORM\Tools\SchemaTool;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpFoundation\Session\Storage\Handler\RedisSessionHandler;
use VersaDigitale\Platform\Logging\Log;
class VersaBase
{
public static EntityManager $em;
public static function __init(): void
{
$config = ORMSetup::createAttributeMetadataConfiguration(
paths: [__DIR__ . "/../"],
isDevMode: true
);
$connection = DriverManager::getConnection([
'driver' => 'pdo_sqlite',
'path' => __DIR__ . '/../../db.sqlite',
], $config);
// obtaining the entity manager
self::$em = new EntityManager($connection, $config);
Log::info("Doctrine ORM geladen!");
self::checkModels();
}
public static function checkModels() {
$sm = self::$em->getConnection()->createSchemaManager();
$models = self::$em->getMetadataFactory()->getAllMetadata();
$schemaTool = new SchemaTool(self::$em);
Log::info(count($models) . " Modelle gefunden");
foreach($models as $model) {
if(!$sm->tableExists($model->getTableName()) and !$model->isMappedSuperclass) {
$schemaTool->createSchema([$model]);
Log::info("Modell \"" . $model->getTableName() . "\" erstellt!");
}
}
}
}

View File

@ -0,0 +1,14 @@
<?php
namespace VersaDigitale\Platform\Kernel\Attributes;
use Attribute;
#[Attribute(Attribute::TARGET_METHOD)]
class Route
{
public function __construct(
public string $path,
public string $method = 'GET'
) {}
}

View File

@ -0,0 +1,8 @@
<?php
namespace VersaDigitale\Platform\Kernel\Config;
class PlatformConfig
{
}

View File

@ -0,0 +1,8 @@
<?php
namespace VersaDigitale\Platform\Kernel;
class Controller
{
}

View File

@ -0,0 +1,8 @@
<?php
namespace VersaDigitale\Platform\Kernel\Http;
class Request extends \Symfony\Component\HttpFoundation\Request
{
}

View File

@ -0,0 +1,8 @@
<?php
namespace VersaDigitale\Platform\Kernel\Http;
class Response extends \Symfony\Component\HttpFoundation\Response
{
}

View File

@ -0,0 +1,19 @@
<?php
namespace VersaDigitale\Platform\Kernel\InternalModule\Controller;
use VersaDigitale\Platform\Kernel\Attributes\Route;
use VersaDigitale\Platform\Kernel\Http\Request;
use VersaDigitale\Platform\Kernel\Http\Response;
use VersaDigitale\Platform\Logging\Log;
class InternalAuthController
{
#[Route(path: "/", method: "GET")]
public function index(Request $request) {
Log::info("Test");
return new Response("Ok!");
}
}

View File

@ -0,0 +1,20 @@
<?php
namespace VersaDigitale\Platform\Kernel\InternalModule;
use VersaDigitale\Platform\Kernel\InternalModule\Controller\InternalAuthController;
use VersaDigitale\Platform\Module\AbstractModule;
class InternalModule extends AbstractModule
{
public function __construct()
{
parent::__construct("internal", "Internes Modul der versa digitale Platform.");
}
public function init(): void
{
$this->registerController(InternalAuthController::class);
}
}

124
src/Kernel/VersaKernel.php Normal file
View File

@ -0,0 +1,124 @@
<?php
namespace VersaDigitale\Platform\Kernel;
use Bramus\Router\Router;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface;
use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface;
use Symfony\Component\HttpKernel\HttpKernel;
use Symfony\Component\Routing\Matcher\UrlMatcher;
use Symfony\Component\Routing\RequestContext;
use Symfony\Component\Routing\RouteCollection;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
use VersaDigitale\Platform\Database\VersaBase;
use VersaDigitale\Platform\Kernel\Attributes\Route;
use VersaDigitale\Platform\Kernel\Http\Request;
use VersaDigitale\Platform\Kernel\Http\Response;
use VersaDigitale\Platform\Kernel\InternalModule\InternalModule;
use VersaDigitale\Platform\Logging\Log;
use VersaDigitale\Platform\Module\ModuleManager;
use VersaDigitale\Platform\Server\VersaServer;
class VersaKernel
{
private static VersaServer $server;
private static bool $isInitialized = false;
private static RouteCollection $routes;
/**
* @var string[]
*/
public static array $controllerClasses;
public static function __initKernel(): void
{
if(!self::$isInitialized) {
VersaBase::__init();
self::$routes = new RouteCollection();
ModuleManager::addModule(new InternalModule());
ModuleManager::initModules();
foreach(self::$controllerClasses as $class) {
self::__registerControllerRoutes($class);
}
self::$isInitialized = true;
}
}
public static function __registerControllerRoutes(string $controllerClass)
{
try {
$controllerReflection = new \ReflectionClass($controllerClass);
foreach ($controllerReflection->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
$attributes = $method->getAttributes(Route::class);
foreach ($attributes as $attribute) {
/** @var Route $route */
$route = $attribute->newInstance();
// Create a Symfony route
$symfonyRoute = new \Symfony\Component\Routing\Route(
$route->path,
['_controller' => [$controllerClass, $method->getName()]]
);
$symfonyRoute->setMethods([$route->method]);
// Add the route to the RouteCollection
self::$routes->add($route->path, $symfonyRoute);
}
}
} catch(\Exception $e) {
Log::error("Error while registering controller routes: ".$e->getMessage());
}
}
public static function __handleRequest(Request $request): Response
{
try {
$context = new RequestContext();
$context->fromRequest($request);
$matcher = new UrlMatcher(self::$routes, $context);
Log::info("Incoming " . $request->getMethod() . " request for path " . $request->getPathInfo());
try {
$routeInfo = $matcher->match($request->getPathInfo());
[$controllerClass, $method] = $routeInfo['_controller'];
unset($routeInfo['_controller']); // Remove the controller key from params
// Call the controller and get the response
$controller = new $controllerClass();
Log::info("Found controller " . $controllerClass . " with method " . $method);
return $controller->$method($request);
} catch (\Symfony\Component\Routing\Exception\ResourceNotFoundException $e) {
return new Response('404 Not Found', 404);
} catch (\Symfony\Component\Routing\Exception\MethodNotAllowedException $e) {
return new Response('405 Method Not Allowed', 405);
}
} catch(\Exception $e) {
return new Response($e->getTraceAsString(), 500);
}
}
public static function __startServer(string $host = "0.0.0.0", int $port = 8080) {
if(!self::$isInitialized) {
self::__initKernel();
}
self::$server = new VersaServer(
host: $host,
port: $port,
);
self::$server->start();
}
}

View File

@ -0,0 +1,10 @@
<?php
namespace VersaDigitale\Platform\Module;
abstract class AbstractModule extends Module
{
public abstract function init(): void;
}

21
src/Module/Module.php Normal file
View File

@ -0,0 +1,21 @@
<?php
namespace VersaDigitale\Platform\Module;
use VersaDigitale\Platform\Kernel\VersaKernel;
use VersaDigitale\Platform\Module\AbstractModule;
class Module
{
public function __construct(
public string $name,
public string $description,
)
{
}
protected function registerController(string $class) {
VersaKernel::$controllerClasses[] = $class;
}
}

View File

@ -0,0 +1,32 @@
<?php
namespace VersaDigitale\Platform\Module;
use VersaDigitale\Platform\Logging\Log;
class ModuleManager
{
/**
* @var AbstractModule[]
*/
public static array $modules = [];
public static function __init(): void {
}
public static function addModule(AbstractModule $module): void {
self::$modules[] = $module;
Log::info("Modul \"" . $module->name . "\" geladen.");
}
public static function initModules(): void
{
foreach (self::$modules as $module) {
$module->init();
Log::info("Modul \"" . $module->name . "\" initialisiert.");
}
}
}

View File

@ -2,10 +2,15 @@
namespace VersaDigitale\Platform\Server;
use Nyholm\Psr7\Factory\Psr17Factory;
use Psr\Http\Message\ServerRequestInterface;
use React\Http\HttpServer;
use React\Http\Message\Response;
use React\Socket\SocketServer;
use Symfony\Bridge\PsrHttpMessage\Factory\HttpFoundationFactory;
use Symfony\Bridge\PsrHttpMessage\Factory\PsrHttpFactory;
use Symfony\Component\HttpFoundation\Request;
use VersaDigitale\Platform\Kernel\VersaKernel;
use VersaDigitale\Platform\Logging\Log;
class VersaServer
@ -21,12 +26,31 @@ class VersaServer
}
public function start(): void {
$this->http = new HttpServer(function (ServerRequestInterface $request) {
return Response::plaintext(
"Hello World!\n"
);
$psr17Factory = new Psr17Factory();
$psrHttpFactory = new PsrHttpFactory($psr17Factory, $psr17Factory, $psr17Factory, $psr17Factory);
$this->http = new HttpServer(function (ServerRequestInterface $request) use($psrHttpFactory) {
$httpFoundationFactory = new HttpFoundationFactory();
Log::info("Test");
try {
$symfonyRequest = $httpFoundationFactory->createRequest($request);
$versaRequest = new \VersaDigitale\Platform\Kernel\Http\Request($symfonyRequest->query->all(), $symfonyRequest->request->all(), $symfonyRequest->attributes->all(), $symfonyRequest->cookies->all(), $symfonyRequest->files->all(), $symfonyRequest->server->all(), $symfonyRequest->getContent());
$versaResponse = VersaKernel::__handleRequest($versaRequest);
return $psrHttpFactory->createResponse($versaResponse);
} catch(\Exception $e) {
return Response::plaintext($e->getTraceAsString())->withStatus(500);
}
});
$this->http->on("error", function(\Throwable $e) {
Log::error($e->getTraceAsString());
});
$this->socket = new SocketServer("{$this->host}:{$this->port}");
$this->socket->on("error", function(\Throwable $e) {
Log::error($e->getTraceAsString());
});
$this->http->listen($this->socket);
Log::info("versa digitale Platform erreichbar unter {$this->host}:{$this->port}");
}

3
versa.json Normal file
View File

@ -0,0 +1,3 @@
{
"base_dir": "example-app"
}