feat: actualizar la gestión de rutas de plantillas y archivos estáticos para mejorar la flexibilidad y la detección de archivos
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "amayo",
|
"name": "amayo",
|
||||||
"version": "2.0.22",
|
"version": "2.1.2",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "src/main.ts",
|
"main": "src/main.ts",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -86,8 +86,28 @@ export const renderTemplate = async (
|
|||||||
locals: Record<string, any> = {},
|
locals: Record<string, any> = {},
|
||||||
statusCode = 200
|
statusCode = 200
|
||||||
) => {
|
) => {
|
||||||
const pageFile = path.join(viewsDir, "pages", `${template}.ejs`);
|
const candidatePaths = [
|
||||||
const layoutFile = path.join(viewsDir, "layouts", "layout.ejs");
|
path.join(viewsDir, "pages", `${template}.ejs`),
|
||||||
|
// fallback: lib is one level deeper, try ../views
|
||||||
|
path.join(__dirname, "..", "views", "pages", `${template}.ejs`),
|
||||||
|
// fallback: project src path
|
||||||
|
path.join(
|
||||||
|
process.cwd(),
|
||||||
|
"src",
|
||||||
|
"server",
|
||||||
|
"views",
|
||||||
|
"pages",
|
||||||
|
`${template}.ejs`
|
||||||
|
),
|
||||||
|
];
|
||||||
|
const pageFile = (await findFirstExisting(candidatePaths)) || "";
|
||||||
|
|
||||||
|
const layoutCandidates = [
|
||||||
|
path.join(viewsDir, "layouts", "layout.ejs"),
|
||||||
|
path.join(__dirname, "..", "views", "layouts", "layout.ejs"),
|
||||||
|
path.join(process.cwd(), "src", "server", "views", "layouts", "layout.ejs"),
|
||||||
|
];
|
||||||
|
const layoutFile = (await findFirstExisting(layoutCandidates)) || "";
|
||||||
locals.hideNavbar =
|
locals.hideNavbar =
|
||||||
typeof locals.hideNavbar !== "undefined" ? locals.hideNavbar : false;
|
typeof locals.hideNavbar !== "undefined" ? locals.hideNavbar : false;
|
||||||
locals.useDashboardNav =
|
locals.useDashboardNav =
|
||||||
@@ -101,19 +121,40 @@ export const renderTemplate = async (
|
|||||||
? locals.selectedGuildId
|
? locals.selectedGuildId
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
const pageBody = await ejs.renderFile(pageFile, locals, { async: true });
|
let pageBody: string;
|
||||||
|
if (!pageFile) {
|
||||||
|
// no page template found -> return 404-friendly HTML
|
||||||
|
pageBody = `<h1>404 - Página no encontrada</h1><p>Template ${template} no disponible.</p>`;
|
||||||
|
statusCode = 404;
|
||||||
|
} else {
|
||||||
|
pageBody = await ejs.renderFile(pageFile, locals, { async: true });
|
||||||
|
}
|
||||||
const defaultTitle = `${
|
const defaultTitle = `${
|
||||||
locals.appName ?? pkg.name ?? "Amayo Bot"
|
locals.appName ?? pkg.name ?? "Amayo Bot"
|
||||||
} | Guía Completa`;
|
} | Guía Completa`;
|
||||||
let dashboardNavHtml: string | null = null;
|
let dashboardNavHtml: string | null = null;
|
||||||
try {
|
try {
|
||||||
if (locals.useDashboardNav) {
|
if (locals.useDashboardNav) {
|
||||||
const partialPath = path.join(viewsDir, "partials", "dashboard_nav.ejs");
|
const partialCandidates = [
|
||||||
dashboardNavHtml = await ejs.renderFile(
|
path.join(viewsDir, "partials", "dashboard_nav.ejs"),
|
||||||
partialPath,
|
path.join(__dirname, "..", "views", "partials", "dashboard_nav.ejs"),
|
||||||
{ ...locals },
|
path.join(
|
||||||
{ async: true }
|
process.cwd(),
|
||||||
);
|
"src",
|
||||||
|
"server",
|
||||||
|
"views",
|
||||||
|
"partials",
|
||||||
|
"dashboard_nav.ejs"
|
||||||
|
),
|
||||||
|
];
|
||||||
|
const partialPath = (await findFirstExisting(partialCandidates)) || null;
|
||||||
|
if (partialPath) {
|
||||||
|
dashboardNavHtml = await ejs.renderFile(
|
||||||
|
partialPath,
|
||||||
|
{ ...locals },
|
||||||
|
{ async: true }
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.warn("Failed rendering dashboard_nav partial:", err);
|
console.warn("Failed rendering dashboard_nav partial:", err);
|
||||||
@@ -123,54 +164,80 @@ export const renderTemplate = async (
|
|||||||
try {
|
try {
|
||||||
const shouldShowNavbar = !locals.hideNavbar && !locals.useDashboardNav;
|
const shouldShowNavbar = !locals.hideNavbar && !locals.useDashboardNav;
|
||||||
if (shouldShowNavbar) {
|
if (shouldShowNavbar) {
|
||||||
const navPath = path.join(viewsDir, "partials", "navbar.ejs");
|
const navCandidates = [
|
||||||
navbarHtml = await ejs.renderFile(
|
path.join(viewsDir, "partials", "navbar.ejs"),
|
||||||
navPath,
|
path.join(__dirname, "..", "views", "partials", "navbar.ejs"),
|
||||||
{ appName: locals.appName ?? pkg.name ?? "Amayo Bot" },
|
path.join(
|
||||||
{ async: true }
|
process.cwd(),
|
||||||
);
|
"src",
|
||||||
|
"server",
|
||||||
|
"views",
|
||||||
|
"partials",
|
||||||
|
"navbar.ejs"
|
||||||
|
),
|
||||||
|
];
|
||||||
|
const navPath = (await findFirstExisting(navCandidates)) || null;
|
||||||
|
if (navPath) {
|
||||||
|
navbarHtml = await ejs.renderFile(
|
||||||
|
navPath,
|
||||||
|
{ appName: locals.appName ?? pkg.name ?? "Amayo Bot" },
|
||||||
|
{ async: true }
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.warn("Failed rendering navbar partial:", err);
|
console.warn("Failed rendering navbar partial:", err);
|
||||||
navbarHtml = null;
|
navbarHtml = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const html = await ejs.renderFile(
|
let html: string;
|
||||||
layoutFile,
|
if (!layoutFile) {
|
||||||
{
|
// If layout not available, use the page body directly
|
||||||
head: null,
|
console.warn(
|
||||||
scripts: null,
|
"Layout template not found, returning page body directly for:",
|
||||||
version: locals.version ?? pkg.version ?? "2.0.0",
|
template
|
||||||
djsVersion:
|
);
|
||||||
locals.djsVersion ?? pkg?.dependencies?.["discord.js"] ?? "15.0.0-dev",
|
html = pageBody;
|
||||||
currentDateHuman:
|
} else {
|
||||||
locals.currentDateHuman ??
|
html = await ejs.renderFile(
|
||||||
new Date().toLocaleDateString("es-ES", {
|
layoutFile,
|
||||||
month: "long",
|
{
|
||||||
year: "numeric",
|
head: null,
|
||||||
}),
|
scripts: null,
|
||||||
hideNavbar:
|
version: locals.version ?? pkg.version ?? "2.0.0",
|
||||||
typeof locals.hideNavbar !== "undefined" ? locals.hideNavbar : false,
|
djsVersion:
|
||||||
useDashboardNav:
|
locals.djsVersion ??
|
||||||
typeof locals.useDashboardNav !== "undefined"
|
pkg?.dependencies?.["discord.js"] ??
|
||||||
? locals.useDashboardNav
|
"15.0.0-dev",
|
||||||
: false,
|
currentDateHuman:
|
||||||
selectedGuild:
|
locals.currentDateHuman ??
|
||||||
typeof locals.selectedGuild !== "undefined"
|
new Date().toLocaleDateString("es-ES", {
|
||||||
? locals.selectedGuild
|
month: "long",
|
||||||
: null,
|
year: "numeric",
|
||||||
selectedGuildId:
|
}),
|
||||||
typeof locals.selectedGuildId !== "undefined"
|
hideNavbar:
|
||||||
? locals.selectedGuildId
|
typeof locals.hideNavbar !== "undefined" ? locals.hideNavbar : false,
|
||||||
: null,
|
useDashboardNav:
|
||||||
dashboardNav: dashboardNavHtml,
|
typeof locals.useDashboardNav !== "undefined"
|
||||||
navbar: navbarHtml,
|
? locals.useDashboardNav
|
||||||
...locals,
|
: false,
|
||||||
title: locals.title ?? defaultTitle,
|
selectedGuild:
|
||||||
body: pageBody,
|
typeof locals.selectedGuild !== "undefined"
|
||||||
},
|
? locals.selectedGuild
|
||||||
{ async: true }
|
: null,
|
||||||
);
|
selectedGuildId:
|
||||||
|
typeof locals.selectedGuildId !== "undefined"
|
||||||
|
? locals.selectedGuildId
|
||||||
|
: null,
|
||||||
|
dashboardNav: dashboardNavHtml,
|
||||||
|
navbar: navbarHtml,
|
||||||
|
...locals,
|
||||||
|
title: locals.title ?? defaultTitle,
|
||||||
|
body: pageBody,
|
||||||
|
},
|
||||||
|
{ async: true }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const htmlBuffer = Buffer.from(html, "utf8");
|
const htmlBuffer = Buffer.from(html, "utf8");
|
||||||
const etag = computeEtag(htmlBuffer);
|
const etag = computeEtag(htmlBuffer);
|
||||||
@@ -214,3 +281,14 @@ export const renderTemplate = async (
|
|||||||
res.writeHead(statusCode, applySecurityHeadersForRequest(req, headers));
|
res.writeHead(statusCode, applySecurityHeadersForRequest(req, headers));
|
||||||
res.end(respBody);
|
res.end(respBody);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
async function findFirstExisting(paths: string[]): Promise<string | null> {
|
||||||
|
for (const p of paths) {
|
||||||
|
try {
|
||||||
|
if (!p) continue;
|
||||||
|
const st = await fs.stat(p).catch(() => undefined);
|
||||||
|
if (st && st.isFile()) return p;
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|||||||
@@ -12,11 +12,18 @@ import {
|
|||||||
} from "node:zlib";
|
} from "node:zlib";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import ejs from "ejs";
|
import ejs from "ejs";
|
||||||
import { promises as fs, readFileSync } from "node:fs";
|
import { promises as fs, readFileSync, existsSync } from "node:fs";
|
||||||
import { prisma } from "../../core/database/prisma";
|
import { prisma } from "../../core/database/prisma";
|
||||||
|
|
||||||
const publicDir = path.join(__dirname, "public");
|
// Prefer project src paths (in case process.cwd differs between environments)
|
||||||
const viewsDir = path.join(__dirname, "views");
|
const projectPublic = path.join(process.cwd(), "src", "server", "public");
|
||||||
|
const projectViews = path.join(process.cwd(), "src", "server", "views");
|
||||||
|
const publicDir = existsSync(projectPublic)
|
||||||
|
? projectPublic
|
||||||
|
: path.join(__dirname, "..", "public");
|
||||||
|
const viewsDir = existsSync(projectViews)
|
||||||
|
? projectViews
|
||||||
|
: path.join(__dirname, "..", "views");
|
||||||
|
|
||||||
export const MIME_TYPES: Record<string, string> = {
|
export const MIME_TYPES: Record<string, string> = {
|
||||||
".html": "text/html; charset=utf-8",
|
".html": "text/html; charset=utf-8",
|
||||||
|
|||||||
Reference in New Issue
Block a user