feat(uploads): restrict to webp/avif with conversion hint
This commit is contained in:
@@ -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!
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user