Feature: Uploading an image to an article As an authenticated RenewCMS user managing the CMS I want to attach an image file to an existing article and get back the image's public location So that an unknown article answers 404 the same way every other article write does, without ever creating an orphaned image, and a disallowed content type is rejected with 400 before it becomes part of the article, without a maintainer needing to trust the client to send a sane file Background: Given internal/infrastructure/useCases.CreateImageUseCase.CreateImage already checks the uploaded content type against the allowed list (image/png, image/jpeg, image/webp, image/svg+xml) and the size against the 10485760-byte maximum before calling image.Repository.Create, returning image.ErrImageTypeNotAllowed or image.ErrImageTooLarge and calling the repository not at all when either check fails And internal/infrastructure/persistence's ArticleRepository.AddImage already returns article.ErrArticleNotFound when the article id doesn't match any row, and image.ErrImageNotFound when the article exists but the image id doesn't, both asserted with errors.Is And before this slice, api/handlers/image.Handler.PostImage existed and was wired to CreateImageUseCase and UpdateArticleUseCase.AddImage, but no route called it, and every error it got back from either use case fell through to a blanket 500 And this slice mounts POST /v1/article/{id}/image in api/router.go, in the same jwtauth Verifier + Authenticator group as the other article write routes, and changes the upload flow so that the article's existence is confirmed before the image is created, so that an unknown article id never leaves an orphaned image behind And this slice changes PostImage's error handling so that errors.Is(err, image.ErrImageTypeNotAllowed) or errors.Is(err, image.ErrImageTooLarge) answers 400 with the sentinel's own message, errors.Is(err, article.ErrArticleNotFound) answers 404 with body apperr.ArticleNotFoundErrorMessage, and any other error still falls back to a generic 500 And PostImage reads the uploaded file from the multipart form field "file" And on success PostImage answers 201 with a JSON body {"id": ..., "location": ...} holding the created image's numeric id and public path, e.g. "/static/uploadedImages/" And this slice (features/remove_article_image.feature) adds the "id" field to that body; the id lets a client later address the image through DELETE /v1/article/{id}/image/{imageId} Scenario: Uploading an allowed image type to an existing article succeeds Given a valid jwt cookie for a user with id 1 And the article repository contains an article with id 1 titled "Backyard Renovation Update" When the client sends POST /v1/article/1/image with that cookie and a multipart form field "file" holding a file named "before.png" with content type "image/png" Then the response status is 201 And the response body is JSON with a "location" field whose value is "/static/uploadedImages/before.png" And the response body's "id" field equals the new image's id in the image repository And the image repository contains a new image whose Path is "/static/uploadedImages/before.png" And the article repository's article with id 1 has that new image's id in its Images slice Scenario: Uploading to an unknown article answers 404 and creates no image Given a valid jwt cookie for a user with id 1 And the article repository has no article with id 9999 When the client sends POST /v1/article/9999/image with that cookie and a multipart form field "file" holding a file named "before.png" with content type "image/png" Then the response status is 404 And the response body is the text "Article not found" And the image repository's Create is never called And no image exists in the image repository Scenario: Uploading a disallowed content type answers 400 before the article is touched Given a valid jwt cookie for a user with id 1 And the article repository contains an article with id 1 titled "Backyard Renovation Update" When the client sends POST /v1/article/1/image with that cookie and a multipart form field "file" holding a file named "notes.pdf" with content type "application/pdf" Then the response status is 400 And the response body is the text "the file must be a PNG, JPEG, WEBP, or SVG image" And the image repository's Create is never called And the article repository's article with id 1 still has an empty Images slice Scenario: Uploading an image still requires authentication Given no jwt cookie is sent And the article repository contains an article with id 1 titled "Backyard Renovation Update" When the client sends POST /v1/article/1/image without a cookie and a multipart form field "file" holding a file named "before.png" with content type "image/png" Then the response status is 401 And the image repository's Create is never called And the article repository's article with id 1 still has an empty Images slice