Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 20 additions & 5 deletions src/utils/render.ts
Original file line number Diff line number Diff line change
@@ -1,63 +1,78 @@
/**
* 模板渲染 / 读取工具
*/
import {join} from "https://deno.land/std/path/mod.ts";
import {join, normalize, resolve, relative, isAbsolute} from "https://deno.land/std/path/mod.ts";
import {basePath} from "../config/constants.ts";
import {getMime} from "../utils/types.ts";
import {cyrb53_str} from "./common.ts";

// 解析静态资源真实路径并校验是否越出静态目录,防止路径穿越
function resolveStaticPath(subDir: string, pathname: string): string {
const baseDir = resolve(join(basePath, subDir));
const target = resolve(baseDir, normalize(pathname));
const rel = relative(baseDir, target);
if (rel === ".." || rel.startsWith(".." + "/") || rel.startsWith(".." + "\\") || isAbsolute(rel)) {
throw new Deno.errors.NotFound("Invalid static path");
}
return target;
}


export async function getJS(ctx, pathname, status = 200): Promise<string> {
try {
ctx.response.body = await Deno.readTextFile(join(basePath, `/static/js/${pathname}`));
ctx.response.body = await Deno.readTextFile(resolveStaticPath("/static/js", pathname));
ctx.response.status = status;
ctx.response.headers.set("Content-Type", "application/javascript");
// ctx.response.headers.set("Cache-Control", "public, max-age=86400, immutable");
const hash = cyrb53_str(`${pathname}-${ctx.response.body.length}`);
ctx.state.metadata = {etag: hash};
} catch (error) {
ctx.response.status = 404;
}
}

export async function getCSS(ctx, pathname, status = 200): Promise<string> {
try {
ctx.response.body = await Deno.readTextFile(join(basePath, `/static/css/${pathname}`));
ctx.response.body = await Deno.readTextFile(resolveStaticPath("/static/css", pathname));
ctx.response.status = status;
ctx.response.headers.set("Content-Type", "text/css");
// ctx.response.headers.set("Cache-Control", "public, max-age=86400, immutable");
const hash = cyrb53_str(`${pathname}-${ctx.response.body.length}`);
ctx.state.metadata = {etag: hash};
} catch (error) {
ctx.response.status = 404;
}
}

export async function getIMG(ctx, pathname, status = 200): Promise<string> {
try {
const extension = pathname.split('.').pop()?.toLowerCase() || '';
const contentType = getMime(extension) || 'application/octet-stream';
ctx.response.body = await Deno.readFile(join(basePath, `/static/img/${pathname}`));
ctx.response.body = await Deno.readFile(resolveStaticPath("/static/img", pathname));
ctx.response.status = status;

ctx.response.headers.set("Content-Type", contentType);
ctx.response.headers.set("Cache-Control", "public, max-age=31536000, immutable");
const hash = cyrb53_str(`${pathname}-${ctx.response.body.length}`);
ctx.state.metadata = {etag: hash};
} catch (error) {
ctx.response.status = 404;
}
}

export async function getFONTS(ctx, pathname, status = 200): Promise<string> {
try {
const extension = pathname.split('.').pop()?.toLowerCase() || '';
const contentType = getMime(extension) || 'application/octet-stream';
ctx.response.body = await Deno.readFile(join(basePath, `/static/css/fonts/${pathname}`));
ctx.response.body = await Deno.readFile(resolveStaticPath("/static/css/fonts", pathname));
ctx.response.status = status;

ctx.response.headers.set("Content-Type", contentType);
ctx.response.headers.set("Cache-Control", "public, max-age=31536000, immutable");
const hash = cyrb53_str(`${pathname}-${ctx.response.body.length}`);
ctx.state.metadata = {etag: hash};
} catch (error) {
ctx.response.status = 404;
}
}

Expand Down