Vielll
This commit is contained in:
parent
d46d8e8463
commit
d6d4be762a
9
README.md
Normal file
9
README.md
Normal 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
|
||||
-
|
||||
@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
15
dashboard-module/src/DashboardModule.php
Normal file
15
dashboard-module/src/DashboardModule.php
Normal 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!");
|
||||
}
|
||||
}
|
||||
4
example-app/config/server.json
Normal file
4
example-app/config/server.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"host": "0.0.0.0",
|
||||
"port": 8080
|
||||
}
|
||||
84
src/Auth/PermissionNode.php
Normal file
84
src/Auth/PermissionNode.php
Normal 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
31
src/Auth/Session.php
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
31
src/Auth/SessionManager.php
Normal file
31
src/Auth/SessionManager.php
Normal 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
181
src/Auth/User.php
Normal 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
67
src/Auth/UserGroup.php
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
63
src/Console/Commands/User/CreateUserCommand.php
Normal file
63
src/Console/Commands/User/CreateUserCommand.php
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
|
||||
49
src/Database/Model/BaseModel.php
Normal file
49
src/Database/Model/BaseModel.php
Normal 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();
|
||||
}
|
||||
}
|
||||
52
src/Database/VersaBase.php
Normal file
52
src/Database/VersaBase.php
Normal 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!");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
14
src/Kernel/Attributes/Route.php
Normal file
14
src/Kernel/Attributes/Route.php
Normal 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'
|
||||
) {}
|
||||
}
|
||||
8
src/Kernel/Config/PlatformConfig.php
Normal file
8
src/Kernel/Config/PlatformConfig.php
Normal file
@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace VersaDigitale\Platform\Kernel\Config;
|
||||
|
||||
class PlatformConfig
|
||||
{
|
||||
|
||||
}
|
||||
8
src/Kernel/Controller.php
Normal file
8
src/Kernel/Controller.php
Normal file
@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace VersaDigitale\Platform\Kernel;
|
||||
|
||||
class Controller
|
||||
{
|
||||
|
||||
}
|
||||
8
src/Kernel/Http/Request.php
Normal file
8
src/Kernel/Http/Request.php
Normal file
@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace VersaDigitale\Platform\Kernel\Http;
|
||||
|
||||
class Request extends \Symfony\Component\HttpFoundation\Request
|
||||
{
|
||||
|
||||
}
|
||||
8
src/Kernel/Http/Response.php
Normal file
8
src/Kernel/Http/Response.php
Normal file
@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace VersaDigitale\Platform\Kernel\Http;
|
||||
|
||||
class Response extends \Symfony\Component\HttpFoundation\Response
|
||||
{
|
||||
|
||||
}
|
||||
@ -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!");
|
||||
}
|
||||
|
||||
}
|
||||
20
src/Kernel/InternalModule/InternalModule.php
Normal file
20
src/Kernel/InternalModule/InternalModule.php
Normal 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
124
src/Kernel/VersaKernel.php
Normal 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();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
10
src/Module/AbstractModule.php
Normal file
10
src/Module/AbstractModule.php
Normal 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
21
src/Module/Module.php
Normal 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;
|
||||
}
|
||||
}
|
||||
32
src/Module/ModuleManager.php
Normal file
32
src/Module/ModuleManager.php
Normal 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.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -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
3
versa.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"base_dir": "example-app"
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user