DenReg/registry/src/utils.ts

138 lines
3.6 KiB
TypeScript
Raw Normal View History

2020-07-31 18:22:09 +00:00
import { ABC, Base64 } from "./deps.ts";
2020-07-28 12:39:54 +00:00
import config from "./config.ts";
const packageNameRegex = /^[@]?[a-zA-Z][\d\w\-\_]*$/g.compile();
const packageVersionRegex = /^\d(\.\d)?(\.\d)?$/g.compile();
const packageFullVersionRegex = /^\d(\.\d)(\.\d)$/g.compile();
export const isValidPackageName = (name: string) => packageNameRegex.test(name);
export const isValidVersion = (version: string) =>
packageVersionRegex.test(version);
export const isValidFullVersion = (version: string) =>
packageFullVersionRegex.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()
2020-07-31 18:22:09 +00:00
.decode(Base64.decode(credentials))
2020-07-28 12:39:54 +00:00
.split(":", 2);
if (config?.user[username]?.password === passwd) {
console.log("User authenticated!");
if (!ctx.customContext) ctx.customContext = {};
ctx.customContext.user = username;
2020-07-28 12:39:54 +00:00
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();
2020-07-28 12:39:54 +00:00
if (path.startsWith("@")) {
packageName = "@";
path = path.slice(1);
}
let parts = path.split("@");
if (parts.length > 2) throw new Error("Invalid package name!");
packageName += parts[0];
2020-08-02 20:39:05 +00:00
let packageVersion: string | undefined = parts[1];
2020-07-28 12:39:54 +00:00
if (!isValidPackageName(packageName))
throw new Error("Invalid package name!");
if (packageVersion !== "") {
if (!isValidVersion(packageVersion))
throw new Error("Invalid package version!");
}
return [packageName, packageVersion];
}
import db from "./db.ts";
import bucket from "./s3.ts";
export async function getFile(
pkgName: string,
version: string | null | undefined,
file: string
2020-08-02 14:33:41 +00:00
): Promise<{ etag: string; data: Uint8Array } | null | undefined> {
2020-08-02 20:39:05 +00:00
console.log("Searching for file: %s/%s@%s", pkgName, file, version);
const meta = await db.package.findOne({ name: pkgName });
if (!meta || meta.versions.length < 1) return null;
const versions = meta.versions.sort(sortVersions).reverse();
if (!version) {
version = versions[0];
} else {
const v = versions.filter((e) => e.startsWith(version as string));
if (v.length < 1) return null;
version = v[0];
}
const bucketPath = (
"packages/" +
pkgName +
"/" +
version +
"/" +
file
).replace(/@/g, "§");
console.log("Getting file from:", bucketPath);
try {
2020-08-02 14:33:41 +00:00
const res = await bucket.getObject(bucketPath);
if (!res) return undefined;
return {
etag: res.etag,
data: res.body,
};
} catch (err) {
const msg = err.message as string;
if (msg.indexOf("404") >= 0) return null;
throw err;
}
}