@gallop.software/studio 1.3.6 → 1.4.0
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-DNDMPY7Q.mjs → StudioUI-XLQBCXNU.mjs} +147 -2
- package/dist/StudioUI-XLQBCXNU.mjs.map +1 -0
- package/dist/{StudioUI-OY33OEG2.js → StudioUI-YRXPBDUX.js} +148 -3
- package/dist/StudioUI-YRXPBDUX.js.map +1 -0
- package/dist/handlers/index.js +154 -40
- package/dist/handlers/index.js.map +1 -1
- package/dist/handlers/index.mjs +114 -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-DNDMPY7Q.mjs.map +0 -1
- package/dist/StudioUI-OY33OEG2.js.map +0 -1
package/dist/handlers/index.mjs
CHANGED
|
@@ -309,6 +309,22 @@ async function deleteFromCdn(imageKey, hasThumbnails) {
|
|
|
309
309
|
}
|
|
310
310
|
}
|
|
311
311
|
}
|
|
312
|
+
async function deleteThumbnailsFromCdn(imageKey) {
|
|
313
|
+
const bucketName = process.env.CLOUDFLARE_R2_BUCKET_NAME;
|
|
314
|
+
if (!bucketName) throw new Error("R2 bucket not configured");
|
|
315
|
+
const r2 = getR2Client();
|
|
316
|
+
for (const thumbPath of getAllThumbnailPaths(imageKey)) {
|
|
317
|
+
try {
|
|
318
|
+
await r2.send(
|
|
319
|
+
new DeleteObjectCommand({
|
|
320
|
+
Bucket: bucketName,
|
|
321
|
+
Key: thumbPath.replace(/^\//, "")
|
|
322
|
+
})
|
|
323
|
+
);
|
|
324
|
+
} catch {
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
312
328
|
|
|
313
329
|
// src/handlers/list.ts
|
|
314
330
|
function getExistingThumbnails(originalPath, entry) {
|
|
@@ -1393,6 +1409,101 @@ async function handleReprocess(request) {
|
|
|
1393
1409
|
return NextResponse3.json({ error: "Failed to reprocess images" }, { status: 500 });
|
|
1394
1410
|
}
|
|
1395
1411
|
}
|
|
1412
|
+
async function handleUnprocessStream(request) {
|
|
1413
|
+
const publicUrl = process.env.CLOUDFLARE_R2_PUBLIC_URL?.replace(/\/\s*$/, "");
|
|
1414
|
+
const encoder = new TextEncoder();
|
|
1415
|
+
let imageKeys;
|
|
1416
|
+
try {
|
|
1417
|
+
const body = await request.json();
|
|
1418
|
+
imageKeys = body.imageKeys;
|
|
1419
|
+
if (!imageKeys || !Array.isArray(imageKeys) || imageKeys.length === 0) {
|
|
1420
|
+
return NextResponse3.json({ error: "No image keys provided" }, { status: 400 });
|
|
1421
|
+
}
|
|
1422
|
+
} catch {
|
|
1423
|
+
return NextResponse3.json({ error: "Invalid request body" }, { status: 400 });
|
|
1424
|
+
}
|
|
1425
|
+
const stream = new ReadableStream({
|
|
1426
|
+
async start(controller) {
|
|
1427
|
+
const sendEvent = (data) => {
|
|
1428
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}
|
|
1429
|
+
|
|
1430
|
+
`));
|
|
1431
|
+
};
|
|
1432
|
+
try {
|
|
1433
|
+
const meta = await loadMeta();
|
|
1434
|
+
const cdnUrls = getCdnUrls(meta);
|
|
1435
|
+
const unprocessed = [];
|
|
1436
|
+
const errors = [];
|
|
1437
|
+
const urlsToPurge = [];
|
|
1438
|
+
const total = imageKeys.length;
|
|
1439
|
+
sendEvent({ type: "start", total });
|
|
1440
|
+
for (let i = 0; i < imageKeys.length; i++) {
|
|
1441
|
+
let imageKey = imageKeys[i];
|
|
1442
|
+
if (!imageKey.startsWith("/")) {
|
|
1443
|
+
imageKey = `/${imageKey}`;
|
|
1444
|
+
}
|
|
1445
|
+
sendEvent({
|
|
1446
|
+
type: "progress",
|
|
1447
|
+
current: i + 1,
|
|
1448
|
+
total,
|
|
1449
|
+
percent: Math.round((i + 1) / total * 100),
|
|
1450
|
+
message: `Removing thumbnails for ${imageKey.slice(1)}...`
|
|
1451
|
+
});
|
|
1452
|
+
try {
|
|
1453
|
+
const entry = getMetaEntry(meta, imageKey);
|
|
1454
|
+
if (!entry) {
|
|
1455
|
+
errors.push(imageKey);
|
|
1456
|
+
continue;
|
|
1457
|
+
}
|
|
1458
|
+
const existingCdnIndex = entry.c;
|
|
1459
|
+
const existingCdnUrl = existingCdnIndex !== void 0 ? cdnUrls[existingCdnIndex] : void 0;
|
|
1460
|
+
const isInOurR2 = existingCdnUrl === publicUrl;
|
|
1461
|
+
await deleteLocalThumbnails(imageKey);
|
|
1462
|
+
if (isInOurR2) {
|
|
1463
|
+
await deleteThumbnailsFromCdn(imageKey);
|
|
1464
|
+
for (const thumbPath of getAllThumbnailPaths(imageKey)) {
|
|
1465
|
+
urlsToPurge.push(`${publicUrl}${thumbPath}`);
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
meta[imageKey] = {
|
|
1469
|
+
o: entry.o,
|
|
1470
|
+
b: entry.b,
|
|
1471
|
+
...entry.c !== void 0 ? { c: entry.c } : {}
|
|
1472
|
+
};
|
|
1473
|
+
unprocessed.push(imageKey);
|
|
1474
|
+
} catch (error) {
|
|
1475
|
+
console.error(`Failed to unprocess ${imageKey}:`, error);
|
|
1476
|
+
errors.push(imageKey);
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
sendEvent({ type: "cleanup", message: "Saving metadata..." });
|
|
1480
|
+
await saveMeta(meta);
|
|
1481
|
+
if (urlsToPurge.length > 0) {
|
|
1482
|
+
sendEvent({ type: "cleanup", message: "Purging CDN cache..." });
|
|
1483
|
+
await purgeCloudflareCache(urlsToPurge);
|
|
1484
|
+
}
|
|
1485
|
+
sendEvent({
|
|
1486
|
+
type: "complete",
|
|
1487
|
+
processed: unprocessed.length,
|
|
1488
|
+
errors: errors.length,
|
|
1489
|
+
message: `Removed thumbnails from ${unprocessed.length} image${unprocessed.length !== 1 ? "s" : ""}${errors.length > 0 ? `, ${errors.length} error${errors.length !== 1 ? "s" : ""}` : ""}`
|
|
1490
|
+
});
|
|
1491
|
+
controller.close();
|
|
1492
|
+
} catch (error) {
|
|
1493
|
+
console.error("Unprocess stream error:", error);
|
|
1494
|
+
sendEvent({ type: "error", message: "Failed to remove thumbnails" });
|
|
1495
|
+
controller.close();
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1498
|
+
});
|
|
1499
|
+
return new Response(stream, {
|
|
1500
|
+
headers: {
|
|
1501
|
+
"Content-Type": "text/event-stream",
|
|
1502
|
+
"Cache-Control": "no-cache",
|
|
1503
|
+
Connection: "keep-alive"
|
|
1504
|
+
}
|
|
1505
|
+
});
|
|
1506
|
+
}
|
|
1396
1507
|
async function handleReprocessStream(request) {
|
|
1397
1508
|
const publicUrl = process.env.CLOUDFLARE_R2_PUBLIC_URL?.replace(/\/\s*$/, "");
|
|
1398
1509
|
const encoder = new TextEncoder();
|
|
@@ -2122,6 +2233,9 @@ async function POST(request) {
|
|
|
2122
2233
|
if (route === "reprocess-stream") {
|
|
2123
2234
|
return handleReprocessStream(request);
|
|
2124
2235
|
}
|
|
2236
|
+
if (route === "unprocess-stream") {
|
|
2237
|
+
return handleUnprocessStream(request);
|
|
2238
|
+
}
|
|
2125
2239
|
if (route === "process-all") {
|
|
2126
2240
|
return handleProcessAllStream();
|
|
2127
2241
|
}
|