Minor changes

This commit is contained in:
julien
2026-03-22 12:51:14 +01:00
parent 365b0b08b5
commit 7f0c07b90f
12 changed files with 40 additions and 135 deletions

View File

@@ -103,10 +103,20 @@
}
});
var payload = await response.json();
var contentType = response.headers.get('content-type') || '';
var raw = await response.text();
var payload = {};
if (contentType.indexOf('application/json') !== -1) {
try {
payload = JSON.parse(raw);
} catch (parseError) {
payload = {};
}
}
if (!response.ok) {
throw new Error(payload.error || 'Le téléversement a échoué.');
throw new Error(payload.error || ('HTTP ' + response.status + (raw ? ' - ' + raw.slice(0, 180) : '')));
}
setFeedback('Image téléversée. Rafraîchissement…', false);

View File

@@ -6,9 +6,9 @@ services:
dockerfile: docker/php/Dockerfile
restart: unless-stopped
volumes:
# Répertoire de travail de l'entrypoint : reçoit public/ compilé,
# puis partagé avec Nginx via le mount ci-dessous.
- ./data:/data
# Répertoire public partagé : reçoit les assets versionnés copiés par
# l'entrypoint, puis sert de racine statique à Nginx.
- ./data/public:/data/public
# Base SQLite et migrations : persistés entre redéploiements.
- ./data/database:/var/www/app/database
@@ -33,8 +33,8 @@ services:
TRUSTED_PROXIES: ${TRUSTED_PROXIES:-*}
# bash /dev/tcp est disponible sur l'image Debian php:8.4-fpm sans dépendance
# supplémentaire. start_period laisse le temps à entrypoint.sh de terminer
# (sync public/, permissions, caches) avant que les échecs ne comptent.
# supplémentaire. start_period laisse le temps à entrypoint.sh de préparer
# les répertoires runtime et de synchroniser les assets publics.
healthcheck:
test: ["CMD-SHELL", "bash -c 'echo > /dev/tcp/localhost/9000'"]
interval: 10s
@@ -63,7 +63,7 @@ services:
# Même environnement/fichiers que le runtime PHP pour provisionner exactement
# la même base SQLite persistée.
volumes:
- ./data:/data
- ./data/public:/data/public
- ./data/database:/var/www/app/database
- ./data/var:/var/www/app/var
- ./data/public/media:/var/www/app/public/media

View File

