create-volt 0.56.0 → 0.56.1
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/CHANGELOG.md +10 -0
- package/package.json +1 -1
- package/templates/blog/server.js +14 -0
- package/templates/business/server.js +14 -0
- package/templates/default/server.js +14 -0
- package/templates/docs/server.js +14 -0
- package/templates/starter/server.js +14 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,15 @@ All notable changes to `create-volt` are documented here. The format follows
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/), and this project adheres to
|
|
5
5
|
[Semantic Versioning](https://semver.org/).
|
|
6
6
|
|
|
7
|
+
## [0.56.1] - 2026-07-05
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
- **Media library thumbnails / editor image previews showed as broken inside the
|
|
11
|
+
config.** The config server served a fixed asset allowlist and had no `/media/`
|
|
12
|
+
route, so uploaded files 404'd in the config UI — even though they uploaded fine
|
|
13
|
+
and render on the live site (the running app serves them via `express.static`).
|
|
14
|
+
The config now serves `public/media/<name>` with a path-traversal guard.
|
|
15
|
+
|
|
7
16
|
## [0.56.0] - 2026-07-05
|
|
8
17
|
|
|
9
18
|
### Added
|
|
@@ -745,6 +754,7 @@ All notable changes to `create-volt` are documented here. The format follows
|
|
|
745
754
|
watching and full-page hot reload. Supports `--skip-install` and `--force`,
|
|
746
755
|
and auto-detects npm / pnpm / yarn / bun for the install step.
|
|
747
756
|
|
|
757
|
+
[0.56.1]: https://github.com/MIR-2025/volt/releases/tag/v0.56.1
|
|
748
758
|
[0.56.0]: https://github.com/MIR-2025/volt/releases/tag/v0.56.0
|
|
749
759
|
[0.55.1]: https://github.com/MIR-2025/volt/releases/tag/v0.55.1
|
|
750
760
|
[0.55.0]: https://github.com/MIR-2025/volt/releases/tag/v0.55.0
|
package/package.json
CHANGED
package/templates/blog/server.js
CHANGED
|
@@ -313,6 +313,20 @@ function startSetup() {
|
|
|
313
313
|
res.setHeader("Content-Type", assets[p][0]);
|
|
314
314
|
return res.end(assets[p][1]);
|
|
315
315
|
}
|
|
316
|
+
// Serve uploaded media so library thumbnails + editor previews render inside the
|
|
317
|
+
// config (the running app serves these via express.static; the config didn't).
|
|
318
|
+
if (req.method === "GET" && p.startsWith("/media/")) {
|
|
319
|
+
const base = path.join(__dirname, "public", "media");
|
|
320
|
+
const f = path.resolve(base, decodeURIComponent(p.slice("/media/".length)));
|
|
321
|
+
if ((f === base || f.startsWith(base + path.sep)) && fs.existsSync(f) && fs.statSync(f).isFile()) {
|
|
322
|
+
const ext = (f.split(".").pop() || "").toLowerCase();
|
|
323
|
+
const mime = { png: "image/png", jpg: "image/jpeg", jpeg: "image/jpeg", gif: "image/gif", webp: "image/webp", avif: "image/avif", svg: "image/svg+xml", bmp: "image/bmp", ico: "image/x-icon", mp4: "video/mp4", webm: "video/webm", mov: "video/quicktime", ogv: "video/ogg", m4v: "video/x-m4v", ogg: "audio/ogg", mp3: "audio/mpeg", wav: "audio/wav" }[ext] || "application/octet-stream";
|
|
324
|
+
res.setHeader("Content-Type", mime);
|
|
325
|
+
return res.end(fs.readFileSync(f));
|
|
326
|
+
}
|
|
327
|
+
res.statusCode = 404;
|
|
328
|
+
return res.end("not found");
|
|
329
|
+
}
|
|
316
330
|
if (req.method === "GET" && p === "/setup/state") {
|
|
317
331
|
res.setHeader("Content-Type", "application/json");
|
|
318
332
|
return res.end(JSON.stringify({ available: availableAddons(), themes: availableThemes(), current: readEnvFile(), defaultPort: DEFAULT_PORT, configDefaultPort: CONFIG_DEFAULT_PORT }));
|
|
@@ -313,6 +313,20 @@ function startSetup() {
|
|
|
313
313
|
res.setHeader("Content-Type", assets[p][0]);
|
|
314
314
|
return res.end(assets[p][1]);
|
|
315
315
|
}
|
|
316
|
+
// Serve uploaded media so library thumbnails + editor previews render inside the
|
|
317
|
+
// config (the running app serves these via express.static; the config didn't).
|
|
318
|
+
if (req.method === "GET" && p.startsWith("/media/")) {
|
|
319
|
+
const base = path.join(__dirname, "public", "media");
|
|
320
|
+
const f = path.resolve(base, decodeURIComponent(p.slice("/media/".length)));
|
|
321
|
+
if ((f === base || f.startsWith(base + path.sep)) && fs.existsSync(f) && fs.statSync(f).isFile()) {
|
|
322
|
+
const ext = (f.split(".").pop() || "").toLowerCase();
|
|
323
|
+
const mime = { png: "image/png", jpg: "image/jpeg", jpeg: "image/jpeg", gif: "image/gif", webp: "image/webp", avif: "image/avif", svg: "image/svg+xml", bmp: "image/bmp", ico: "image/x-icon", mp4: "video/mp4", webm: "video/webm", mov: "video/quicktime", ogv: "video/ogg", m4v: "video/x-m4v", ogg: "audio/ogg", mp3: "audio/mpeg", wav: "audio/wav" }[ext] || "application/octet-stream";
|
|
324
|
+
res.setHeader("Content-Type", mime);
|
|
325
|
+
return res.end(fs.readFileSync(f));
|
|
326
|
+
}
|
|
327
|
+
res.statusCode = 404;
|
|
328
|
+
return res.end("not found");
|
|
329
|
+
}
|
|
316
330
|
if (req.method === "GET" && p === "/setup/state") {
|
|
317
331
|
res.setHeader("Content-Type", "application/json");
|
|
318
332
|
return res.end(JSON.stringify({ available: availableAddons(), themes: availableThemes(), current: readEnvFile(), defaultPort: DEFAULT_PORT, configDefaultPort: CONFIG_DEFAULT_PORT }));
|
|
@@ -313,6 +313,20 @@ function startSetup() {
|
|
|
313
313
|
res.setHeader("Content-Type", assets[p][0]);
|
|
314
314
|
return res.end(assets[p][1]);
|
|
315
315
|
}
|
|
316
|
+
// Serve uploaded media so library thumbnails + editor previews render inside the
|
|
317
|
+
// config (the running app serves these via express.static; the config didn't).
|
|
318
|
+
if (req.method === "GET" && p.startsWith("/media/")) {
|
|
319
|
+
const base = path.join(__dirname, "public", "media");
|
|
320
|
+
const f = path.resolve(base, decodeURIComponent(p.slice("/media/".length)));
|
|
321
|
+
if ((f === base || f.startsWith(base + path.sep)) && fs.existsSync(f) && fs.statSync(f).isFile()) {
|
|
322
|
+
const ext = (f.split(".").pop() || "").toLowerCase();
|
|
323
|
+
const mime = { png: "image/png", jpg: "image/jpeg", jpeg: "image/jpeg", gif: "image/gif", webp: "image/webp", avif: "image/avif", svg: "image/svg+xml", bmp: "image/bmp", ico: "image/x-icon", mp4: "video/mp4", webm: "video/webm", mov: "video/quicktime", ogv: "video/ogg", m4v: "video/x-m4v", ogg: "audio/ogg", mp3: "audio/mpeg", wav: "audio/wav" }[ext] || "application/octet-stream";
|
|
324
|
+
res.setHeader("Content-Type", mime);
|
|
325
|
+
return res.end(fs.readFileSync(f));
|
|
326
|
+
}
|
|
327
|
+
res.statusCode = 404;
|
|
328
|
+
return res.end("not found");
|
|
329
|
+
}
|
|
316
330
|
if (req.method === "GET" && p === "/setup/state") {
|
|
317
331
|
res.setHeader("Content-Type", "application/json");
|
|
318
332
|
return res.end(JSON.stringify({ available: availableAddons(), themes: availableThemes(), current: readEnvFile(), defaultPort: DEFAULT_PORT, configDefaultPort: CONFIG_DEFAULT_PORT }));
|
package/templates/docs/server.js
CHANGED
|
@@ -313,6 +313,20 @@ function startSetup() {
|
|
|
313
313
|
res.setHeader("Content-Type", assets[p][0]);
|
|
314
314
|
return res.end(assets[p][1]);
|
|
315
315
|
}
|
|
316
|
+
// Serve uploaded media so library thumbnails + editor previews render inside the
|
|
317
|
+
// config (the running app serves these via express.static; the config didn't).
|
|
318
|
+
if (req.method === "GET" && p.startsWith("/media/")) {
|
|
319
|
+
const base = path.join(__dirname, "public", "media");
|
|
320
|
+
const f = path.resolve(base, decodeURIComponent(p.slice("/media/".length)));
|
|
321
|
+
if ((f === base || f.startsWith(base + path.sep)) && fs.existsSync(f) && fs.statSync(f).isFile()) {
|
|
322
|
+
const ext = (f.split(".").pop() || "").toLowerCase();
|
|
323
|
+
const mime = { png: "image/png", jpg: "image/jpeg", jpeg: "image/jpeg", gif: "image/gif", webp: "image/webp", avif: "image/avif", svg: "image/svg+xml", bmp: "image/bmp", ico: "image/x-icon", mp4: "video/mp4", webm: "video/webm", mov: "video/quicktime", ogv: "video/ogg", m4v: "video/x-m4v", ogg: "audio/ogg", mp3: "audio/mpeg", wav: "audio/wav" }[ext] || "application/octet-stream";
|
|
324
|
+
res.setHeader("Content-Type", mime);
|
|
325
|
+
return res.end(fs.readFileSync(f));
|
|
326
|
+
}
|
|
327
|
+
res.statusCode = 404;
|
|
328
|
+
return res.end("not found");
|
|
329
|
+
}
|
|
316
330
|
if (req.method === "GET" && p === "/setup/state") {
|
|
317
331
|
res.setHeader("Content-Type", "application/json");
|
|
318
332
|
return res.end(JSON.stringify({ available: availableAddons(), themes: availableThemes(), current: readEnvFile(), defaultPort: DEFAULT_PORT, configDefaultPort: CONFIG_DEFAULT_PORT }));
|
|
@@ -338,6 +338,20 @@ function startSetup() {
|
|
|
338
338
|
res.setHeader("Content-Type", assets[p][0]);
|
|
339
339
|
return res.end(assets[p][1]);
|
|
340
340
|
}
|
|
341
|
+
// Serve uploaded media so library thumbnails + editor previews render inside the
|
|
342
|
+
// config (the running app serves these via express.static; the config didn't).
|
|
343
|
+
if (req.method === "GET" && p.startsWith("/media/")) {
|
|
344
|
+
const base = path.join(__dirname, "public", "media");
|
|
345
|
+
const f = path.resolve(base, decodeURIComponent(p.slice("/media/".length)));
|
|
346
|
+
if ((f === base || f.startsWith(base + path.sep)) && fs.existsSync(f) && fs.statSync(f).isFile()) {
|
|
347
|
+
const ext = (f.split(".").pop() || "").toLowerCase();
|
|
348
|
+
const mime = { png: "image/png", jpg: "image/jpeg", jpeg: "image/jpeg", gif: "image/gif", webp: "image/webp", avif: "image/avif", svg: "image/svg+xml", bmp: "image/bmp", ico: "image/x-icon", mp4: "video/mp4", webm: "video/webm", mov: "video/quicktime", ogv: "video/ogg", m4v: "video/x-m4v", ogg: "audio/ogg", mp3: "audio/mpeg", wav: "audio/wav" }[ext] || "application/octet-stream";
|
|
349
|
+
res.setHeader("Content-Type", mime);
|
|
350
|
+
return res.end(fs.readFileSync(f));
|
|
351
|
+
}
|
|
352
|
+
res.statusCode = 404;
|
|
353
|
+
return res.end("not found");
|
|
354
|
+
}
|
|
341
355
|
if (req.method === "GET" && p === "/setup/state") {
|
|
342
356
|
res.setHeader("Content-Type", "application/json");
|
|
343
357
|
return res.end(JSON.stringify({ available: availableAddons(), themes: availableThemes(), current: readEnvFile(), defaultPort: DEFAULT_PORT, configDefaultPort: CONFIG_DEFAULT_PORT }));
|