@@ -31,19 +31,69 @@ async function api<T>(token: string, path: string, init: FetchInit = {}): Promis
3131 return ( await res . json ( ) ) as T ;
3232}
3333
34- /** Minimales 1x1 PNG (stat. Daten) — gross genug, dass HEAD eine Size meldet. */
35- const TINY_PNG_BYTES = new Uint8Array ( [
36- 0x89 , 0x50 , 0x4e , 0x47 , 0x0d , 0x0a , 0x1a , 0x0a , 0x00 , 0x00 , 0x00 , 0x0d ,
37- 0x49 , 0x48 , 0x44 , 0x52 , 0x00 , 0x00 , 0x00 , 0x01 , 0x00 , 0x00 , 0x00 , 0x01 ,
38- 0x08 , 0x06 , 0x00 , 0x00 , 0x00 , 0x1f , 0x15 , 0xc4 , 0x89 , 0x00 , 0x00 , 0x00 ,
39- 0x0d , 0x49 , 0x44 , 0x41 , 0x54 , 0x78 , 0x9c , 0x63 , 0xfa , 0xcf , 0xc0 , 0xc0 ,
40- 0xc0 , 0x00 , 0x00 , 0x00 , 0x05 , 0x00 , 0x01 , 0x95 , 0x90 , 0xf4 , 0x97 , 0x00 ,
41- 0x00 , 0x00 , 0x00 , 0x49 , 0x45 , 0x4e , 0x44 , 0xae , 0x42 , 0x60 , 0x82 ,
34+ // Minimales valides 1x1 JPEG. MUSS ein JPEG sein: oeffentliche Preset-
35+ // Previews verlangen content_type image/jpeg (presets.py), und confirm_upload
36+ // prueft die echten Magic-Bytes (\xff\xd8\xff) gegen den deklarierten Typ
37+ // (images.py). Ein PNG wuerde hier mit 400 bzw. 415 abgelehnt.
38+ const TINY_JPEG_BYTES = new Uint8Array ( [
39+ 0xff , 0xd8 , 0xff , 0xe0 , 0x00 , 0x10 , 0x4a , 0x46 , 0x49 , 0x46 , 0x00 , 0x01 ,
40+ 0x01 , 0x00 , 0x00 , 0x01 , 0x00 , 0x01 , 0x00 , 0x00 , 0xff , 0xdb , 0x00 , 0x43 ,
41+ 0x00 , 0x0a , 0x07 , 0x07 , 0x08 , 0x07 , 0x06 , 0x0a , 0x08 , 0x08 , 0x08 , 0x0b ,
42+ 0x0a , 0x0a , 0x0b , 0x0e , 0x18 , 0x10 , 0x0e , 0x0d , 0x0d , 0x0e , 0x1d , 0x15 ,
43+ 0x16 , 0x11 , 0x18 , 0x23 , 0x1f , 0x25 , 0x24 , 0x22 , 0x1f , 0x22 , 0x21 , 0x26 ,
44+ 0x2b , 0x37 , 0x2f , 0x26 , 0x29 , 0x34 , 0x29 , 0x21 , 0x22 , 0x30 , 0x41 , 0x31 ,
45+ 0x34 , 0x39 , 0x3b , 0x3e , 0x3e , 0x3e , 0x25 , 0x2e , 0x44 , 0x49 , 0x43 , 0x3c ,
46+ 0x48 , 0x37 , 0x3d , 0x3e , 0x3b , 0xff , 0xdb , 0x00 , 0x43 , 0x01 , 0x0a , 0x0b ,
47+ 0x0b , 0x0e , 0x0d , 0x0e , 0x1c , 0x10 , 0x10 , 0x1c , 0x3b , 0x28 , 0x22 , 0x28 ,
48+ 0x3b , 0x3b , 0x3b , 0x3b , 0x3b , 0x3b , 0x3b , 0x3b , 0x3b , 0x3b , 0x3b , 0x3b ,
49+ 0x3b , 0x3b , 0x3b , 0x3b , 0x3b , 0x3b , 0x3b , 0x3b , 0x3b , 0x3b , 0x3b , 0x3b ,
50+ 0x3b , 0x3b , 0x3b , 0x3b , 0x3b , 0x3b , 0x3b , 0x3b , 0x3b , 0x3b , 0x3b , 0x3b ,
51+ 0x3b , 0x3b , 0x3b , 0x3b , 0x3b , 0x3b , 0x3b , 0x3b , 0x3b , 0x3b , 0x3b , 0x3b ,
52+ 0x3b , 0x3b , 0xff , 0xc0 , 0x00 , 0x11 , 0x08 , 0x00 , 0x01 , 0x00 , 0x01 , 0x03 ,
53+ 0x01 , 0x22 , 0x00 , 0x02 , 0x11 , 0x01 , 0x03 , 0x11 , 0x01 , 0xff , 0xc4 , 0x00 ,
54+ 0x1f , 0x00 , 0x00 , 0x01 , 0x05 , 0x01 , 0x01 , 0x01 , 0x01 , 0x01 , 0x01 , 0x00 ,
55+ 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x01 , 0x02 , 0x03 , 0x04 , 0x05 ,
56+ 0x06 , 0x07 , 0x08 , 0x09 , 0x0a , 0x0b , 0xff , 0xc4 , 0x00 , 0xb5 , 0x10 , 0x00 ,
57+ 0x02 , 0x01 , 0x03 , 0x03 , 0x02 , 0x04 , 0x03 , 0x05 , 0x05 , 0x04 , 0x04 , 0x00 ,
58+ 0x00 , 0x01 , 0x7d , 0x01 , 0x02 , 0x03 , 0x00 , 0x04 , 0x11 , 0x05 , 0x12 , 0x21 ,
59+ 0x31 , 0x41 , 0x06 , 0x13 , 0x51 , 0x61 , 0x07 , 0x22 , 0x71 , 0x14 , 0x32 , 0x81 ,
60+ 0x91 , 0xa1 , 0x08 , 0x23 , 0x42 , 0xb1 , 0xc1 , 0x15 , 0x52 , 0xd1 , 0xf0 , 0x24 ,
61+ 0x33 , 0x62 , 0x72 , 0x82 , 0x09 , 0x0a , 0x16 , 0x17 , 0x18 , 0x19 , 0x1a , 0x25 ,
62+ 0x26 , 0x27 , 0x28 , 0x29 , 0x2a , 0x34 , 0x35 , 0x36 , 0x37 , 0x38 , 0x39 , 0x3a ,
63+ 0x43 , 0x44 , 0x45 , 0x46 , 0x47 , 0x48 , 0x49 , 0x4a , 0x53 , 0x54 , 0x55 , 0x56 ,
64+ 0x57 , 0x58 , 0x59 , 0x5a , 0x63 , 0x64 , 0x65 , 0x66 , 0x67 , 0x68 , 0x69 , 0x6a ,
65+ 0x73 , 0x74 , 0x75 , 0x76 , 0x77 , 0x78 , 0x79 , 0x7a , 0x83 , 0x84 , 0x85 , 0x86 ,
66+ 0x87 , 0x88 , 0x89 , 0x8a , 0x92 , 0x93 , 0x94 , 0x95 , 0x96 , 0x97 , 0x98 , 0x99 ,
67+ 0x9a , 0xa2 , 0xa3 , 0xa4 , 0xa5 , 0xa6 , 0xa7 , 0xa8 , 0xa9 , 0xaa , 0xb2 , 0xb3 ,
68+ 0xb4 , 0xb5 , 0xb6 , 0xb7 , 0xb8 , 0xb9 , 0xba , 0xc2 , 0xc3 , 0xc4 , 0xc5 , 0xc6 ,
69+ 0xc7 , 0xc8 , 0xc9 , 0xca , 0xd2 , 0xd3 , 0xd4 , 0xd5 , 0xd6 , 0xd7 , 0xd8 , 0xd9 ,
70+ 0xda , 0xe1 , 0xe2 , 0xe3 , 0xe4 , 0xe5 , 0xe6 , 0xe7 , 0xe8 , 0xe9 , 0xea , 0xf1 ,
71+ 0xf2 , 0xf3 , 0xf4 , 0xf5 , 0xf6 , 0xf7 , 0xf8 , 0xf9 , 0xfa , 0xff , 0xc4 , 0x00 ,
72+ 0x1f , 0x01 , 0x00 , 0x03 , 0x01 , 0x01 , 0x01 , 0x01 , 0x01 , 0x01 , 0x01 , 0x01 ,
73+ 0x01 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x01 , 0x02 , 0x03 , 0x04 , 0x05 ,
74+ 0x06 , 0x07 , 0x08 , 0x09 , 0x0a , 0x0b , 0xff , 0xc4 , 0x00 , 0xb5 , 0x11 , 0x00 ,
75+ 0x02 , 0x01 , 0x02 , 0x04 , 0x04 , 0x03 , 0x04 , 0x07 , 0x05 , 0x04 , 0x04 , 0x00 ,
76+ 0x01 , 0x02 , 0x77 , 0x00 , 0x01 , 0x02 , 0x03 , 0x11 , 0x04 , 0x05 , 0x21 , 0x31 ,
77+ 0x06 , 0x12 , 0x41 , 0x51 , 0x07 , 0x61 , 0x71 , 0x13 , 0x22 , 0x32 , 0x81 , 0x08 ,
78+ 0x14 , 0x42 , 0x91 , 0xa1 , 0xb1 , 0xc1 , 0x09 , 0x23 , 0x33 , 0x52 , 0xf0 , 0x15 ,
79+ 0x62 , 0x72 , 0xd1 , 0x0a , 0x16 , 0x24 , 0x34 , 0xe1 , 0x25 , 0xf1 , 0x17 , 0x18 ,
80+ 0x19 , 0x1a , 0x26 , 0x27 , 0x28 , 0x29 , 0x2a , 0x35 , 0x36 , 0x37 , 0x38 , 0x39 ,
81+ 0x3a , 0x43 , 0x44 , 0x45 , 0x46 , 0x47 , 0x48 , 0x49 , 0x4a , 0x53 , 0x54 , 0x55 ,
82+ 0x56 , 0x57 , 0x58 , 0x59 , 0x5a , 0x63 , 0x64 , 0x65 , 0x66 , 0x67 , 0x68 , 0x69 ,
83+ 0x6a , 0x73 , 0x74 , 0x75 , 0x76 , 0x77 , 0x78 , 0x79 , 0x7a , 0x82 , 0x83 , 0x84 ,
84+ 0x85 , 0x86 , 0x87 , 0x88 , 0x89 , 0x8a , 0x92 , 0x93 , 0x94 , 0x95 , 0x96 , 0x97 ,
85+ 0x98 , 0x99 , 0x9a , 0xa2 , 0xa3 , 0xa4 , 0xa5 , 0xa6 , 0xa7 , 0xa8 , 0xa9 , 0xaa ,
86+ 0xb2 , 0xb3 , 0xb4 , 0xb5 , 0xb6 , 0xb7 , 0xb8 , 0xb9 , 0xba , 0xc2 , 0xc3 , 0xc4 ,
87+ 0xc5 , 0xc6 , 0xc7 , 0xc8 , 0xc9 , 0xca , 0xd2 , 0xd3 , 0xd4 , 0xd5 , 0xd6 , 0xd7 ,
88+ 0xd8 , 0xd9 , 0xda , 0xe2 , 0xe3 , 0xe4 , 0xe5 , 0xe6 , 0xe7 , 0xe8 , 0xe9 , 0xea ,
89+ 0xf2 , 0xf3 , 0xf4 , 0xf5 , 0xf6 , 0xf7 , 0xf8 , 0xf9 , 0xfa , 0xff , 0xda , 0x00 ,
90+ 0x0c , 0x03 , 0x01 , 0x00 , 0x02 , 0x11 , 0x03 , 0x11 , 0x00 , 0x3f , 0x00 , 0x4a ,
91+ 0x28 , 0xa2 , 0x80 , 0x3f , 0xff , 0xd9 ,
4292] ) ;
4393
4494export async function uploadTinyImage (
4595 token : string ,
46- filename = "marketplace-preview.png " ,
96+ filename = "marketplace-preview.jpg " ,
4797) : Promise < { id : string } > {
4898 // Snake-Case-Body: dev backend kann beide Formen, alte Backends nur snake.
4999 const init = await api < { id ?: string ; upload_url ?: string ; uploadUrl ?: string } > (
@@ -53,8 +103,8 @@ export async function uploadTinyImage(
53103 method : "POST" ,
54104 body : JSON . stringify ( {
55105 filename,
56- content_type : "image/png " ,
57- size_bytes : TINY_PNG_BYTES . byteLength ,
106+ content_type : "image/jpeg " ,
107+ size_bytes : TINY_JPEG_BYTES . byteLength ,
58108 } ) ,
59109 } ,
60110 ) ;
@@ -66,8 +116,8 @@ export async function uploadTinyImage(
66116 // PUT direkt gegen den Pre-Signed URL (MinIO/Garage).
67117 const put = await fetch ( uploadUrl , {
68118 method : "PUT" ,
69- headers : { "Content-Type" : "image/png " } ,
70- body : TINY_PNG_BYTES ,
119+ headers : { "Content-Type" : "image/jpeg " } ,
120+ body : TINY_JPEG_BYTES ,
71121 } ) ;
72122 if ( ! put . ok ) {
73123 throw new Error ( `Pre-Signed-PUT fehlgeschlagen: ${ put . status } ` ) ;
0 commit comments