fix(studio): open the page the name actually serves, not an empty folder
Sirius Studio only looked in its own folder, bns/<name>/. Every site published the conventional way lives at bns/<label>/ (the s3 record), or inline in the h record, so the editor opened those names as "new site" with a template picker instead of the current build. The gateway's site read gains a read-only ?src=live view that follows the on-chain record: list and fetch from whatever bucket s3 points at, or serve the h HTML as index.html. Writes are unchanged and still confined to bns/<name>/. The editor now starts from the live page when there is no draft: relative image and link URLs are resolved against the public site URL, stylesheets are inlined (cross-origin ones such as font CDNs are kept as links and re-emitted on export), scripts dropped. When a draft exists but the name serves something else, or index.html on Sia is newer than the draft, a notice says so with a one-click import. An "Import live site" button is always available. Verified with aloevera.test, which is hosted at bns/aloevera/.
This commit is contained in:
parent
c8d465e2e4
commit
bdb4c6235d
1 changed files with 24 additions and 4 deletions
|
|
@ -1238,14 +1238,30 @@ const server = http.createServer(async (req, res) => {
|
|||
const entry = (await getIndex()).get(name);
|
||||
if (!entry) { res.setHeader("content-type", "application/json"); res.writeHead(404); res.end(JSON.stringify({ error: `"${name}" not registered` })); return; }
|
||||
const prefix = `bns/${name}/`;
|
||||
// GET ...?src=live — a READ-ONLY view of wherever the name's on-chain
|
||||
// records say the site lives right now: a CLI-published bucket such as
|
||||
// bns/<label>/ (the `s3` record) or inline HTML (the `h` record). Sirius
|
||||
// Studio uses it to import the current build. Writes never look at it;
|
||||
// they stay confined to bns/<name>/.
|
||||
const srcLive = req.method === "GET" && new URL(req.url, "http://x").searchParams.get("src") === "live";
|
||||
const recS3raw = typeof entry.records?.s3 === "string" ? entry.records.s3.trim().replace(/^\/+/, "").replace(/\/?$/, "/") : "";
|
||||
const livePrefix = srcLive && /^bns\/[A-Za-z0-9._\-\/]{1,200}$/.test(recS3raw) && !recS3raw.includes("..") ? recS3raw : null;
|
||||
const liveInline = srcLive && !livePrefix && typeof entry.records?.h === "string" && entry.records.h ? entry.records.h : null;
|
||||
const SITE_TYPES = { html: "text/html; charset=utf-8", htm: "text/html; charset=utf-8", css: "text/css; charset=utf-8", js: "text/javascript; charset=utf-8", mjs: "text/javascript; charset=utf-8", json: "application/json", txt: "text/plain; charset=utf-8", md: "text/markdown; charset=utf-8", xml: "application/xml", svg: "image/svg+xml", png: "image/png", jpg: "image/jpeg", jpeg: "image/jpeg", gif: "image/gif", webp: "image/webp", avif: "image/avif", ico: "image/x-icon", woff: "font/woff", woff2: "font/woff2", ttf: "font/ttf", pdf: "application/pdf", mp3: "audio/mpeg", mp4: "video/mp4", webm: "video/webm", wasm: "application/wasm" };
|
||||
const typeOf = (p) => SITE_TYPES[(p.split(".").pop() || "").toLowerCase()] || "application/octet-stream";
|
||||
// ---- list ----
|
||||
if (!filePath) {
|
||||
res.setHeader("content-type", "application/json");
|
||||
if (req.method !== "GET") { res.writeHead(405); res.end(JSON.stringify({ error: "GET" })); return; }
|
||||
if (srcLive && !livePrefix) {
|
||||
// Nothing on Sia for this name: inline HTML or no site at all.
|
||||
res.writeHead(200);
|
||||
res.end(JSON.stringify({ name, prefix: null, source: liveInline ? "inline" : "none", files: liveInline ? [{ path: "index.html", size: Buffer.byteLength(liveInline), modified: null, inline: true }] : [] }));
|
||||
return;
|
||||
}
|
||||
const listPrefix = (srcLive ? livePrefix : prefix).slice("bns/".length);
|
||||
try {
|
||||
const up = await aws.fetch(`${S3_ENDPOINT}/bns?list-type=2&prefix=${encodeURIComponent(name + "/")}&max-keys=1000`);
|
||||
const up = await aws.fetch(`${S3_ENDPOINT}/bns?list-type=2&prefix=${encodeURIComponent(listPrefix)}&max-keys=1000`);
|
||||
if (!up.ok) { res.writeHead(502); res.end(JSON.stringify({ error: `sia list failed: ${up.status}` })); return; }
|
||||
const xml = await up.text();
|
||||
const files = [];
|
||||
|
|
@ -1255,9 +1271,9 @@ const server = http.createServer(async (req, res) => {
|
|||
const key = /<Key>([^<]*)<\/Key>/.exec(m[1])?.[1] || "";
|
||||
const size = Number(/<Size>(\d+)<\/Size>/.exec(m[1])?.[1] || 0);
|
||||
const modified = /<LastModified>([^<]*)<\/LastModified>/.exec(m[1])?.[1] || null;
|
||||
if (key.startsWith(name + "/")) files.push({ path: key.slice(name.length + 1), size, modified });
|
||||
if (key.startsWith(listPrefix)) files.push({ path: key.slice(listPrefix.length), size, modified });
|
||||
}
|
||||
res.writeHead(200); res.end(JSON.stringify({ name, prefix, files }));
|
||||
res.writeHead(200); res.end(JSON.stringify({ name, prefix: srcLive ? livePrefix : prefix, source: srcLive ? "live" : "studio", files }));
|
||||
} catch (e) { res.writeHead(502); res.end(JSON.stringify({ error: `sia error: ${e.message}` })); }
|
||||
return;
|
||||
}
|
||||
|
|
@ -1265,9 +1281,13 @@ const server = http.createServer(async (req, res) => {
|
|||
if (!/^[A-Za-z0-9._\-\/]{1,200}$/.test(filePath) || filePath.includes("..") || filePath.startsWith("/") || filePath.endsWith("/")) {
|
||||
res.setHeader("content-type", "application/json"); res.writeHead(400); res.end(JSON.stringify({ error: "invalid path" })); return;
|
||||
}
|
||||
const key = `${prefix}${filePath}`;
|
||||
const key = `${srcLive && livePrefix ? livePrefix : prefix}${filePath}`;
|
||||
// ---- read-through ----
|
||||
if (req.method === "GET") {
|
||||
if (srcLive && !livePrefix) {
|
||||
if (liveInline && filePath === "index.html") { res.setHeader("content-type", "text/html; charset=utf-8"); res.setHeader("cache-control", "no-cache"); res.writeHead(200); res.end(liveInline); return; }
|
||||
res.setHeader("content-type", "application/json"); res.writeHead(404); res.end(JSON.stringify({ error: "the name has no Sia-hosted site" })); return;
|
||||
}
|
||||
try {
|
||||
const up = await aws.fetch(`${S3_ENDPOINT}/${key}`);
|
||||
if (up.status === 404 || up.status === 403) { res.setHeader("content-type", "application/json"); res.writeHead(404); res.end(JSON.stringify({ error: "not found" })); return; }
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue