fix: allow preview image uploads on plain HTTP origins

This commit is contained in:
Tautvydas Derzinskas committed 2026-10-18 14:10:11 +02:00
1 parent 20c41359dc
commit 6e569a9e82
4 files changed
+25 -3

No files matched your search

@@ -38,6 +38,7 @@ import TagInput from "../../components/TagInput";
import DescriptionEditor from "./DescriptionEditor";
import { translateCategoryDisplay } from "../../utils/translateCategoryDisplay";
import { buildCategoryTree, flattenCategoryTree } from "../../utils/categoryTree";
import { newLocalId } from "../../utils/localId";
type ImageItem =
{ kind: "existing"; id: string; url: string } | { kind: "new"; localId: string; file: File; previewUrl: string };
@@ -124,7 +125,7 @@ export default function EditModelModal({ print, onClose, onUnauthorized, onUpdat
const newItems: ImageItem[] = Array.from(fileList).map((file) => {
const previewUrl = URL.createObjectURL(file);
createdUrlsRef.current.push(previewUrl);
return { kind: "new", localId: crypto.randomUUID(), file, previewUrl };
return { kind: "new", localId: newLocalId(), file, previewUrl };
});
setImages((prev) => [...prev, ...newItems]);
markDirty();
@@ -152,7 +153,7 @@ export default function EditModelModal({ print, onClose, onUnauthorized, onUpdat
if (!fileList?.length) return;
const newItems: PlateItem[] = Array.from(fileList).map((file) => ({
kind: "new",
localId: crypto.randomUUID(),
localId: newLocalId(),
file,
}));
setPlateItems((prev) => [...prev, ...newItems]);
+3 -1
View File
@@ -1,5 +1,7 @@
// SHA-256 works with crypto.subtle, avoiding an MD5 dependency. https://docs.gravatar.com/general/hash/
export async function gravatarUrl(email: string, size = 128): Promise<string> {
// crypto.subtle is missing on plain-HTTP origins; callers then fall back to initials.
export async function gravatarUrl(email: string, size = 128): Promise<string | undefined> {
if (!crypto.subtle) return undefined;
const normalized = email.trim().toLowerCase();
const data = new TextEncoder().encode(normalized);
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
+13
View File
@@ -0,0 +1,13 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import { newLocalId } from "./localId";
describe("newLocalId", () => {
afterEach(() => vi.unstubAllGlobals());
it("works without crypto.randomUUID (insecure HTTP origins)", () => {
vi.stubGlobal("crypto", { getRandomValues: crypto.getRandomValues.bind(crypto) });
const a = newLocalId();
expect(a).toMatch(/^[0-9a-f]{32}$/);
expect(newLocalId()).not.toBe(a);
});
});
+6
View File
@@ -0,0 +1,6 @@
// crypto.randomUUID only exists in secure contexts, and self-hosted instances are often plain HTTP on a LAN.
export function newLocalId(): string {
if (typeof crypto.randomUUID === "function") return crypto.randomUUID();
const bytes = crypto.getRandomValues(new Uint8Array(16));
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
}