- Correction de l'affichage du texte dans le toast (suppression de w-0/truncate)
- Déplacement des toasts en bas à gauche avec animation slide depuis la gauche
- Inversion de l'ordre des éléments : bouton fermeture > texte > icône > bande couleur
- Fix timing : ChapterScrapingStarted synchrone pour notif "démarrage" avant le scraping
- Ajout make notify-test pour tester les 4 types de notifications
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add mangaTitle to ScrapingJob context at creation time
- Fix job.js constructor to map failureReason, attempts, maxAttempts, context from API
- JobItem: show readable type label, manga name, chapter number, source and attempts counter
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Docker resolves bind mounts at container start time, not dynamically when
the Deployer symlink changes. Running cache:clear before restart means
docker exec sees the old release code, causing errors on changed config.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Symfony's YAML route loader does not support the priority key (only PHP config does).
Relying on vue_app being defined first in routes.yaml to ensure it is matched
before legacy controller routes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Move vue_app before controllers in routes.yaml AND keep priority:1.
Using both guarantees Symfony matches the Vue SPA catch-all first
regardless of how the router compiles equal-priority routes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Without priority:1, Symfony matched legacy controllers (e.g. app_activity at /activity)
before the vue_app catch-all on hard reload. Now vue_app matches first for all paths
except /api/* and /legacy* which still route to Symfony controllers.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The var/ directory is a persistent Docker volume. Without explicit cache:clear,
docker restart keeps serving old cached routes (e.g. / → MangaController).
New code is already visible via bind mount before restart, so docker exec works.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Route `/` now serves the Vue SPA directly (catch-all `/{req}`)
- Legacy Twig interface moved to `/legacy`
- Vue Router base changed from `/vue/` to `/`
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace the per-page API call (base64 payload) with static image URLs
served directly by Caddy from public/images/pages/{chapterId}/.
- LocalImageStorage now stores to public/images/ (was MANGA_DATA_PATH)
- LegacyChapterRepository returns /images/pages/{id}/{file} URLs,
uses getimagesize() instead of loading file content into memory
- Delete GetChapterPage query/handler/response, ChapterPageResource,
ChapterPageProvider, PageContent model
- Remove getPageContent() from ChapterRepositoryInterface
- Frontend: loadChapter() fetches chapter + all pages in parallel,
ReaderPage uses URL instead of base64 data URI, InfiniteReader drops
lazy-load observer side effect, readerStore drops loadedPages/preload
- GetChapterPagesTest: extract fixture images from CBZ at runtime,
ignore tests/Fixtures/pages/ in .gitignore
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Le domaine Scraping ne génère plus d'archives CBZ ni ne modifie les
entités du domaine Manga directement. Il scrape, stocke les images
individuellement, et émet un événement partagé.
- Suppression : CbzGeneratorInterface, CbzGenerator, CbzGenerationRequest,
CbzPath, CbzGenerationException
- Suppression : save() de ChapterRepositoryInterface (Scraping)
- Suppression : cbzPath du modèle Chapter (Scraping)
- Ajout : ImageStorageInterface + LocalImageStorage
(stockage dans {MANGA_DATA_PATH}/pages/{chapterId}/)
- ScrapeChapterHandler utilise ImageStorage au lieu du générateur CBZ
- ChapterScraped déplacé dans Domain/Shared/Domain/Event/
avec jobId, chapterId, pagesDirectory, pageCount
- Routing Messenger ajouté
- Ajout : ChapterScrapedEventListener + ChapterScrapedMessageHandler
pour mettre à jour Chapter.pagesDirectory via le Repository Manga
- LegacyChapterRepository en dual-mode :
pagesDirectory en priorité, fallback cbzPath (backward compat)
- Requêtes prev/next : filtrent pagesDirectory IS NOT NULL OR cbzPath IS NOT NULL
- ChapterContext expose pagesDirectory
- phparkitect.php : App\Domain\Shared\Domain\Event autorisé dans
les couches Application (correction violations pré-existantes
ChapterImported/VolumeImported + nouvelle ChapterScraped)
- 218/218 tests passent (+3 nouveaux)
- InMemoryImageStorage créé pour les tests unitaires
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Les index idx_available_at/idx_delivered_at/idx_queue_available/idx_queue_name
n'existent pas sur tous les environnements. IF EXISTS évite l'erreur 42704.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- ChapterResponse expose createdAt comme string formatée (RFC3339)
- GetMangaChaptersHandler formate la date à la construction du DTO
- GetMangaChaptersStateProvider adapté en conséquence
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Ajoute AggregateRoot dans Shared (domain events + pull pattern)
- Manga extends AggregateRoot, devient vrai aggregate root DDD
- Chapter passe de readonly à entité mutable avec MangaId VO
- Manga expose les méthodes domaine pour toute mutation de chapitre :
addChapter, updateChapterTitle/Volume/Pages, hideChapter, removeChapterPages
- Supprime saveChapter/updateChapter/deleteChapter de MangaRepositoryInterface
- save(Manga) gère désormais la persistance des chapitres via pull pattern
- Tous les handlers/listeners passent par l'agrégat (plus d'accès direct)
- phparkitect autorise AggregateRoot dans les couches Domain
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Détecte le crash EAGER loading Doctrine si la colonne pages_directory
est absente de la table chapter (SQLSTATE 42703).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>