Skip to content

Commit 5fcc97b

Browse files
committed
api module added
1 parent 95da125 commit 5fcc97b

34 files changed

+872
-40
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,7 @@ if `... did not complete successfully: exit code: 100`
6868
```bash
6969
$ docker image prune -f
7070
```
71+
72+
# TODO
73+
74+
* [switch CI to github workflow](https://www.strangebuzz.com/en/blog/setting-a-ci-cd-workflow-for-a-symfony-project-thanks-to-the-github-actions)

src/app/Bootstrap.php

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22

33
namespace App;
44

5-
use DomainException;
5+
use App\Shared\Listeners\ImplicitViewListener;
66
use Phalcon\Di\Di;
77
use Phalcon\Di\FactoryDefault;
8+
use Phalcon\Events\Manager;
89
use Phalcon\Support\Registry;
910

1011
class Bootstrap
@@ -23,6 +24,11 @@ public function __construct(bool $autoDetect = true)
2324
$this->_app = $isCli ? new CliApplication($container) : new WebApplication($container);
2425
$this->_app->registerServices($container);
2526

27+
/** @var Manager $eventsManager */
28+
$eventsManager = $container->getShared('eventsManager');
29+
$eventsManager->attach('application', new ImplicitViewListener());
30+
$this->_app->setEventsManager($eventsManager);
31+
2632
$registry = new Registry();
2733
$registry->set('modules', $this->_app->getModules());
2834
$container->set('registry', $registry);
@@ -47,7 +53,7 @@ public function identify(): string
4753

4854
if (is_numeric($tld)) { // if an IP requested
4955
return $_SERVER['REQUEST_METHOD'] === 'GET' ?
50-
throw new DomainException('Request a domain name instead of IP') : env('DEFAULT_DOMAIN');
56+
throw new \DomainException('Request a domain name instead of IP') : env('DEFAULT_DOMAIN');
5157
}
5258
return $domain;
5359
}

src/app/Config/Main.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?php
22

33
return [
4-
'debug' => env('APP_DEBUG'),
4+
'debug' => (bool) env('APP_DEBUG'),
55
'app' => [
66
'locale' => 'de_DE',
77
'domain' => env('APP_DOMAIN'),

src/app/Micro.php

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
use App\Shared\ExceptionDto;
44
use App\Shared\Micro;
5+
use App\Shared\Notification;
56
use App\Shared\Simple;
67
use App\Shared\Volt;
78
use Phalcon\Config\Adapter\Php;
@@ -44,19 +45,14 @@
4445
'separator' => '_',
4546
]);
4647
return $volt;
47-
}
48+
},
4849
]);
4950
$view->setVar('config', $config);
5051
$view->setVar('site', $site);
5152
return $view;
5253
});
5354

