feat(uploads): restrict to webp/avif with conversion hint

This commit is contained in:
2026-07-12 11:03:49 +02:00
parent 34dc67ce99
commit ff0452bcc3
3 changed files with 28 additions and 14 deletions
+6 -4
View File
@@ -19,9 +19,7 @@ module Volumen
MAX_LOGIN_ATTEMPTS = 10
LOGIN_WINDOW = 60 # seconds
MAX_UPLOAD_BYTES = 10 * 1024 * 1024 # 10 MB
ALLOWED_UPLOAD_TYPES = %w[
image/jpeg image/png image/gif image/webp image/avif image/svg+xml
].freeze
ALLOWED_UPLOAD_TYPES = %w[image/webp image/avif].freeze
SLUG_REGEX = /\A[a-z0-9](?:[a-z0-9._-]*[a-z0-9])?\z/
register ApiRoutes
@@ -248,7 +246,11 @@ module Volumen
content_type = upload[:type].to_s.split(";").first.to_s.strip.downcase
return if ALLOWED_UPLOAD_TYPES.include?(content_type)
halt(415, json("error" => "unsupported_media_type", "allowed" => ALLOWED_UPLOAD_TYPES))
halt(415, json("error" => "unsupported_format",
"message" => "Only WebP (.webp) and AVIF (.avif) images are supported. " \
"Please convert your image before uploading — for example with " \
"`cwebp` (JPEG/PNG → WebP) or `avifenc` (PNG/JPEG → AVIF).",
"allowed" => ALLOWED_UPLOAD_TYPES))
end
def check_login_rate_limit!
+17 -5
View File
@@ -299,13 +299,25 @@
// --- Image upload ------------------------------------------------------
function uploadImage(file, onDone) {
function uploadImage(file, onSuccess, onError) {
var data = new FormData();
data.append("file", file);
data.append("_csrf", csrf);
fetch(uploadUrl, { method: "POST", body: data })
.then(function (response) { return response.json(); })
.then(function (json) { if (json.url) { onDone(json.url); } });
.then(function (response) {
return response.json().then(function (json) { return { ok: response.ok, json: json }; });
})
.then(function (result) {
if (result.ok && result.json.url) {
onSuccess(result.json.url);
} else {
onError(result.json.message || ("Upload failed (HTTP " + (result.json.error || "error") + ")"));
}
});
}
function showUploadError(message) {
alert("Upload failed:\n\n" + message);
}
imageInput.addEventListener("change", function () {
@@ -317,7 +329,7 @@
} else {
exec("insertImage", url);
}
});
}, showUploadError);
imageInput.value = "";
});
@@ -346,7 +358,7 @@
uploadImage(file, function (url) {
coverInput.value = url;
refreshCoverPreview();
});
}, showUploadError);
coverFile.value = "";
});
coverInput.addEventListener("input", refreshCoverPreview);