169 lines
4.9 KiB
TypeScript
169 lines
4.9 KiB
TypeScript
import type { ABC } from "../deps.ts";
|
|
import {
|
|
extractPackagePath,
|
|
getFilePath,
|
|
getFile,
|
|
getOneOf,
|
|
getAbsolutePackageVersion,
|
|
sortVersions,
|
|
} from "../utils.ts";
|
|
|
|
import { Hash, Path } from "../deps.ts";
|
|
import db, { IPackage } from "../db.ts";
|
|
import * as Storage from "../storage.ts";
|
|
|
|
const MAX_CACHE_AGE = 60 * 30; // 30 Minutes
|
|
|
|
const CACHE_CONTROL = "public, max-age=" + MAX_CACHE_AGE;
|
|
|
|
export default function views(g: ABC.Application) {
|
|
g.get("/", async (ctx) => {
|
|
ctx.response.headers.set("cache-control", CACHE_CONTROL);
|
|
|
|
const search = ctx.queryParams.q;
|
|
|
|
let packages: IPackage[] = [];
|
|
if (search && search !== "") {
|
|
packages = await db.package.find({
|
|
name: RegExp(`${search}`),
|
|
});
|
|
} else {
|
|
packages = await db.package.find({});
|
|
}
|
|
|
|
|
|
await ctx.render("index", {
|
|
packages: packages.reverse(),
|
|
search,
|
|
});
|
|
|
|
const etag =
|
|
"W/" +
|
|
Hash.createHash("sha3-256")
|
|
.update(
|
|
packages
|
|
.map((e) => {
|
|
const sorted = e.versions.sort(sortVersions).reverse();
|
|
return e.name + sorted[0];
|
|
})
|
|
.join(":")
|
|
)
|
|
.toString("base64");
|
|
|
|
ctx.response.headers.set("cache-control", CACHE_CONTROL);
|
|
ctx.response.headers.set("E-Tag", etag);
|
|
});
|
|
|
|
g.get("/package/:package", async (ctx) => {
|
|
const [packageName, packageVersion] = extractPackagePath(
|
|
ctx.params.package
|
|
);
|
|
|
|
const pkg = await db.package.findOne({ name: packageName });
|
|
|
|
if (!pkg) {
|
|
ctx.response.status = 404;
|
|
ctx.response.body = "// Package not found!";
|
|
return;
|
|
}
|
|
|
|
|
|
const readmeContentRaw = (await getOneOf(
|
|
pkg.name,
|
|
packageVersion || pkg.versions.sort(sortVersions).reverse()[0],
|
|
[pkg.readme, "README.md", "readme.md", "Readme.md"]
|
|
))?.data;
|
|
|
|
const readmeContent = readmeContentRaw
|
|
? new TextDecoder().decode(readmeContentRaw)
|
|
: "";
|
|
|
|
const etag =
|
|
"W/" +
|
|
Hash.createHash("sha3-256")
|
|
.update(`${packageName}:${packageVersion}`)
|
|
.toString("base64");
|
|
|
|
await ctx.render("package", { pkg, version: packageVersion, readmeContent });
|
|
ctx.response.headers.set("cache-control", CACHE_CONTROL);
|
|
ctx.response.headers.set("E-Tag", etag);
|
|
});
|
|
|
|
async function handleBrowse(ctx: ABC.Context) {
|
|
const E404 = () => {
|
|
ctx.response.status = 404;
|
|
ctx.response.body = "// Not found!";
|
|
};
|
|
|
|
let [packageName, packageVersion] = extractPackagePath(
|
|
ctx.params.package
|
|
);
|
|
|
|
const pkg = await db.package.findOne({ name: packageName });
|
|
packageVersion = getAbsolutePackageVersion(pkg, packageVersion);
|
|
|
|
if (!packageVersion) return E404();
|
|
|
|
const path = ctx.params.path || "";
|
|
|
|
const fileContent = await getFile(packageName, packageVersion, path);
|
|
if (fileContent) {
|
|
await ctx.render("browse_file", {
|
|
pkg,
|
|
version: packageVersion,
|
|
content: new TextDecoder().decode(fileContent.data),
|
|
ext: Path.extname(path),
|
|
path: `${packageName}@${packageVersion}/${path}`,
|
|
});
|
|
} else {
|
|
const filesPath = getFilePath(
|
|
packageName,
|
|
packageVersion,
|
|
path
|
|
);
|
|
if (!filesPath) return E404();
|
|
|
|
console.log(filesPath);
|
|
|
|
const filesItr = Storage.walkFiles(filesPath);
|
|
|
|
const files: { name: string; size: number }[] = [];
|
|
const directories: Set<string> = new Set();
|
|
let readme: string | null = null;
|
|
for await (const file of filesItr) {
|
|
const relPath = Path.posix.relative(filesPath, file.path || "");
|
|
console.log({ file, relPath, filesPath });
|
|
if (relPath.indexOf("/") >= 0) {
|
|
directories.add(relPath.split("/")[0]);
|
|
} else {
|
|
files.push({ name: relPath, size: -1 }); //TODO: Size is not implemented yet
|
|
if (relPath.toLowerCase() === "readme.md") {
|
|
const readmeCont = await getFile(
|
|
packageName,
|
|
packageVersion,
|
|
Path.posix.join(path, relPath)
|
|
);
|
|
if (readmeCont) {
|
|
readme = new TextDecoder().decode(readmeCont?.data);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
await ctx.render("browse_folder", {
|
|
pkg,
|
|
version: packageVersion,
|
|
readme,
|
|
files,
|
|
directories: Array.from(directories.values()).map((e) => ({
|
|
name: e,
|
|
})),
|
|
path: `${packageName}@${packageVersion}/${path}`,
|
|
});
|
|
}
|
|
}
|
|
|
|
g.get("/browse/:package", handleBrowse);
|
|
g.get("/browse/:package/*path", handleBrowse);
|
|
}
|