@gallop.software/studio 2.3.25 → 2.3.26
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/server/index.js +42 -11
- package/dist/server/index.js.map +1 -1
- package/package.json +1 -1
package/dist/server/index.js
CHANGED
|
@@ -95,6 +95,17 @@ function getFileEntries(meta) {
|
|
|
95
95
|
|
|
96
96
|
// src/handlers/utils/files.ts
|
|
97
97
|
import path2 from "path";
|
|
98
|
+
function slugifyFilename(filename) {
|
|
99
|
+
const ext = path2.extname(filename).toLowerCase();
|
|
100
|
+
const baseName = path2.basename(filename, path2.extname(filename));
|
|
101
|
+
const slugged = baseName.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[_\s]+/g, "-").replace(/[^a-z0-9-]/g, "").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
102
|
+
const finalSlug = slugged || "file";
|
|
103
|
+
return finalSlug + ext;
|
|
104
|
+
}
|
|
105
|
+
function slugifyFolderName(name) {
|
|
106
|
+
const slugged = name.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[_\s]+/g, "-").replace(/[^a-z0-9-]/g, "").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
107
|
+
return slugged || "folder";
|
|
108
|
+
}
|
|
98
109
|
function isImageFile(filename) {
|
|
99
110
|
const ext = path2.extname(filename).toLowerCase();
|
|
100
111
|
return [".jpg", ".jpeg", ".png", ".gif", ".webp", ".svg", ".ico", ".bmp", ".tiff", ".tif"].includes(ext);
|
|
@@ -1033,7 +1044,7 @@ async function handleUpload(request) {
|
|
|
1033
1044
|
}
|
|
1034
1045
|
const bytes = await file.arrayBuffer();
|
|
1035
1046
|
const buffer = Buffer.from(bytes);
|
|
1036
|
-
const fileName = file.name;
|
|
1047
|
+
const fileName = slugifyFilename(file.name);
|
|
1037
1048
|
const ext = path6.extname(fileName).toLowerCase();
|
|
1038
1049
|
const isImage = isImageFile(fileName);
|
|
1039
1050
|
const isMedia = isMediaFile(fileName);
|
|
@@ -1225,7 +1236,7 @@ async function handleCreateFolder(request) {
|
|
|
1225
1236
|
if (!name || typeof name !== "string") {
|
|
1226
1237
|
return jsonResponse({ error: "Folder name is required" }, { status: 400 });
|
|
1227
1238
|
}
|
|
1228
|
-
const sanitizedName = name
|
|
1239
|
+
const sanitizedName = slugifyFolderName(name);
|
|
1229
1240
|
if (!sanitizedName) {
|
|
1230
1241
|
return jsonResponse({ error: "Invalid folder name" }, { status: 400 });
|
|
1231
1242
|
}
|
|
@@ -1252,14 +1263,8 @@ async function handleRename(request) {
|
|
|
1252
1263
|
if (!oldPath || !newName) {
|
|
1253
1264
|
return jsonResponse({ error: "Path and new name are required" }, { status: 400 });
|
|
1254
1265
|
}
|
|
1255
|
-
const sanitizedName = newName.replace(/[<>:"/\\|?*]/g, "").trim();
|
|
1256
|
-
if (!sanitizedName) {
|
|
1257
|
-
return jsonResponse({ error: "Invalid name" }, { status: 400 });
|
|
1258
|
-
}
|
|
1259
1266
|
const safePath = oldPath.replace(/\.\./g, "");
|
|
1260
1267
|
const absoluteOldPath = getWorkspacePath(safePath);
|
|
1261
|
-
const parentDir = path6.dirname(absoluteOldPath);
|
|
1262
|
-
const absoluteNewPath = path6.join(parentDir, sanitizedName);
|
|
1263
1268
|
if (!absoluteOldPath.startsWith(getPublicPath())) {
|
|
1264
1269
|
return jsonResponse({ error: "Invalid path" }, { status: 400 });
|
|
1265
1270
|
}
|
|
@@ -1268,14 +1273,20 @@ async function handleRename(request) {
|
|
|
1268
1273
|
} catch {
|
|
1269
1274
|
return jsonResponse({ error: "File or folder not found" }, { status: 404 });
|
|
1270
1275
|
}
|
|
1276
|
+
const stats = await fs6.stat(absoluteOldPath);
|
|
1277
|
+
const isFile = stats.isFile();
|
|
1278
|
+
const isImage = isFile && isImageFile(path6.basename(oldPath));
|
|
1279
|
+
const sanitizedName = isFile ? slugifyFilename(newName) : slugifyFolderName(newName);
|
|
1280
|
+
if (!sanitizedName) {
|
|
1281
|
+
return jsonResponse({ error: "Invalid name" }, { status: 400 });
|
|
1282
|
+
}
|
|
1283
|
+
const parentDir = path6.dirname(absoluteOldPath);
|
|
1284
|
+
const absoluteNewPath = path6.join(parentDir, sanitizedName);
|
|
1271
1285
|
try {
|
|
1272
1286
|
await fs6.access(absoluteNewPath);
|
|
1273
1287
|
return jsonResponse({ error: "An item with this name already exists" }, { status: 400 });
|
|
1274
1288
|
} catch {
|
|
1275
1289
|
}
|
|
1276
|
-
const stats = await fs6.stat(absoluteOldPath);
|
|
1277
|
-
const isFile = stats.isFile();
|
|
1278
|
-
const isImage = isFile && isImageFile(path6.basename(oldPath));
|
|
1279
1290
|
await fs6.rename(absoluteOldPath, absoluteNewPath);
|
|
1280
1291
|
if (isImage) {
|
|
1281
1292
|
const meta = await loadMeta();
|
|
@@ -2363,6 +2374,26 @@ async function handleScanStream() {
|
|
|
2363
2374
|
}
|
|
2364
2375
|
continue;
|
|
2365
2376
|
}
|
|
2377
|
+
const dirName = path8.dirname(relativePath);
|
|
2378
|
+
const originalFileName = path8.basename(relativePath);
|
|
2379
|
+
const sluggedFileName = slugifyFilename(originalFileName);
|
|
2380
|
+
if (sluggedFileName !== originalFileName) {
|
|
2381
|
+
const newRelativePath = dirName === "." ? sluggedFileName : `${dirName}/${sluggedFileName}`;
|
|
2382
|
+
const newFullPath = getPublicPath(newRelativePath);
|
|
2383
|
+
const newKey = "/" + newRelativePath;
|
|
2384
|
+
if (!meta[newKey] && !existingKeys.has(newKey)) {
|
|
2385
|
+
try {
|
|
2386
|
+
await fs8.mkdir(path8.dirname(newFullPath), { recursive: true });
|
|
2387
|
+
await fs8.rename(fullPath, newFullPath);
|
|
2388
|
+
renamed.push({ from: relativePath, to: newRelativePath });
|
|
2389
|
+
relativePath = newRelativePath;
|
|
2390
|
+
fullPath = newFullPath;
|
|
2391
|
+
imageKey = newKey;
|
|
2392
|
+
} catch (err) {
|
|
2393
|
+
console.error(`Failed to slugify ${relativePath}:`, err);
|
|
2394
|
+
}
|
|
2395
|
+
}
|
|
2396
|
+
}
|
|
2366
2397
|
if (meta[imageKey]) {
|
|
2367
2398
|
const ext = path8.extname(relativePath);
|
|
2368
2399
|
const baseName = relativePath.slice(0, -ext.length);
|