@gallop.software/studio 2.3.135 → 2.3.137
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/client/index.html
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
|
12
12
|
}
|
|
13
13
|
</style>
|
|
14
|
-
<script type="module" crossorigin src="/assets/index-
|
|
14
|
+
<script type="module" crossorigin src="/assets/index-3VggyaFt.js"></script>
|
|
15
15
|
<link rel="stylesheet" crossorigin href="/assets/index-DfPQBmNf.css">
|
|
16
16
|
</head>
|
|
17
17
|
<body>
|
package/dist/server/index.js
CHANGED
|
@@ -3967,8 +3967,8 @@ import sharp4 from "sharp";
|
|
|
3967
3967
|
function parseImageUrl(url) {
|
|
3968
3968
|
const parsed = new URL(url);
|
|
3969
3969
|
const base = `${parsed.protocol}//${parsed.host}`;
|
|
3970
|
-
const
|
|
3971
|
-
return { base, path:
|
|
3970
|
+
const path12 = parsed.pathname;
|
|
3971
|
+
return { base, path: path12 };
|
|
3972
3972
|
}
|
|
3973
3973
|
async function processRemoteImage(url) {
|
|
3974
3974
|
const response = await fetch(url);
|
|
@@ -4029,10 +4029,10 @@ async function handleImportUrls(request) {
|
|
|
4029
4029
|
const url = urls[i].trim();
|
|
4030
4030
|
if (!url) continue;
|
|
4031
4031
|
try {
|
|
4032
|
-
const { base, path:
|
|
4033
|
-
const existingEntry = getMetaEntry(meta,
|
|
4032
|
+
const { base, path: path12 } = parseImageUrl(url);
|
|
4033
|
+
const existingEntry = getMetaEntry(meta, path12);
|
|
4034
4034
|
if (existingEntry) {
|
|
4035
|
-
skipped.push(
|
|
4035
|
+
skipped.push(path12);
|
|
4036
4036
|
sendEvent({
|
|
4037
4037
|
type: "progress",
|
|
4038
4038
|
current: i + 1,
|
|
@@ -4045,13 +4045,13 @@ async function handleImportUrls(request) {
|
|
|
4045
4045
|
}
|
|
4046
4046
|
const cdnIndex = getOrAddCdnIndex(meta, base);
|
|
4047
4047
|
const imageData = await processRemoteImage(url);
|
|
4048
|
-
setMetaEntry(meta,
|
|
4048
|
+
setMetaEntry(meta, path12, {
|
|
4049
4049
|
o: imageData.o,
|
|
4050
4050
|
b: imageData.b,
|
|
4051
4051
|
c: cdnIndex
|
|
4052
4052
|
});
|
|
4053
4053
|
await saveMeta(meta);
|
|
4054
|
-
added.push(
|
|
4054
|
+
added.push(path12);
|
|
4055
4055
|
sendEvent({
|
|
4056
4056
|
type: "progress",
|
|
4057
4057
|
current: i + 1,
|
|
@@ -4572,6 +4572,189 @@ async function handleEditImage(request) {
|
|
|
4572
4572
|
}
|
|
4573
4573
|
}
|
|
4574
4574
|
|
|
4575
|
+
// src/handlers/fonts.ts
|
|
4576
|
+
import { promises as fs12 } from "fs";
|
|
4577
|
+
import path11 from "path";
|
|
4578
|
+
async function handleFontsList(request) {
|
|
4579
|
+
const searchParams = new URL(request.url).searchParams;
|
|
4580
|
+
const requestedPath = searchParams.get("path") || "_fonts";
|
|
4581
|
+
try {
|
|
4582
|
+
const items = [];
|
|
4583
|
+
let fsPath;
|
|
4584
|
+
let allowedPaths = ["_fonts", "src/fonts", "src"];
|
|
4585
|
+
const isAllowed = allowedPaths.some(
|
|
4586
|
+
(allowed) => requestedPath === allowed || requestedPath.startsWith(allowed + "/")
|
|
4587
|
+
);
|
|
4588
|
+
if (!isAllowed) {
|
|
4589
|
+
return jsonResponse({ items: [], error: "Path not allowed" }, { status: 400 });
|
|
4590
|
+
}
|
|
4591
|
+
if (requestedPath === "src") {
|
|
4592
|
+
const fontsFolderPath = getWorkspacePath("src", "fonts");
|
|
4593
|
+
try {
|
|
4594
|
+
const stat = await fs12.stat(fontsFolderPath);
|
|
4595
|
+
if (stat.isDirectory()) {
|
|
4596
|
+
items.push({
|
|
4597
|
+
name: "fonts",
|
|
4598
|
+
path: "src/fonts",
|
|
4599
|
+
type: "folder"
|
|
4600
|
+
});
|
|
4601
|
+
}
|
|
4602
|
+
} catch {
|
|
4603
|
+
}
|
|
4604
|
+
return jsonResponse({ items, canCreate: true });
|
|
4605
|
+
}
|
|
4606
|
+
fsPath = getWorkspacePath(requestedPath);
|
|
4607
|
+
try {
|
|
4608
|
+
const stat = await fs12.stat(fsPath);
|
|
4609
|
+
if (!stat.isDirectory()) {
|
|
4610
|
+
return jsonResponse({ items: [] });
|
|
4611
|
+
}
|
|
4612
|
+
} catch {
|
|
4613
|
+
return jsonResponse({ items: [], canCreate: true });
|
|
4614
|
+
}
|
|
4615
|
+
const entries = await fs12.readdir(fsPath, { withFileTypes: true });
|
|
4616
|
+
for (const entry of entries) {
|
|
4617
|
+
const itemPath = `${requestedPath}/${entry.name}`;
|
|
4618
|
+
if (entry.isDirectory()) {
|
|
4619
|
+
let fileCount = 0;
|
|
4620
|
+
try {
|
|
4621
|
+
const subEntries = await fs12.readdir(path11.join(fsPath, entry.name));
|
|
4622
|
+
fileCount = subEntries.filter(
|
|
4623
|
+
(f) => f.match(/\.(ttf|woff2?|otf|ts|tsx|js)$/i)
|
|
4624
|
+
).length;
|
|
4625
|
+
} catch {
|
|
4626
|
+
}
|
|
4627
|
+
items.push({
|
|
4628
|
+
name: entry.name,
|
|
4629
|
+
path: itemPath,
|
|
4630
|
+
type: "folder",
|
|
4631
|
+
fileCount
|
|
4632
|
+
});
|
|
4633
|
+
} else {
|
|
4634
|
+
const ext = path11.extname(entry.name).toLowerCase();
|
|
4635
|
+
const allowedExts = [".ttf", ".woff", ".woff2", ".otf", ".ts", ".tsx", ".js"];
|
|
4636
|
+
if (allowedExts.includes(ext)) {
|
|
4637
|
+
let size = 0;
|
|
4638
|
+
try {
|
|
4639
|
+
const fileStat = await fs12.stat(path11.join(fsPath, entry.name));
|
|
4640
|
+
size = fileStat.size;
|
|
4641
|
+
} catch {
|
|
4642
|
+
}
|
|
4643
|
+
items.push({
|
|
4644
|
+
name: entry.name,
|
|
4645
|
+
path: itemPath,
|
|
4646
|
+
type: "file",
|
|
4647
|
+
size
|
|
4648
|
+
});
|
|
4649
|
+
}
|
|
4650
|
+
}
|
|
4651
|
+
}
|
|
4652
|
+
items.sort((a, b) => {
|
|
4653
|
+
if (a.type === "folder" && b.type !== "folder") return -1;
|
|
4654
|
+
if (a.type !== "folder" && b.type === "folder") return 1;
|
|
4655
|
+
return a.name.localeCompare(b.name);
|
|
4656
|
+
});
|
|
4657
|
+
return jsonResponse({ items });
|
|
4658
|
+
} catch (error) {
|
|
4659
|
+
console.error("Error listing fonts:", error);
|
|
4660
|
+
return jsonResponse({ error: "Failed to list fonts" }, { status: 500 });
|
|
4661
|
+
}
|
|
4662
|
+
}
|
|
4663
|
+
async function handleFontsUpload(request) {
|
|
4664
|
+
try {
|
|
4665
|
+
const formData = await request.formData();
|
|
4666
|
+
const file = formData.get("file");
|
|
4667
|
+
const targetPath = formData.get("path") || "_fonts";
|
|
4668
|
+
if (!file) {
|
|
4669
|
+
return jsonResponse({ error: "No file provided" }, { status: 400 });
|
|
4670
|
+
}
|
|
4671
|
+
if (!file.name.toLowerCase().endsWith(".ttf")) {
|
|
4672
|
+
return jsonResponse({ error: "Only TTF files are supported" }, { status: 400 });
|
|
4673
|
+
}
|
|
4674
|
+
if (!targetPath.startsWith("_fonts")) {
|
|
4675
|
+
return jsonResponse({ error: "Can only upload to _fonts/" }, { status: 400 });
|
|
4676
|
+
}
|
|
4677
|
+
const bytes = await file.arrayBuffer();
|
|
4678
|
+
const buffer = Buffer.from(bytes);
|
|
4679
|
+
const uploadDir = getWorkspacePath(targetPath);
|
|
4680
|
+
await fs12.mkdir(uploadDir, { recursive: true });
|
|
4681
|
+
const filePath = path11.join(uploadDir, file.name.toLowerCase());
|
|
4682
|
+
await fs12.writeFile(filePath, buffer);
|
|
4683
|
+
return jsonResponse({
|
|
4684
|
+
success: true,
|
|
4685
|
+
path: `${targetPath}/${file.name.toLowerCase()}`
|
|
4686
|
+
});
|
|
4687
|
+
} catch (error) {
|
|
4688
|
+
console.error("Error uploading font:", error);
|
|
4689
|
+
return jsonResponse({ error: "Failed to upload font" }, { status: 500 });
|
|
4690
|
+
}
|
|
4691
|
+
}
|
|
4692
|
+
async function handleFontsCreateFolder(request) {
|
|
4693
|
+
try {
|
|
4694
|
+
const { path: targetPath, name } = await request.json();
|
|
4695
|
+
if (!targetPath || !name) {
|
|
4696
|
+
return jsonResponse({ error: "Path and name are required" }, { status: 400 });
|
|
4697
|
+
}
|
|
4698
|
+
const allowedPaths = ["_fonts", "src/fonts", "src"];
|
|
4699
|
+
const isAllowed = allowedPaths.some(
|
|
4700
|
+
(allowed) => targetPath === allowed || targetPath.startsWith(allowed + "/")
|
|
4701
|
+
);
|
|
4702
|
+
if (!isAllowed) {
|
|
4703
|
+
return jsonResponse({ error: "Path not allowed" }, { status: 400 });
|
|
4704
|
+
}
|
|
4705
|
+
const folderPath = getWorkspacePath(targetPath, name.toLowerCase());
|
|
4706
|
+
await fs12.mkdir(folderPath, { recursive: true });
|
|
4707
|
+
return jsonResponse({
|
|
4708
|
+
success: true,
|
|
4709
|
+
path: `${targetPath}/${name.toLowerCase()}`
|
|
4710
|
+
});
|
|
4711
|
+
} catch (error) {
|
|
4712
|
+
console.error("Error creating folder:", error);
|
|
4713
|
+
return jsonResponse({ error: "Failed to create folder" }, { status: 500 });
|
|
4714
|
+
}
|
|
4715
|
+
}
|
|
4716
|
+
async function handleFontsDelete(request) {
|
|
4717
|
+
try {
|
|
4718
|
+
const { paths } = await request.json();
|
|
4719
|
+
if (!paths || !Array.isArray(paths) || paths.length === 0) {
|
|
4720
|
+
return jsonResponse({ error: "Paths are required" }, { status: 400 });
|
|
4721
|
+
}
|
|
4722
|
+
const allowedPaths = ["_fonts", "src/fonts"];
|
|
4723
|
+
for (const p of paths) {
|
|
4724
|
+
const isAllowed = allowedPaths.some(
|
|
4725
|
+
(allowed) => p.startsWith(allowed + "/")
|
|
4726
|
+
);
|
|
4727
|
+
if (!isAllowed) {
|
|
4728
|
+
return jsonResponse({ error: `Path not allowed: ${p}` }, { status: 400 });
|
|
4729
|
+
}
|
|
4730
|
+
}
|
|
4731
|
+
const deleted = [];
|
|
4732
|
+
const errors = [];
|
|
4733
|
+
for (const p of paths) {
|
|
4734
|
+
try {
|
|
4735
|
+
const fullPath = getWorkspacePath(p);
|
|
4736
|
+
const stat = await fs12.stat(fullPath);
|
|
4737
|
+
if (stat.isDirectory()) {
|
|
4738
|
+
await fs12.rm(fullPath, { recursive: true });
|
|
4739
|
+
} else {
|
|
4740
|
+
await fs12.unlink(fullPath);
|
|
4741
|
+
}
|
|
4742
|
+
deleted.push(p);
|
|
4743
|
+
} catch (err) {
|
|
4744
|
+
errors.push(`Failed to delete ${p}: ${err instanceof Error ? err.message : "Unknown error"}`);
|
|
4745
|
+
}
|
|
4746
|
+
}
|
|
4747
|
+
return jsonResponse({
|
|
4748
|
+
success: true,
|
|
4749
|
+
deleted,
|
|
4750
|
+
errors: errors.length > 0 ? errors : void 0
|
|
4751
|
+
});
|
|
4752
|
+
} catch (error) {
|
|
4753
|
+
console.error("Error deleting:", error);
|
|
4754
|
+
return jsonResponse({ error: "Failed to delete" }, { status: 500 });
|
|
4755
|
+
}
|
|
4756
|
+
}
|
|
4757
|
+
|
|
4575
4758
|
// src/server/index.ts
|
|
4576
4759
|
var __filename = fileURLToPath(import.meta.url);
|
|
4577
4760
|
var __dirname = dirname(__filename);
|
|
@@ -4617,15 +4800,16 @@ async function startServer(options) {
|
|
|
4617
4800
|
if (!process.env.STUDIO_DEV_SITE_URL && process.env.NEXT_PUBLIC_PRODUCTION_URL) {
|
|
4618
4801
|
process.env.STUDIO_DEV_SITE_URL = process.env.NEXT_PUBLIC_PRODUCTION_URL;
|
|
4619
4802
|
}
|
|
4803
|
+
const rawBodyPaths = ["/api/studio/upload", "/api/studio/fonts/upload"];
|
|
4620
4804
|
app.use((req, res, next) => {
|
|
4621
|
-
if (req.path
|
|
4805
|
+
if (rawBodyPaths.includes(req.path)) {
|
|
4622
4806
|
next();
|
|
4623
4807
|
} else {
|
|
4624
4808
|
express.json({ limit: "50mb" })(req, res, next);
|
|
4625
4809
|
}
|
|
4626
4810
|
});
|
|
4627
4811
|
app.use((req, res, next) => {
|
|
4628
|
-
if (req.path
|
|
4812
|
+
if (rawBodyPaths.includes(req.path)) {
|
|
4629
4813
|
next();
|
|
4630
4814
|
} else {
|
|
4631
4815
|
express.urlencoded({ extended: true, limit: "50mb" })(req, res, next);
|
|
@@ -4645,6 +4829,10 @@ async function startServer(options) {
|
|
|
4645
4829
|
"/api/studio/featured-image-options",
|
|
4646
4830
|
wrapHandler(handleGetFeaturedImageOptions)
|
|
4647
4831
|
);
|
|
4832
|
+
app.get("/api/studio/fonts/list", wrapHandler(handleFontsList));
|
|
4833
|
+
app.post("/api/studio/fonts/upload", wrapRawHandler(handleFontsUpload));
|
|
4834
|
+
app.post("/api/studio/fonts/create-folder", wrapHandler(handleFontsCreateFolder));
|
|
4835
|
+
app.post("/api/studio/fonts/delete", wrapHandler(handleFontsDelete));
|
|
4648
4836
|
app.post("/api/studio/upload", wrapRawHandler(handleUpload));
|
|
4649
4837
|
app.post("/api/studio/create-folder", wrapHandler(handleCreateFolder));
|
|
4650
4838
|
app.post("/api/studio/rename", wrapHandler(handleRename));
|