Milestone admin and frontend stage
This commit is contained in:
+21
-3
@@ -66,6 +66,8 @@ const MEDIA_EXTENSIONS = new Set([
|
||||
".gif",
|
||||
".glb",
|
||||
".gltf",
|
||||
".ico",
|
||||
".json",
|
||||
".jpeg",
|
||||
".jpg",
|
||||
".m4v",
|
||||
@@ -73,6 +75,7 @@ const MEDIA_EXTENSIONS = new Set([
|
||||
".mp4",
|
||||
".png",
|
||||
".svg",
|
||||
".webmanifest",
|
||||
".webm",
|
||||
".webp",
|
||||
".woff",
|
||||
@@ -91,6 +94,7 @@ const REFERENCE_TEXT_EXTENSIONS = new Set([
|
||||
".ts",
|
||||
".tsx",
|
||||
".txt",
|
||||
".webmanifest",
|
||||
".xml",
|
||||
]);
|
||||
|
||||
@@ -100,6 +104,8 @@ const MIME = {
|
||||
".js": "text/javascript; charset=utf-8",
|
||||
".json": "application/json; charset=utf-8",
|
||||
".svg": "image/svg+xml",
|
||||
".ico": "image/x-icon",
|
||||
".webmanifest": "application/manifest+json; charset=utf-8",
|
||||
".png": "image/png",
|
||||
".jpg": "image/jpeg",
|
||||
".jpeg": "image/jpeg",
|
||||
@@ -150,6 +156,15 @@ function sendStream(req, res, status, stream, headers) {
|
||||
stream.pipe(res);
|
||||
}
|
||||
|
||||
function contentTypeForPath(filePath) {
|
||||
const normalizedPath = String(filePath || "").toLowerCase();
|
||||
if (normalizedPath.endsWith(".webmanifest") || normalizedPath.endsWith(".webmanifest.json")) {
|
||||
return "application/manifest+json; charset=utf-8";
|
||||
}
|
||||
|
||||
return MIME[extname(filePath).toLowerCase()] || "application/octet-stream";
|
||||
}
|
||||
|
||||
function sendJson(res, status, payload) {
|
||||
send(res, status, `${JSON.stringify(payload, null, 2)}\n`, "application/json; charset=utf-8");
|
||||
}
|
||||
@@ -225,6 +240,8 @@ function sanitizeUploadBucket(value) {
|
||||
function extensionFromMimeType(mimeType) {
|
||||
const map = {
|
||||
"image/svg+xml": ".svg",
|
||||
"image/x-icon": ".ico",
|
||||
"image/vnd.microsoft.icon": ".ico",
|
||||
"image/png": ".png",
|
||||
"image/jpeg": ".jpg",
|
||||
"image/gif": ".gif",
|
||||
@@ -233,6 +250,8 @@ function extensionFromMimeType(mimeType) {
|
||||
"video/mp4": ".mp4",
|
||||
"video/webm": ".webm",
|
||||
"video/quicktime": ".mov",
|
||||
"application/manifest+json": ".webmanifest",
|
||||
"application/json": ".json",
|
||||
"model/gltf-binary": ".glb",
|
||||
"model/gltf+json": ".gltf",
|
||||
"font/woff": ".woff",
|
||||
@@ -503,7 +522,7 @@ function serializedAssetReplacements(replacements) {
|
||||
}
|
||||
|
||||
function mediaKindFromExtension(extension) {
|
||||
if ([".avif", ".gif", ".jpeg", ".jpg", ".png", ".svg", ".webp"].includes(extension)) return "image";
|
||||
if ([".avif", ".gif", ".ico", ".jpeg", ".jpg", ".png", ".svg", ".webp"].includes(extension)) return "image";
|
||||
if ([".m4v", ".mov", ".mp4", ".webm"].includes(extension)) return "video";
|
||||
if ([".glb", ".gltf"].includes(extension)) return "model";
|
||||
if ([".woff", ".woff2"].includes(extension)) return "font";
|
||||
@@ -1383,8 +1402,7 @@ async function serveStatic(req, res) {
|
||||
return;
|
||||
}
|
||||
|
||||
const ext = extname(filePath);
|
||||
const contentType = MIME[ext] || "application/octet-stream";
|
||||
const contentType = contentTypeForPath(filePath);
|
||||
const range = req.headers.range;
|
||||
|
||||
if (range) {
|
||||
|
||||
@@ -720,6 +720,12 @@ const staticElements = {
|
||||
appIconAlt: "Иконка NODE.DC",
|
||||
appWordmarkSrc: "./assets/webflow/images/69ad96a28b06b7552a339137_logtype.svg?v=node-dc",
|
||||
appWordmarkAlt: "Логотип NODE.DC",
|
||||
faviconSvg: "./assets/uploads/favicon/icon-adaptive.svg",
|
||||
faviconIco: "./assets/uploads/favicon/favicon.ico",
|
||||
faviconAppleTouchIcon: "./assets/uploads/favicon/apple-touch-icon.png",
|
||||
faviconPng192: "./assets/uploads/favicon/icon-192.png",
|
||||
faviconPng512: "./assets/uploads/favicon/icon-512.png",
|
||||
faviconManifest: "./assets/uploads/favicon/manifest.webmanifest.json",
|
||||
},
|
||||
},
|
||||
editableFields: [
|
||||
@@ -753,6 +759,12 @@ const staticElements = {
|
||||
field("content.assets.appIconAlt", "Иконка приложения: alt"),
|
||||
field("content.assets.appWordmarkSrc", "Wordmark приложения", "media", "attr"),
|
||||
field("content.assets.appWordmarkAlt", "Wordmark приложения: alt"),
|
||||
field("content.assets.faviconSvg", "Favicon SVG", "media", "attr"),
|
||||
field("content.assets.faviconIco", "Favicon ICO", "media", "attr"),
|
||||
field("content.assets.faviconAppleTouchIcon", "Favicon Apple 180", "media", "attr"),
|
||||
field("content.assets.faviconPng192", "Favicon PNG 192", "media", "attr"),
|
||||
field("content.assets.faviconPng512", "Favicon PNG 512", "media", "attr"),
|
||||
field("content.assets.faviconManifest", "Favicon manifest", "media", "attr"),
|
||||
],
|
||||
};
|
||||
|
||||
@@ -772,6 +784,39 @@ function tokenizeStaticShell(source, definition) {
|
||||
const { navigation, notifications, dock } = definition.content;
|
||||
|
||||
let tokenized = source;
|
||||
const faviconHead = [
|
||||
`{{#if staticElements.content.assets.faviconSvg}}<link rel="icon" type="image/svg+xml" sizes="any" href="${shellToken(
|
||||
"attr",
|
||||
"content.assets.faviconSvg",
|
||||
)}"/>{{/if}}`,
|
||||
`{{#if staticElements.content.assets.faviconIco}}<link rel="icon" type="image/x-icon" href="${shellToken(
|
||||
"attr",
|
||||
"content.assets.faviconIco",
|
||||
)}"/>{{/if}}`,
|
||||
`{{#if staticElements.content.assets.faviconAppleTouchIcon}}<link rel="apple-touch-icon" sizes="180x180" href="${shellToken(
|
||||
"attr",
|
||||
"content.assets.faviconAppleTouchIcon",
|
||||
)}"/>{{/if}}`,
|
||||
`{{#if staticElements.content.assets.faviconPng192}}<link rel="icon" type="image/png" sizes="192x192" href="${shellToken(
|
||||
"attr",
|
||||
"content.assets.faviconPng192",
|
||||
)}"/>{{/if}}`,
|
||||
`{{#if staticElements.content.assets.faviconPng512}}<link rel="icon" type="image/png" sizes="512x512" href="${shellToken(
|
||||
"attr",
|
||||
"content.assets.faviconPng512",
|
||||
)}"/>{{/if}}`,
|
||||
`{{#if staticElements.content.assets.faviconManifest}}<link rel="manifest" href="${shellToken(
|
||||
"attr",
|
||||
"content.assets.faviconManifest",
|
||||
)}"/>{{/if}}`,
|
||||
].join("");
|
||||
|
||||
tokenized = replaceShellLiteral(
|
||||
tokenized,
|
||||
`<link href="./assets/webflow/images/69b83bc24411350dcdb86781_favicon.png" rel="shortcut icon" type="image/x-icon"/><link href="./assets/webflow/images/69b83bc6ad9e108637556ec5_webclip.png" rel="apple-touch-icon"/>`,
|
||||
faviconHead,
|
||||
"favicon links",
|
||||
);
|
||||
|
||||
tokenized = replaceShellLiteral(
|
||||
tokenized,
|
||||
|
||||
@@ -78,6 +78,11 @@ function isExternalHref(value) {
|
||||
return /^(https?:)?\/\//i.test(String(value || "")) || /^(mailto:|tel:)/i.test(String(value || ""));
|
||||
}
|
||||
|
||||
function isInertHref(value) {
|
||||
const href = String(value || "").trim();
|
||||
return !href || href === "#" || href === "#hero" || href.toLowerCase().startsWith("javascript:");
|
||||
}
|
||||
|
||||
function normalizeLinkForRender(item) {
|
||||
if (!item || typeof item !== "object") return;
|
||||
|
||||
@@ -103,12 +108,28 @@ function normalizeLinkForRender(item) {
|
||||
item.resolvedTarget = mode === "href" ? linkTarget : "";
|
||||
}
|
||||
|
||||
function normalizeDockItemForRender(item) {
|
||||
if (!item || typeof item !== "object") return;
|
||||
|
||||
const href = String(item.href || "").trim();
|
||||
const opensWindow = !isInertHref(href);
|
||||
|
||||
item.resolvedDockHref = href || "#";
|
||||
item.resolvedDockTarget = opensWindow ? "_blank" : "";
|
||||
item.resolvedDockRel = opensWindow ? "noopener noreferrer" : "";
|
||||
}
|
||||
|
||||
function applyRenderableLinks(model) {
|
||||
const staticLinks = model.staticElements?.content?.navigation?.links;
|
||||
if (Array.isArray(staticLinks)) {
|
||||
staticLinks.forEach(normalizeLinkForRender);
|
||||
}
|
||||
|
||||
const dockItems = model.staticElements?.content?.dock?.items;
|
||||
if (Array.isArray(dockItems)) {
|
||||
dockItems.forEach(normalizeDockItemForRender);
|
||||
}
|
||||
|
||||
for (const block of allBlocks(model)) {
|
||||
const footer = block.content?.footer;
|
||||
if (!footer) continue;
|
||||
|
||||
Reference in New Issue
Block a user