@gallop.software/studio 0.1.115 → 1.0.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-OVL65ONP.js → StudioUI-GWMM47P7.js} +99 -17
- package/dist/StudioUI-GWMM47P7.js.map +1 -0
- package/dist/{StudioUI-73XFVFV4.mjs → StudioUI-KCUI5YUD.mjs} +99 -17
- package/dist/StudioUI-KCUI5YUD.mjs.map +1 -0
- package/dist/{chunk-RDNC5ABF.mjs → chunk-FDWPNRNZ.mjs} +1 -1
- package/dist/chunk-FDWPNRNZ.mjs.map +1 -0
- package/dist/{chunk-LEOQKJCL.js → chunk-WJJHVPLT.js} +1 -1
- package/dist/chunk-WJJHVPLT.js.map +1 -0
- package/dist/handlers/index.js +126 -27
- package/dist/handlers/index.js.map +1 -1
- package/dist/handlers/index.mjs +116 -17
- package/dist/handlers/index.mjs.map +1 -1
- package/dist/index.d.mts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3 -3
- package/dist/index.mjs +2 -2
- package/package.json +1 -1
- package/dist/StudioUI-73XFVFV4.mjs.map +0 -1
- package/dist/StudioUI-OVL65ONP.js.map +0 -1
- package/dist/chunk-LEOQKJCL.js.map +0 -1
- package/dist/chunk-RDNC5ABF.mjs.map +0 -1
package/dist/handlers/index.mjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getAllThumbnailPaths,
|
|
3
3
|
getThumbnailPath
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-FDWPNRNZ.mjs";
|
|
5
5
|
|
|
6
6
|
// src/handlers/index.ts
|
|
7
|
-
import { NextResponse as
|
|
7
|
+
import { NextResponse as NextResponse5 } from "next/server";
|
|
8
8
|
|
|
9
9
|
// src/handlers/list.ts
|
|
10
10
|
import { NextResponse } from "next/server";
|
|
@@ -309,13 +309,16 @@ async function handleList(request) {
|
|
|
309
309
|
const items = [];
|
|
310
310
|
const seenFolders = /* @__PURE__ */ new Set();
|
|
311
311
|
const metaKeys = fileEntries.map(([key]) => key);
|
|
312
|
+
const isInsideImagesFolder = relativePath === "images" || relativePath.startsWith("images/");
|
|
312
313
|
const absoluteDir = path5.join(process.cwd(), requestedPath);
|
|
313
314
|
try {
|
|
314
315
|
const dirEntries = await fs4.readdir(absoluteDir, { withFileTypes: true });
|
|
315
316
|
for (const entry of dirEntries) {
|
|
316
|
-
if (entry.isDirectory() && !entry.name.startsWith(".")
|
|
317
|
+
if (entry.isDirectory() && !entry.name.startsWith(".")) {
|
|
317
318
|
if (!seenFolders.has(entry.name)) {
|
|
318
319
|
seenFolders.add(entry.name);
|
|
320
|
+
const isImagesFolder = entry.name === "images" && !relativePath;
|
|
321
|
+
const folderPath = relativePath ? `public/${relativePath}/${entry.name}` : `public/${entry.name}`;
|
|
319
322
|
const folderPrefix = pathPrefix === "/" ? `/${entry.name}/` : `${pathPrefix}${entry.name}/`;
|
|
320
323
|
let fileCount = 0;
|
|
321
324
|
for (const k of metaKeys) {
|
|
@@ -323,9 +326,10 @@ async function handleList(request) {
|
|
|
323
326
|
}
|
|
324
327
|
items.push({
|
|
325
328
|
name: entry.name,
|
|
326
|
-
path:
|
|
329
|
+
path: folderPath,
|
|
327
330
|
type: "folder",
|
|
328
|
-
fileCount
|
|
331
|
+
fileCount,
|
|
332
|
+
isProtected: isImagesFolder || isInsideImagesFolder
|
|
329
333
|
});
|
|
330
334
|
}
|
|
331
335
|
}
|
|
@@ -354,7 +358,8 @@ async function handleList(request) {
|
|
|
354
358
|
name: folderName,
|
|
355
359
|
path: relativePath ? `public/${relativePath}/${folderName}` : `public/${folderName}`,
|
|
356
360
|
type: "folder",
|
|
357
|
-
fileCount
|
|
361
|
+
fileCount,
|
|
362
|
+
isProtected: isInsideImagesFolder
|
|
358
363
|
});
|
|
359
364
|
}
|
|
360
365
|
} else {
|
|
@@ -413,6 +418,7 @@ async function handleList(request) {
|
|
|
413
418
|
cdnPushed: isPushedToCloud,
|
|
414
419
|
cdnBaseUrl: fileCdnUrl,
|
|
415
420
|
isRemote,
|
|
421
|
+
isProtected: isInsideImagesFolder,
|
|
416
422
|
dimensions: entry.w && entry.h ? { width: entry.w, height: entry.h } : void 0
|
|
417
423
|
});
|
|
418
424
|
}
|
|
@@ -1083,10 +1089,6 @@ async function handleSync(request) {
|
|
|
1083
1089
|
continue;
|
|
1084
1090
|
}
|
|
1085
1091
|
const isRemote = entry.c !== void 0 && existingCdnUrl !== publicUrl;
|
|
1086
|
-
if (!isRemote && !entry.p) {
|
|
1087
|
-
errors.push(`Image not processed: ${imageKey}. Run Process Images first.`);
|
|
1088
|
-
continue;
|
|
1089
|
-
}
|
|
1090
1092
|
try {
|
|
1091
1093
|
let originalBuffer;
|
|
1092
1094
|
if (isRemote) {
|
|
@@ -1433,6 +1435,7 @@ async function handleProcessAllStream() {
|
|
|
1433
1435
|
}
|
|
1434
1436
|
|
|
1435
1437
|
// src/handlers/scan.ts
|
|
1438
|
+
import { NextResponse as NextResponse4 } from "next/server";
|
|
1436
1439
|
import { promises as fs7 } from "fs";
|
|
1437
1440
|
import path8 from "path";
|
|
1438
1441
|
import sharp3 from "sharp";
|
|
@@ -1448,11 +1451,12 @@ async function handleScanStream() {
|
|
|
1448
1451
|
};
|
|
1449
1452
|
try {
|
|
1450
1453
|
const meta = await loadMeta();
|
|
1451
|
-
const existingCount = Object.keys(meta).length;
|
|
1454
|
+
const existingCount = Object.keys(meta).filter((k) => !k.startsWith("_")).length;
|
|
1452
1455
|
const existingKeys = new Set(Object.keys(meta));
|
|
1453
1456
|
const added = [];
|
|
1454
1457
|
const renamed = [];
|
|
1455
1458
|
const errors = [];
|
|
1459
|
+
const orphanedFiles = [];
|
|
1456
1460
|
const allFiles = [];
|
|
1457
1461
|
async function scanDir(dir, relativePath = "") {
|
|
1458
1462
|
try {
|
|
@@ -1542,6 +1546,40 @@ async function handleScanStream() {
|
|
|
1542
1546
|
errors.push(relativePath);
|
|
1543
1547
|
}
|
|
1544
1548
|
}
|
|
1549
|
+
sendEvent({ type: "cleanup", message: "Checking for orphaned thumbnails..." });
|
|
1550
|
+
const expectedThumbnails = /* @__PURE__ */ new Set();
|
|
1551
|
+
const fileEntries = getFileEntries(meta);
|
|
1552
|
+
for (const [imageKey, entry] of fileEntries) {
|
|
1553
|
+
if (entry.c === void 0 && entry.p === 1) {
|
|
1554
|
+
for (const thumbPath of getAllThumbnailPaths(imageKey)) {
|
|
1555
|
+
expectedThumbnails.add(thumbPath);
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1559
|
+
async function findOrphans(dir, relativePath = "") {
|
|
1560
|
+
try {
|
|
1561
|
+
const entries = await fs7.readdir(dir, { withFileTypes: true });
|
|
1562
|
+
for (const entry of entries) {
|
|
1563
|
+
if (entry.name.startsWith(".")) continue;
|
|
1564
|
+
const fullPath = path8.join(dir, entry.name);
|
|
1565
|
+
const relPath = relativePath ? `${relativePath}/${entry.name}` : entry.name;
|
|
1566
|
+
if (entry.isDirectory()) {
|
|
1567
|
+
await findOrphans(fullPath, relPath);
|
|
1568
|
+
} else if (isImageFile(entry.name)) {
|
|
1569
|
+
const publicPath = `/images/${relPath}`;
|
|
1570
|
+
if (!expectedThumbnails.has(publicPath)) {
|
|
1571
|
+
orphanedFiles.push(publicPath);
|
|
1572
|
+
}
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1575
|
+
} catch {
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
const imagesDir = path8.join(process.cwd(), "public", "images");
|
|
1579
|
+
try {
|
|
1580
|
+
await findOrphans(imagesDir);
|
|
1581
|
+
} catch {
|
|
1582
|
+
}
|
|
1545
1583
|
await saveMeta(meta);
|
|
1546
1584
|
sendEvent({
|
|
1547
1585
|
type: "complete",
|
|
@@ -1549,7 +1587,8 @@ async function handleScanStream() {
|
|
|
1549
1587
|
added: added.length,
|
|
1550
1588
|
renamed: renamed.length,
|
|
1551
1589
|
errors: errors.length,
|
|
1552
|
-
renamedFiles: renamed
|
|
1590
|
+
renamedFiles: renamed,
|
|
1591
|
+
orphanedFiles: orphanedFiles.length > 0 ? orphanedFiles : void 0
|
|
1553
1592
|
});
|
|
1554
1593
|
} catch (error) {
|
|
1555
1594
|
console.error("Scan failed:", error);
|
|
@@ -1567,6 +1606,63 @@ async function handleScanStream() {
|
|
|
1567
1606
|
}
|
|
1568
1607
|
});
|
|
1569
1608
|
}
|
|
1609
|
+
async function handleDeleteOrphans(request) {
|
|
1610
|
+
try {
|
|
1611
|
+
const { paths } = await request.json();
|
|
1612
|
+
if (!paths || !Array.isArray(paths) || paths.length === 0) {
|
|
1613
|
+
return NextResponse4.json({ error: "No paths provided" }, { status: 400 });
|
|
1614
|
+
}
|
|
1615
|
+
const deleted = [];
|
|
1616
|
+
const errors = [];
|
|
1617
|
+
for (const orphanPath of paths) {
|
|
1618
|
+
if (!orphanPath.startsWith("/images/")) {
|
|
1619
|
+
errors.push(`Invalid path: ${orphanPath}`);
|
|
1620
|
+
continue;
|
|
1621
|
+
}
|
|
1622
|
+
const fullPath = path8.join(process.cwd(), "public", orphanPath);
|
|
1623
|
+
try {
|
|
1624
|
+
await fs7.unlink(fullPath);
|
|
1625
|
+
deleted.push(orphanPath);
|
|
1626
|
+
} catch (err) {
|
|
1627
|
+
console.error(`Failed to delete ${orphanPath}:`, err);
|
|
1628
|
+
errors.push(orphanPath);
|
|
1629
|
+
}
|
|
1630
|
+
}
|
|
1631
|
+
const imagesDir = path8.join(process.cwd(), "public", "images");
|
|
1632
|
+
async function removeEmptyDirs(dir) {
|
|
1633
|
+
try {
|
|
1634
|
+
const entries = await fs7.readdir(dir, { withFileTypes: true });
|
|
1635
|
+
let isEmpty = true;
|
|
1636
|
+
for (const entry of entries) {
|
|
1637
|
+
if (entry.isDirectory()) {
|
|
1638
|
+
const subDirEmpty = await removeEmptyDirs(path8.join(dir, entry.name));
|
|
1639
|
+
if (!subDirEmpty) isEmpty = false;
|
|
1640
|
+
} else {
|
|
1641
|
+
isEmpty = false;
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1644
|
+
if (isEmpty && dir !== imagesDir) {
|
|
1645
|
+
await fs7.rmdir(dir);
|
|
1646
|
+
}
|
|
1647
|
+
return isEmpty;
|
|
1648
|
+
} catch {
|
|
1649
|
+
return true;
|
|
1650
|
+
}
|
|
1651
|
+
}
|
|
1652
|
+
try {
|
|
1653
|
+
await removeEmptyDirs(imagesDir);
|
|
1654
|
+
} catch {
|
|
1655
|
+
}
|
|
1656
|
+
return NextResponse4.json({
|
|
1657
|
+
success: true,
|
|
1658
|
+
deleted: deleted.length,
|
|
1659
|
+
errors: errors.length
|
|
1660
|
+
});
|
|
1661
|
+
} catch (error) {
|
|
1662
|
+
console.error("Failed to delete orphans:", error);
|
|
1663
|
+
return NextResponse4.json({ error: "Failed to delete orphaned files" }, { status: 500 });
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1570
1666
|
|
|
1571
1667
|
// src/handlers/import.ts
|
|
1572
1668
|
import sharp4 from "sharp";
|
|
@@ -1697,7 +1793,7 @@ async function handleUpdateCdns(request) {
|
|
|
1697
1793
|
// src/handlers/index.ts
|
|
1698
1794
|
async function GET(request) {
|
|
1699
1795
|
if (process.env.NODE_ENV !== "development") {
|
|
1700
|
-
return
|
|
1796
|
+
return NextResponse5.json({ error: "Not available in production" }, { status: 403 });
|
|
1701
1797
|
}
|
|
1702
1798
|
const pathname = request.nextUrl.pathname;
|
|
1703
1799
|
const route = pathname.replace(/^\/api\/studio\/?/, "");
|
|
@@ -1719,11 +1815,11 @@ async function GET(request) {
|
|
|
1719
1815
|
if (route === "cdns") {
|
|
1720
1816
|
return handleGetCdns();
|
|
1721
1817
|
}
|
|
1722
|
-
return
|
|
1818
|
+
return NextResponse5.json({ error: "Not found" }, { status: 404 });
|
|
1723
1819
|
}
|
|
1724
1820
|
async function POST(request) {
|
|
1725
1821
|
if (process.env.NODE_ENV !== "development") {
|
|
1726
|
-
return
|
|
1822
|
+
return NextResponse5.json({ error: "Not available in production" }, { status: 403 });
|
|
1727
1823
|
}
|
|
1728
1824
|
const pathname = request.nextUrl.pathname;
|
|
1729
1825
|
const route = pathname.replace(/^\/api\/studio\/?/, "");
|
|
@@ -1754,17 +1850,20 @@ async function POST(request) {
|
|
|
1754
1850
|
if (route === "scan") {
|
|
1755
1851
|
return handleScanStream();
|
|
1756
1852
|
}
|
|
1853
|
+
if (route === "delete-orphans") {
|
|
1854
|
+
return handleDeleteOrphans(request);
|
|
1855
|
+
}
|
|
1757
1856
|
if (route === "import") {
|
|
1758
1857
|
return handleImportUrls(request);
|
|
1759
1858
|
}
|
|
1760
1859
|
if (route === "cdns") {
|
|
1761
1860
|
return handleUpdateCdns(request);
|
|
1762
1861
|
}
|
|
1763
|
-
return
|
|
1862
|
+
return NextResponse5.json({ error: "Not found" }, { status: 404 });
|
|
1764
1863
|
}
|
|
1765
1864
|
async function DELETE(request) {
|
|
1766
1865
|
if (process.env.NODE_ENV !== "development") {
|
|
1767
|
-
return
|
|
1866
|
+
return NextResponse5.json({ error: "Not available in production" }, { status: 403 });
|
|
1768
1867
|
}
|
|
1769
1868
|
return handleDelete(request);
|
|
1770
1869
|
}
|