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:
Shni
2025-10-15 23:32:40 -05:00
parent 9aa2c79be1
commit 585a351785
3 changed files with 140 additions and 55 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "amayo",
"version": "2.0.22",
"version": "2.1.2",
"description": "",
"main": "src/main.ts",
"scripts": {

View File

@@ -86,8 +86,28 @@ export const renderTemplate = async (
locals: Record<string, any> = {},
statusCode = 200
) => {
const pageFile = path.join(viewsDir, "pages", `${template}.ejs`);
const layoutFile = path.join(viewsDir, "layouts", "layout.ejs");
const candidatePaths = [
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 =
typeof locals.hideNavbar !== "undefined" ? locals.hideNavbar : false;
locals.useDashboardNav =
@@ -101,19 +121,40 @@ export const renderTemplate = async (
? locals.selectedGuildId
: 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 = `${
locals.appName ?? pkg.name ?? "Amayo Bot"
} | Guía Completa`;
let dashboardNavHtml: string | null = null;
try {
if (locals.useDashboardNav) {
const partialPath = path.join(viewsDir, "partials", "dashboard_nav.ejs");
dashboardNavHtml = await ejs.renderFile(
partialPath,
{ ...locals },
{ async: true }
);
const partialCandidates = [
path.join(viewsDir, "partials", "dashboard_nav.ejs"),
path.join(__dirname, "..", "views", "partials", "dashboard_nav.ejs"),
path.join(
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) {
console.warn("Failed rendering dashboard_nav partial:", err);
@@ -123,54 +164,80 @@ export const renderTemplate = async (
try {
const shouldShowNavbar = !locals.hideNavbar && !locals.useDashboardNav;
if (shouldShowNavbar) {
const navPath = path.join(viewsDir, "partials", "navbar.ejs");
navbarHtml = await ejs.renderFile(
navPath,
{ appName: locals.appName ?? pkg.name ?? "Amayo Bot" },
{ async: true }
);
const navCandidates = [
path.join(viewsDir, "partials", "navbar.ejs"),
path.join(__dirname, "..", "views", "partials", "navbar.ejs"),
path.join(
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) {
console.warn("Failed rendering navbar partial:", err);
navbarHtml = null;
}
const html = await ejs.renderFile(
layoutFile,
{
head: null,
scripts: null,
version: locals.version ?? pkg.version ?? "2.0.0",
djsVersion:
locals.djsVersion ?? pkg?.dependencies?.["discord.js"] ?? "15.0.0-dev",
currentDateHuman:
locals.currentDateHuman ??
new Date().toLocaleDateString("es-ES", {
month: "long",
year: "numeric",
}),
hideNavbar:
typeof locals.hideNavbar !== "undefined" ? locals.hideNavbar : false,
useDashboardNav:
typeof locals.useDashboardNav !== "undefined"
? locals.useDashboardNav
: false,
selectedGuild:
typeof locals.selectedGuild !== "undefined"
? locals.selectedGuild
: null,
selectedGuildId:
typeof locals.selectedGuildId !== "undefined"
? locals.selectedGuildId
: null,
dashboardNav: dashboardNavHtml,
navbar: navbarHtml,
...locals,
title: locals.title ?? defaultTitle,
body: pageBody,
},
{ async: true }
);
let html: string;
if (!layoutFile) {
// If layout not available, use the page body directly
console.warn(
"Layout template not found, returning page body directly for:",
template
);
html = pageBody;
} else {
html = await ejs.renderFile(
layoutFile,
{
head: null,
scripts: null,
version: locals.version ?? pkg.version ?? "2.0.0",
djsVersion:
locals.djsVersion ??
pkg?.dependencies?.["discord.js"] ??
"15.0.0-dev",
currentDateHuman:
locals.currentDateHuman ??
new Date().toLocaleDateString("es-ES", {
month: "long",
year: "numeric",
}),
hideNavbar:
typeof locals.hideNavbar !== "undefined" ? locals.hideNavbar : false,
useDashboardNav:
typeof locals.useDashboardNav !== "undefined"
? locals.useDashboardNav
: false,
selectedGuild:
typeof locals.selectedGuild !== "undefined"
? locals.selectedGuild
: 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 etag = computeEtag(htmlBuffer);
@@ -214,3 +281,14 @@ export const renderTemplate = async (
res.writeHead(statusCode, applySecurityHeadersForRequest(req, headers));
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;
}

View File

@@ -12,11 +12,18 @@ import {
} from "node:zlib";
import path from "node:path";
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";
const publicDir = path.join(__dirname, "public");
const viewsDir = path.join(__dirname, "views");
// Prefer project src paths (in case process.cwd differs between environments)
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> = {
".html": "text/html; charset=utf-8",