Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
File renamed without changes.
3 changes: 3 additions & 0 deletions src/App/src/ConfigProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@
use Light\App\Factory\FeedGeneratorFactory;
use Light\App\Factory\GetFeedViewHandlerFactory;
use Light\App\Factory\GetIndexViewHandlerFactory;
use Light\App\Factory\GetMarkdownArticleHandlerFactory;
use Light\App\Handler\GetFeedViewHandler;
use Light\App\Handler\GetIndexViewHandler;
use Light\App\Handler\GetMarkdownArticleHandler;
use Light\App\Resolver\EntityListenerResolver;
use Light\App\Service\FeedGenerator;
use Mezzio\Application;
Expand Down Expand Up @@ -114,6 +116,7 @@ public function getDependencies(): array
EntityListenerResolver::class => EntityListenerResolverFactory::class,
GetIndexViewHandler::class => GetIndexViewHandlerFactory::class,
GetFeedViewHandler::class => GetFeedViewHandlerFactory::class,
GetMarkdownArticleHandler::class => GetMarkdownArticleHandlerFactory::class,
FeedGenerator::class => FeedGeneratorFactory::class,
],
'aliases' => [
Expand Down
33 changes: 33 additions & 0 deletions src/App/src/Factory/GetMarkdownArticleHandlerFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

declare(strict_types=1);

namespace Light\App\Factory;

use Light\App\Handler\GetMarkdownArticleHandler;
use Light\Blog\Repository\CategoryRepository;
use Mezzio\Template\TemplateRendererInterface;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\ContainerInterface;
use Psr\Container\NotFoundExceptionInterface;

use function assert;
use function getcwd;

class GetMarkdownArticleHandlerFactory
{
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
public function __invoke(ContainerInterface $container, string $requestedName): GetMarkdownArticleHandler
{
$template = $container->get(TemplateRendererInterface::class);
assert($template instanceof TemplateRendererInterface);

$categoryRepository = $container->get(CategoryRepository::class);
assert($categoryRepository instanceof CategoryRepository);

return new GetMarkdownArticleHandler($template, $categoryRepository, getcwd() . '/public/md-articles');
}
}
80 changes: 80 additions & 0 deletions src/App/src/Handler/GetMarkdownArticleHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<?php

declare(strict_types=1);

namespace Light\App\Handler;

use Fig\Http\Message\StatusCodeInterface;
use Laminas\Diactoros\Response\HtmlResponse;
use Laminas\Diactoros\Response\TextResponse;
use Light\Blog\Repository\CategoryRepository;
use Mezzio\Template\TemplateRendererInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;

use function file_get_contents;
use function is_file;
use function realpath;
use function rtrim;
use function str_starts_with;

class GetMarkdownArticleHandler implements RequestHandlerInterface
{
public function __construct(
private readonly TemplateRendererInterface $template,
private readonly CategoryRepository $categoryRepository,
private readonly string $articlesPath,
) {
}

public function handle(ServerRequestInterface $request): ResponseInterface
{
$categorySlug = (string) $request->getAttribute('categorySlug');
$slug = (string) $request->getAttribute('slug');

$filePath = $this->resolveFilePath($categorySlug, $slug);
if ($filePath === null) {
return $this->notFound();
}

return new TextResponse(
(string) file_get_contents($filePath),
StatusCodeInterface::STATUS_OK,
['Content-Type' => 'text/markdown; charset=utf-8'],
);
}

private function resolveFilePath(string $categorySlug, string $slug): ?string
{
if ($categorySlug === '' || $slug === '') {
return null;
}

$base = realpath($this->articlesPath);
if ($base === false) {
return null;
}
$base = rtrim($base, '/');
$realPath = realpath($base . '/' . $categorySlug . '/' . $slug . '.md');
if ($realPath === false || ! is_file($realPath)) {
return null;
}

if (! str_starts_with($realPath, $base . '/')) {
return null;
}

return $realPath;
}

private function notFound(): HtmlResponse
{
return new HtmlResponse(
$this->template->render('error::404', [
'categories' => $this->categoryRepository->getCategories(),
]),
StatusCodeInterface::STATUS_NOT_FOUND,
);
}
}
2 changes: 2 additions & 0 deletions src/App/src/RoutesDelegator.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Laminas\Diactoros\Response\RedirectResponse;
use Light\App\Handler\GetFeedViewHandler;
use Light\App\Handler\GetIndexViewHandler;
use Light\App\Handler\GetMarkdownArticleHandler;
use Mezzio\Application;
use Psr\Container\ContainerInterface;

Expand All @@ -20,6 +21,7 @@ public function __invoke(ContainerInterface $container, string $serviceName, cal
assert($app instanceof Application);
$app->get('/', [GetIndexViewHandler::class], 'app::index');
$app->get('/feed/', [GetFeedViewHandler::class], 'app::feed');
$app->get('/{categorySlug}/{slug}.md', [GetMarkdownArticleHandler::class], 'app::markdown-article');

$app->get('/{first}', function ($request) {
$uri = $request->getUri();
Expand Down