@@ -2,6 +2,8 @@ server {
listen 80;
server_name _;
client_max_body_size 8M;
root /var/www/app/public;
index index.php;

18
docker/php/entrypoint.sh Normal file → Executable file
View File

@@ -2,19 +2,11 @@
set -eu
# Prépare les répertoires runtime/persistants.
mkdir -p \
/data/public \
/var/www/app/database \
/var/www/app/public/media \
/var/www/app/var/cache/twig \
/var/www/app/var/cache/htmlpurifier \
/var/www/app/var/cache/di \
/var/www/app/var/logs
mkdir -p /data/public /data/public/media /var/www/app/database /var/www/app/public/media /var/www/app/var/cache/twig /var/www/app/var/cache/htmlpurifier /var/www/app/var/cache/di /var/www/app/var/logs
# Synchronise les fichiers publics versionnés (index.php, assets compilés, etc.)
# vers le volume partagé. Le répertoire media est exclu car il contient les
# uploads utilisateurs et est géré séparément.
mkdir -p /data/public/media
# Synchronise uniquement les fichiers publics versionnés (index.php, assets
# compilés, favicon, etc.) vers le répertoire partagé avec Nginx. Le dossier
# media est préservé car il contient les uploads persistants.
find /data/public -mindepth 1 -maxdepth 1 ! -name media -exec rm -rf {} +
for item in /var/www/app/public/*; do
@@ -26,7 +18,7 @@ done
# Permissions sur les répertoires persistants. Doit s'exécuter en root avant
# le démarrage de PHP-FPM.
chown -R www-data:www-data /data /var/www/app/database /var/www/app/var /var/www/app/public/media
chown -R www-data:www-data /data/public /var/www/app/database /var/www/app/var /var/www/app/public/media
# Invalide les caches compilés à chaque déploiement.
rm -rf /var/www/app/var/cache/twig/*

View File

@@ -13,5 +13,5 @@ error_log = /dev/stderr
; La valeur doit être synchronisée avec session_name() dans public/index.php si modifiée.
session.name = sid
; En production, journaliser les erreurs sans les injecter dans les réponses HTTP
; Ne pas injecter les warnings/notices dans les réponses HTTP de prod
display_errors = Off

View File

@@ -14,7 +14,7 @@ use App\Post\Domain\Repository\PostRepositoryInterface;
use Netig\Netslim\Kernel\Pagination\Application\PaginatedResult;
use Netig\Netslim\Kernel\Support\Exception\NotFoundException;
final class PostApplicationService implements PostServiceInterface
class PostApplicationService
{
public function __construct(
private readonly PostRepositoryInterface $postRepository,

View File

@@ -1,97 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Post\Application;
use App\Post\Domain\Entity\Post;
use Netig\Netslim\Kernel\Pagination\Application\PaginatedResult;
use Netig\Netslim\Kernel\Support\Exception\NotFoundException;
/**
* Contrat applicatif du domaine Post.
*/
interface PostServiceInterface
{
/**
* Retourne l'ensemble des articles, éventuellement filtrés par catégorie.
*
* @return list<Post>
*/
public function findAll(?int $categoryId = null): array;
/**
* Retourne une page d'articles, éventuellement filtrés par catégorie.
*
* @return PaginatedResult<Post>
*/
public function findPaginated(int $page, int $perPage, ?int $categoryId = null): PaginatedResult;
/**
* Retourne les articles les plus récents.
*
* @return list<Post>
*/
public function findRecent(int $limit = 20): array;
/**
* Retourne les articles d'un auteur, éventuellement filtrés par catégorie.
*
* @return list<Post>
*/
public function findByUserId(int $userId, ?int $categoryId = null): array;
/**
* Retourne une page d'articles pour un auteur, éventuellement filtrés par catégorie.
*
* @return PaginatedResult<Post>
*/
public function findByUserIdPaginated(int $userId, int $page, int $perPage, ?int $categoryId = null): PaginatedResult;
/**
* Retourne un article par slug.
*
* @throws NotFoundException Si aucun article ne correspond au slug fourni.
*/
public function findBySlug(string $slug): Post;
/**
* Retourne un article par identifiant.
*
* @throws NotFoundException Si aucun article ne correspond à l'identifiant fourni.
*/
public function findById(int $id): Post;
/**
* Recherche des articles selon une requête textuelle et des filtres optionnels.
*
* @return list<Post>
*/
public function search(string $query, ?int $categoryId = null, ?int $authorId = null): array;
/**
* Retourne une page de résultats de recherche.
*
* @return PaginatedResult<Post>
*/
public function searchPaginated(string $query, int $page, int $perPage, ?int $categoryId = null, ?int $authorId = null): PaginatedResult;
/**
* Crée un article et retourne son identifiant persistant.
*/
public function create(string $title, string $content, int $authorId, ?int $categoryId = null): int;
/**
* Met à jour un article existant.
*
* @throws NotFoundException Si l'article n'existe pas.
*/
public function update(int $id, string $title, string $content, string $slug = '', ?int $categoryId = null): void;
/**
* Supprime un article existant.
*
* @throws NotFoundException Si l'article n'existe pas.
*/
public function delete(int $id): void;
}

View File

@@ -3,7 +3,6 @@
declare(strict_types=1);
use App\Post\Application\PostApplicationService;
use App\Post\Application\PostServiceInterface;
use App\Post\Application\UseCase\CreatePost;
use App\Post\Application\UseCase\DeletePost;
use App\Post\Application\UseCase\UpdatePost;
@@ -25,7 +24,6 @@ use Netig\Netslim\Media\Contracts\MediaUsageReaderInterface;
use Netig\Netslim\Taxonomy\Contracts\TaxonUsageCheckerInterface;
return [
PostServiceInterface::class => autowire(PostApplicationService::class),
PostRepositoryInterface::class => autowire(PdoPostRepository::class),
PostMediaUsageRepositoryInterface::class => autowire(PdoPostMediaUsageRepository::class),
PostMediaReferenceExtractorInterface::class => autowire(HtmlPostMediaReferenceExtractor::class),
@@ -35,7 +33,7 @@ return [
UpdatePost::class => autowire(),
DeletePost::class => autowire(),
MediaUsageReaderInterface::class => autowire(PostMediaUsageReader::class),
RssController::class => factory(function (PostServiceInterface $postService): RssController {
RssController::class => factory(function (PostApplicationService $postService): RssController {
return new RssController(
$postService,
rtrim($_ENV['APP_URL'] ?? 'http://localhost', '/'),

View File

@@ -4,7 +4,7 @@ declare(strict_types=1);
namespace App\Post\UI\Http;
use App\Post\Application\PostServiceInterface;
use App\Post\Application\PostApplicationService;
use App\Post\Domain\Entity\Post;
use App\Post\UI\Http\Request\PostFormRequest;
use Netig\Netslim\AuditLog\Contracts\AuditLoggerInterface;
@@ -29,7 +29,7 @@ final class PostController
{
public function __construct(
private readonly Twig $view,
private readonly PostServiceInterface $postService,
private readonly PostApplicationService $postService,
private readonly TaxonomyReaderInterface $taxonomyReader,
private readonly SettingsReaderInterface $settings,
private readonly AuthorizationServiceInterface $authorization,

View File

@@ -4,7 +4,7 @@ declare(strict_types=1);
namespace App\Post\UI\Http;
use App\Post\Application\PostServiceInterface;
use App\Post\Application\PostApplicationService;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
@@ -16,7 +16,7 @@ class RssController
private const FEED_LIMIT = 20;
public function __construct(
private readonly PostServiceInterface $postService,
private readonly PostApplicationService $postService,
private readonly string $appUrl,
private readonly string $appName,
) {}

View File

@@ -4,7 +4,7 @@ declare(strict_types=1);
namespace Tests\Post;
use App\Post\Application\PostServiceInterface;
use App\Post\Application\PostApplicationService;
use App\Post\Domain\Entity\Post;
use App\Post\UI\Http\PostController;
use Netig\Netslim\AuditLog\Contracts\AuditLoggerInterface;
@@ -39,8 +39,8 @@ final class PostControllerTest extends ControllerTestBase
/** @var \Slim\Views\Twig&MockObject */
private \Slim\Views\Twig $view;
/** @var PostServiceInterface&MockObject */
private PostServiceInterface $postService;
/** @var PostApplicationService&MockObject */
private PostApplicationService $postService;
/** @var TaxonomyReaderInterface&MockObject */
private TaxonomyReaderInterface $taxonomyReader;
@@ -65,7 +65,7 @@ final class PostControllerTest extends ControllerTestBase
protected function setUp(): void
{
$this->view = $this->makeTwigMock();
$this->postService = $this->createMock(PostServiceInterface::class);
$this->postService = $this->createMock(PostApplicationService::class);
$this->taxonomyReader = $this->createMock(TaxonomyReaderInterface::class);
$this->settings = $this->createMock(SettingsReaderInterface::class);
$this->authorization = $this->createMock(AuthorizationServiceInterface::class);

View File

@@ -4,7 +4,7 @@ declare(strict_types=1);
namespace Tests\Post;
use App\Post\Application\PostServiceInterface;
use App\Post\Application\PostApplicationService;
use App\Post\Domain\Entity\Post;
use App\Post\UI\Http\RssController;
use PHPUnit\Framework\MockObject\MockObject;
@@ -23,8 +23,8 @@ use Tests\ControllerTestBase;
#[\PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations]
final class RssControllerTest extends ControllerTestBase
{
/** @var PostServiceInterface&MockObject */
private PostServiceInterface $postService;
/** @var PostApplicationService&MockObject */
private PostApplicationService $postService;
private RssController $controller;
@@ -33,7 +33,7 @@ final class RssControllerTest extends ControllerTestBase
protected function setUp(): void
{
$this->postService = $this->createMock(PostServiceInterface::class);
$this->postService = $this->createMock(PostApplicationService::class);
$this->controller = new RssController(
$this->postService,