import { ABC, Base64, Path } from "./deps.ts"; import config from "./config.ts"; import * as Storage from "./storage.ts"; export const isValidPackageName = (name: string) => /^[@]?[a-zA-Z][\d\w\-\_]*$/g.test(name); export const isValidVersion = (version: string) => /^\d+(\.\d+)?(\.\d+)?$/g.test(version); export const isValidFullVersion = (version: string) => /^\d+(\.\d+)(\.\d+)$/g.test(version); const ALg = 1; const ASm = -ALg; const Equ = 0; export const sortVersions = (a: string, b: string) => { const [a1, a2, a3] = a.split(".").map(Number); const [b1, b2, b3] = b.split(".").map(Number); if (a1 > b1) return ALg; if (a1 < b1) return ASm; if (a2 > b2) return ALg; if (a2 < b2) return ASm; if (a3 > b3) return ALg; if (a3 < b3) return ASm; return Equ; }; export const basicauth = (realm: string) => (next: ABC.HandlerFunc) => ( ctx: ABC.Context ) => { const value = ctx.request.headers.get("authorization"); console.log("Header:", value); if (value && value.toLowerCase().startsWith("basic ")) { const credentials = value.slice(6); const [username, passwd] = new TextDecoder() .decode(Base64.decode(credentials)) .split(":", 2); if (config?.user[username]?.password === passwd) { console.log("User authenticated!"); if (!ctx.customContext) ctx.customContext = {}; ctx.customContext.user = username; return next(ctx); } } console.log("Authentication required"); ctx.response.status = 401; ctx.response.headers.set("WWW-Authenticate", "Basic realm=" + realm); return { statusCode: 401, error: "Authentication required", }; }; export function extractPackagePath(path: string): [string, string | undefined] { let packageName = ""; path = path.toLowerCase(); if (path.startsWith("@")) { packageName = "@"; path = path.slice(1); } const parts = path.split("@"); if (parts.length > 2) throw new Error("Invalid package name!"); packageName += parts[0]; const packageVersion: string | undefined = parts[1]; console.log({ path, parts, packageName, packageVersion }); if (!isValidPackageName(packageName)) throw new Error("Invalid package name!"); if (packageVersion && packageVersion !== "") { if (!isValidVersion(packageVersion)) throw new Error("Invalid package version!"); } return [packageName, packageVersion]; } import type { IPackage } from "./db.ts"; export function getAbsolutePackageVersion( pkg?: IPackage | null, version?: string ) { if (!pkg || pkg.versions.length < 1) return undefined; const versions = pkg.versions.sort(sortVersions).reverse(); if (!version || version === "latest") { version = versions[0]; } else { const v = versions.filter((e) => e.startsWith(version as string)); if (v.length < 1) return undefined; version = v[0]; } return version; } export function getFilePath( pkgName: string, version: string, file: string ) { if (file.startsWith("/")) { file = file.substr(1); } const bucketPath = ( pkgName + "/" + version + "/" + file ).replace(/@/g, "ยง"); return bucketPath; } export async function getOneOf(pkgName: string, version: string, files: (string | undefined)[]) { for (const file of files) { if (!file) continue; const res = await getFile(pkgName, version, file); if (res) return res; } return undefined; } export async function getFile( pkgName: string, version: string | undefined, file: string ): Promise<{ etag: string; data: Uint8Array } | null | undefined> { if (!version) return undefined; const bucketPath = await getFilePath(pkgName, version, file); if (!bucketPath) return null; console.log("Getting file from:", bucketPath); try { const res = await Storage.readFile(bucketPath); if (!res) return undefined; return { etag: Base64.encodeBase64(await crypto.subtle.digest("sha-1", res)), data: res, }; } catch (err) { const msg = err.message as string; if (msg.indexOf("404") >= 0) return null; throw err; } } const exts = new Map(); exts.set(".js", "text/javascript"); exts.set(".mjs", "text/javascript"); exts.set(".json", "application/json"); exts.set(".jsonld", "application/ld+json"); exts.set(".css", "text/css"); exts.set(".html", "text/html"); exts.set(".htm", "text/html"); export function getContentType(filename: string) { const ext = Path.extname(filename); return exts.get(ext); }