@gallop.software/studio 1.3.1 → 1.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{StudioUI-NREPJZTK.mjs → StudioUI-IHTWNJHB.mjs} +81 -23
- package/dist/{StudioUI-NREPJZTK.mjs.map → StudioUI-IHTWNJHB.mjs.map} +1 -1
- package/dist/{StudioUI-LORP7MIK.js → StudioUI-X5SN52MU.js} +82 -24
- package/dist/StudioUI-X5SN52MU.js.map +1 -0
- package/dist/handlers/index.js +149 -12
- package/dist/handlers/index.js.map +1 -1
- package/dist/handlers/index.mjs +137 -0
- package/dist/handlers/index.mjs.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
- package/dist/StudioUI-LORP7MIK.js.map +0 -1
package/dist/handlers/index.mjs
CHANGED
|
@@ -1393,6 +1393,140 @@ async function handleReprocess(request) {
|
|
|
1393
1393
|
return NextResponse3.json({ error: "Failed to reprocess images" }, { status: 500 });
|
|
1394
1394
|
}
|
|
1395
1395
|
}
|
|
1396
|
+
async function handleReprocessStream(request) {
|
|
1397
|
+
const publicUrl = process.env.CLOUDFLARE_R2_PUBLIC_URL?.replace(/\/\s*$/, "");
|
|
1398
|
+
const encoder = new TextEncoder();
|
|
1399
|
+
let imageKeys;
|
|
1400
|
+
try {
|
|
1401
|
+
const body = await request.json();
|
|
1402
|
+
imageKeys = body.imageKeys;
|
|
1403
|
+
if (!imageKeys || !Array.isArray(imageKeys) || imageKeys.length === 0) {
|
|
1404
|
+
return NextResponse3.json({ error: "No image keys provided" }, { status: 400 });
|
|
1405
|
+
}
|
|
1406
|
+
} catch {
|
|
1407
|
+
return NextResponse3.json({ error: "Invalid request body" }, { status: 400 });
|
|
1408
|
+
}
|
|
1409
|
+
const stream = new ReadableStream({
|
|
1410
|
+
async start(controller) {
|
|
1411
|
+
const sendEvent = (data) => {
|
|
1412
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}
|
|
1413
|
+
|
|
1414
|
+
`));
|
|
1415
|
+
};
|
|
1416
|
+
try {
|
|
1417
|
+
const meta = await loadMeta();
|
|
1418
|
+
const cdnUrls = getCdnUrls(meta);
|
|
1419
|
+
const processed = [];
|
|
1420
|
+
const errors = [];
|
|
1421
|
+
const urlsToPurge = [];
|
|
1422
|
+
const total = imageKeys.length;
|
|
1423
|
+
sendEvent({ type: "start", total });
|
|
1424
|
+
for (let i = 0; i < imageKeys.length; i++) {
|
|
1425
|
+
let imageKey = imageKeys[i];
|
|
1426
|
+
if (!imageKey.startsWith("/")) {
|
|
1427
|
+
imageKey = `/${imageKey}`;
|
|
1428
|
+
}
|
|
1429
|
+
sendEvent({
|
|
1430
|
+
type: "progress",
|
|
1431
|
+
current: i + 1,
|
|
1432
|
+
total,
|
|
1433
|
+
percent: Math.round((i + 1) / total * 100),
|
|
1434
|
+
message: `Processing ${imageKey.slice(1)}...`
|
|
1435
|
+
});
|
|
1436
|
+
try {
|
|
1437
|
+
let buffer;
|
|
1438
|
+
const entry = getMetaEntry(meta, imageKey);
|
|
1439
|
+
const existingCdnIndex = entry?.c;
|
|
1440
|
+
const existingCdnUrl = existingCdnIndex !== void 0 ? cdnUrls[existingCdnIndex] : void 0;
|
|
1441
|
+
const isInOurR2 = existingCdnUrl === publicUrl;
|
|
1442
|
+
const isRemote = existingCdnIndex !== void 0 && !isInOurR2;
|
|
1443
|
+
const originalPath = path7.join(process.cwd(), "public", imageKey);
|
|
1444
|
+
try {
|
|
1445
|
+
buffer = await fs6.readFile(originalPath);
|
|
1446
|
+
} catch {
|
|
1447
|
+
if (isInOurR2) {
|
|
1448
|
+
buffer = await downloadFromCdn(imageKey);
|
|
1449
|
+
const dir = path7.dirname(originalPath);
|
|
1450
|
+
await fs6.mkdir(dir, { recursive: true });
|
|
1451
|
+
await fs6.writeFile(originalPath, buffer);
|
|
1452
|
+
} else if (isRemote && existingCdnUrl) {
|
|
1453
|
+
const remoteUrl = `${existingCdnUrl}${imageKey}`;
|
|
1454
|
+
buffer = await downloadFromRemoteUrl(remoteUrl);
|
|
1455
|
+
const dir = path7.dirname(originalPath);
|
|
1456
|
+
await fs6.mkdir(dir, { recursive: true });
|
|
1457
|
+
await fs6.writeFile(originalPath, buffer);
|
|
1458
|
+
} else {
|
|
1459
|
+
throw new Error(`File not found: ${imageKey}`);
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
const ext = path7.extname(imageKey).toLowerCase();
|
|
1463
|
+
const isSvg = ext === ".svg";
|
|
1464
|
+
if (isSvg) {
|
|
1465
|
+
const imageDir = path7.dirname(imageKey.slice(1));
|
|
1466
|
+
const imagesPath = path7.join(process.cwd(), "public", "images", imageDir === "." ? "" : imageDir);
|
|
1467
|
+
await fs6.mkdir(imagesPath, { recursive: true });
|
|
1468
|
+
const fileName = path7.basename(imageKey);
|
|
1469
|
+
const destPath = path7.join(imagesPath, fileName);
|
|
1470
|
+
await fs6.writeFile(destPath, buffer);
|
|
1471
|
+
meta[imageKey] = {
|
|
1472
|
+
...entry,
|
|
1473
|
+
o: { w: 0, h: 0 },
|
|
1474
|
+
b: "",
|
|
1475
|
+
f: { w: 0, h: 0 }
|
|
1476
|
+
};
|
|
1477
|
+
if (isRemote) {
|
|
1478
|
+
delete meta[imageKey].c;
|
|
1479
|
+
}
|
|
1480
|
+
} else {
|
|
1481
|
+
const updatedEntry = await processImage(buffer, imageKey);
|
|
1482
|
+
if (isInOurR2) {
|
|
1483
|
+
updatedEntry.c = existingCdnIndex;
|
|
1484
|
+
await uploadToCdn(imageKey);
|
|
1485
|
+
for (const thumbPath of getAllThumbnailPaths(imageKey)) {
|
|
1486
|
+
urlsToPurge.push(`${publicUrl}${thumbPath}`);
|
|
1487
|
+
}
|
|
1488
|
+
await deleteLocalThumbnails(imageKey);
|
|
1489
|
+
try {
|
|
1490
|
+
await fs6.unlink(originalPath);
|
|
1491
|
+
} catch {
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
meta[imageKey] = updatedEntry;
|
|
1495
|
+
}
|
|
1496
|
+
processed.push(imageKey);
|
|
1497
|
+
} catch (error) {
|
|
1498
|
+
console.error(`Failed to reprocess ${imageKey}:`, error);
|
|
1499
|
+
errors.push(imageKey);
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1502
|
+
sendEvent({ type: "cleanup", message: "Saving metadata..." });
|
|
1503
|
+
await saveMeta(meta);
|
|
1504
|
+
if (urlsToPurge.length > 0) {
|
|
1505
|
+
sendEvent({ type: "cleanup", message: "Purging CDN cache..." });
|
|
1506
|
+
await purgeCloudflareCache(urlsToPurge);
|
|
1507
|
+
}
|
|
1508
|
+
sendEvent({
|
|
1509
|
+
type: "complete",
|
|
1510
|
+
processed: processed.length,
|
|
1511
|
+
errors: errors.length,
|
|
1512
|
+
message: `Processed ${processed.length} image${processed.length !== 1 ? "s" : ""}${errors.length > 0 ? `, ${errors.length} error${errors.length !== 1 ? "s" : ""}` : ""}`
|
|
1513
|
+
});
|
|
1514
|
+
controller.close();
|
|
1515
|
+
} catch (error) {
|
|
1516
|
+
console.error("Reprocess stream error:", error);
|
|
1517
|
+
sendEvent({ type: "error", message: "Failed to process images" });
|
|
1518
|
+
controller.close();
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
});
|
|
1522
|
+
return new Response(stream, {
|
|
1523
|
+
headers: {
|
|
1524
|
+
"Content-Type": "text/event-stream",
|
|
1525
|
+
"Cache-Control": "no-cache",
|
|
1526
|
+
Connection: "keep-alive"
|
|
1527
|
+
}
|
|
1528
|
+
});
|
|
1529
|
+
}
|
|
1396
1530
|
async function handleProcessAllStream() {
|
|
1397
1531
|
const publicUrl = process.env.CLOUDFLARE_R2_PUBLIC_URL?.replace(/\/\s*$/, "");
|
|
1398
1532
|
const encoder = new TextEncoder();
|
|
@@ -1985,6 +2119,9 @@ async function POST(request) {
|
|
|
1985
2119
|
if (route === "reprocess") {
|
|
1986
2120
|
return handleReprocess(request);
|
|
1987
2121
|
}
|
|
2122
|
+
if (route === "reprocess-stream") {
|
|
2123
|
+
return handleReprocessStream(request);
|
|
2124
|
+
}
|
|
1988
2125
|
if (route === "process-all") {
|
|
1989
2126
|
return handleProcessAllStream();
|
|
1990
2127
|
}
|