• fsylvain released this 2026-10-01 05:32:13 +02:00 | 85 commits to main since this release

    RenewCMS now has a media library. Every uploaded image lives in one place where you can browse, rename, delete and clean up duplicates, and the article editor can reuse an image you already uploaded.

    New

    Media page

    • A new Media page at /media sits between Articles and Users in the sidebar and the mobile drawer.
    • Browse the library as a grid or a list, with search by filename, sort by newest, oldest, name or largest, and Unused and Duplicates tabs with counts.
    • Open an image to see its type, size, dimensions, uploader, upload date and URL, copy its URL, and see which articles use it.
    • Rename an image. Only the filename changes, the URL stays the same, so articles keep working.
    • Delete one image or many at once. Deleting an image that is still used asks for a stronger confirmation and lists the articles that will show a broken image.
    • Upload several images at once, with a progress row for each one and a summary. Drop files anywhere on the page.
    • When you upload a file that is already in the library, you are told so and can use the existing image instead.
    • A Duplicates view groups identical images and merges them into one. Articles that used the other copies switch to the one you keep.

    Image picker in the editor

    • The editor's "Add an image" dialog now has Upload and Library tabs. Pick an image you already uploaded, set its alt text and insert it.
    • Images already in the article are tagged "In this article".
    • Removing an image from an article now only removes it from that article. The file stays in the library.

    Improvements and fixes

    • Deleting an article no longer deletes its images, and its confirmation says so.
    • An image that no longer exists shows a "This image is no longer available" placeholder in the editor, with a way to remove it, instead of a collapsed pixel.
    • Deleted images stop showing as soon as you open an article, not only after a refresh. Uploaded images are now revalidated by the browser instead of being cached for a year.
    • The editor's Images row in the article details counts the images in the article body.

    API

    • GET /v1/media/ lists the library with q, filter (all, unused, duplicates), sort, limit, cursor and hash parameters. Each item carries its size, type, dimensions, uploader, used_in count and a duplicate flag, and the response carries the counts for each tab.
    • POST /v1/media/ uploads an image (multipart file). It returns 201, 400 for an unreadable image, 413 for a file over 10 MB and 415 for another type.
    • PATCH /v1/media/{id} renames an image and keeps its extension and URL. It returns 200, or 404.
    • DELETE /v1/media/{id} deletes an image, even when articles use it. It returns 204, or 404.
    • POST /v1/media/merge takes keep_id and merge_ids, switches the articles that use the other copies to the kept image, deletes the copies and returns how many articles changed. It returns 409 when a copy is gone or the images are no longer identical.
    • GET /v1/media/{id}/usage lists the articles that use an image, with their title and whether they are online.
    • Changed: images no longer belong to an article. GET /v1/article/{id} no longer returns images, and deleting an article keeps its images.
    • Changed: DELETE /v1/article/{id}/image/{imageId} and POST /v1/article/{id}/image are removed, replaced by the /v1/media/ endpoints.
    • Changed: /static/uploadedImages/* now sends Cache-Control: public, no-cache instead of a one-year immutable lifetime.

    Under the hood

    • The image domain, repository, use cases and handlers were rebuilt around a library model, keeping the domain, use-case and infrastructure layering.
    • WEBP dimensions are read with golang.org/x/image. golang.org/x/sys and golang.org/x/text were updated with it.
    • New feature specs and tests cover the media API, the Media page, uploads, duplicates and the editor picker.

    Upgrading

    • Replace the binary and restart. Your .env, SQLite file and uploads directory stay as they are, and there are no new environment variables.
    • The database migrates itself on startup. It adds columns to the images table, and a one-time backfill reads each existing image file to fill in its size, type, dimensions and content hash. An image whose file is missing is left alone. Back up your SQLite file and uploads directory first as a precaution.
    • Existing images become part of the library and are no longer tied to an article. The old article link is no longer used.
    • Clients that call the removed article image endpoints, or read images from an article, must switch to /v1/media/.
    • Browsers keep serving images they cached before the upgrade until they are reloaded once.

    Known limitations

    • The Media page shows only the first 100 images.
    • There is no rate limit on /v1/media/ yet.
    • The library list loads every article and image in memory, so very large libraries will be slow.
    • A file whose extension doesn't match its content type, such as a JPEG labelled as PNG, is rejected.
    • Searching filenames with non-ASCII letters is case-sensitive.
    • Uploaded SVGs are served as they are. They are sandboxed with a restrictive content security policy, but not sanitised.
    Downloads