54-
/**
55-
* @param Micro $app
56-
* @param array<int, \Throwable> $exceptions
57-
* @return string
58-
*/
59-
$output = static function(Micro $app, array $exceptions) {
55+
$output = static function(Micro $app, array $exceptions, bool $renderTemplate = true) {
6056
$code = ResponseStatusCodeInterface::STATUS_INTERNAL_SERVER_ERROR;
6157
if ($exceptions[0] instanceof \DomainException) {
6258
$code = ResponseStatusCodeInterface::STATUS_FORBIDDEN;
@@ -68,18 +64,32 @@
6864
foreach ($exceptions as $exception) {
6965
$storage->attach(new ExceptionDto($exception::class, $exception->getMessage()));
7066
}
71-
/** @var Simple $view */
72-
$view = $app->view;
73-
return $view->render('error', [
74-
'errCode' => $code,
75-
'exceptionData' => $storage,
76-
]);
67+
if ($renderTemplate) {
68+
/** @var Simple $view */
69+
$view = $app->view;
70+
return $view->render('error', [
71+
'errCode' => $code,
72+
'exceptionData' => $storage,
73+
]);
74+
}
75+
$exceptionMessages = [];
76+
/** @var ExceptionDto $dto */
77+
foreach ($storage as $dto) {
78+
$exceptionMessages[] = Notification::failure($dto->message);
79+
}
80+
$app->response->setJsonContent(
81+
$exceptionMessages,
82+
\JSON_NUMERIC_CHECK | \JSON_UNESCAPED_SLASHES | \JSON_UNESCAPED_UNICODE
83+
);
84+
return $app->response;
7785
};
7886

7987
/** @var \Throwable $e from the catch block where this file is included */
80-
$app->error(function(\Throwable $appException) use ($output, $app, $e) {
88+
$explicitView = $explicitView ?? true;
89+
90+
$app->error(function(\Throwable $appException) use ($output, $app, $e, $explicitView) {
8191
$exceptions = $appException->getMessage() === $e->getMessage() ? [$e] : [$appException, $e];
82-
echo $output($app, $exceptions);
92+
echo $output($app, $exceptions, $explicitView);
8393
});
8494
$app->notFound(function() use ($output, $app, $e) { echo $output($app, [$e]); });
8595

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
namespace App\Modules\Api\Attributes;
4+
5+
#[\Attribute(\Attribute::TARGET_METHOD)]
6+
class ApiAuth
7+
{
8+
public function __construct(public ?string $type = null) {}
9+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
namespace App\Modules\Api\Components;
4+
5+
use Phalcon\Http\Request;
6+
7+
class ApiAuthFactory
8+
{
9+
public static function create(string $type = 'bearer'): ApiAuthInterface
10+
{
11+
$className = match (strtolower($type))
12+
{
13+
// ...
14+
default => Bearer::class,
15+
};
16+
return new $className;
17+
}
18+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
namespace App\Modules\Api\Components;
4+
5+
use Phalcon\Http\Request;
6+
7+
interface ApiAuthInterface
8+
{
9+
public function from(Request $request): static;
10+
public function getToken(): string;
11+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
namespace App\Modules\Api\Components;
4+
5+
class ApiHandler
6+
{
7+
public function __construct(private ?ApiAuthInterface $tokenHandler = null)
8+
{
9+
// ...
10+
}
11+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
namespace App\Modules\Api\Components;
4+
5+
use App\Modules\Api\Exceptions\HttpClientException;
6+
use Phalcon\Http\Message\ResponseStatusCodeInterface as StatusCode;
7+
use Phalcon\Http\Request;
8+
9+
class Bearer implements ApiAuthInterface
10+
{
11+
private ?string $token = null;
12+
13+
/**
14+
* @throws HttpClientException
15+
*/
16+
public function from(Request $request): static
17+
{
18+
$authKey = 'Authorization'; // Authorization: Bearer xjhgjhgkjdgfjdhgfjdgfjdg
19+
$authHeader = $request->hasHeader($authKey) ? trim($request->getHeader($authKey)) : null;
20+
if (! $authHeader) {
21+
throw new HttpClientException(StatusCode::STATUS_UNAUTHORIZED);
22+
}
23+
[$tokenType, $this->token] = explode(' ', $authHeader);
24+
if (strtolower($tokenType) !== 'bearer') {
25+
throw new HttpClientException(StatusCode::STATUS_UNAUTHORIZED);
26+
}
27+
}
28+
29+
public function getToken(): string
30+
{
31+
return $this->token;
32+
}
33+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
3+
namespace App\Modules\Api\Components;
4+
5+
use App\Shared\Http\Status;
6+
7+
class ErrorContent implements \JsonSerializable
8+
{
9+
/** @var array<string, mixed> */
10+
protected array $_schema = [];
11+
12+
public function __construct(
13+
private int $statusCode,
14+
private readonly ?string $message = null
15+
) {
16+
$httpStatus = Status::tryFrom($this->statusCode);
17+
if (! $httpStatus) {
18+
$this->statusCode = Status::INTERNAL_SERVER_ERROR->value;
19+
$httpStatus = Status::tryFrom($this->statusCode);
20+
}
21+
$this->_schema = [
22+
'code' => $this->statusCode,
23+
'message' => $this->message ?? $httpStatus->message(),
24+
];
25+
}
26+
27+
public function setParam(string $key, mixed $message): self
28+
{
29+
$this->_schema[$key] = $message;
30+
return $this;
31+
}
32+
33+
public function setStatusMessage(string $message): self
34+
{
35+
return $this->setParam('message', $message);
36+
}
37+
38+
public function setDebugMessage(string $message): self
39+
{
40+
return $this->setParam('debug', $message);
41+
}
42+
43+
public function get(): array
44+
{
45+
return $this->_schema;
46+
}
47+
48+
public function jsonSerialize(): array
49+
{
50+
return $this->get();
51+
}
52+
}

0 commit comments

Comments
 (0)