Skip to content

Instantly share code, notes, and snippets.

@mayankchoubey
Last active January 3, 2022 07:23
Show Gist options
  • Save mayankchoubey/b87e5180e85116660a2084c16d2e4fad to your computer and use it in GitHub Desktop.
Save mayankchoubey/b87e5180e85116660a2084c16d2e4fad to your computer and use it in GitHub Desktop.
Deno content server - Medium course - section 1
import { API_KEYS_PATH } from "./consts.ts";
let apiKeys: Array<string> = [];
try {
apiKeys = JSON.parse(Deno.readTextFileSync(API_KEYS_PATH));
} catch (_e) {
console.info("No API keys found, authentication is disabled");
}
export function authorize(headers: Headers) {
const authorized = true, notAuthorized = false;
if (!apiKeys.length) {
return authorized;
}
const authHeader = headers.get("Authorization");
if (!authHeader) {
return notAuthorized;
}
const apiKey = authHeader.split(" ")[1];
if (!apiKey) {
return notAuthorized;
}
return apiKeys.includes(apiKey);
}
{
"apiKeysPath": "./test/unit/data/apiKeys.json",
"servePath": "./test/unit/"
}
const cfg = JSON.parse(Deno.readTextFileSync("./cfg.json"));
export const API_KEYS_PATH = cfg.apiKeysPath;
export const SERVE_PATH = cfg.servePath;
export const CONTENT_TYPE_RAW = "application/octet-stream";
export const EXTENSION_TO_CONTENT_TYPE: Record<string, string> = {
".bin": "application/octet-stream",
".csv": "text/csv",
".doc": "application/msword",
".docx":
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
".gz": "application/gzip",
".gif": "image/gif",
".htm": "text/html",
".html": "text/html",
".jpeg": "image/jpeg",
".jpg": "image/jpeg",
".js": "text/javascript",
".json": "application/json",
".mp3": "audio/mpeg",
".mp4": "video/mp4",
".mpeg": "video/mpeg",
".png": "image/png",
".pdf": "application/pdf",
".ppt": "application/vnd.ms-powerpoint",
".pptx":
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
".rar": "application/vnd.rar",
".svg": "image/svg+xml",
".tar": "application/x-tar",
".txt": "text/plain",
".wav": "audio/wav",
".xls": "application/vnd.ms-excel",
".xlsx":
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
".xml": "application/xml",
".zip": "application/zip",
};
import { authorize } from "./authService.ts";
import { getContent } from "./fileService.ts";
import { Status } from "https://deno.land/std/http/http_status.ts";
import {
CONTENT_TYPE_RAW as rawCT,
EXTENSION_TO_CONTENT_TYPE as getCT,
} from "./consts.ts";
import { extname as ext } from "https://deno.land/std/path/mod.ts";
export async function handleRequest(req: Request): Promise<Response> {
const relativePath = (new URL(req.url)).pathname;
if (!authorize(req.headers)) {
return new Response(null, { status: Status.Unauthorized });
}
try {
const { len, content } = await getContent(relativePath);
return new Response(content, {
status: 200,
headers: {
"content-length": len.toString(),
"content-type": getCT[ext(relativePath)] || rawCT,
},
});
} catch (err) {
if (err instanceof Deno.errors.NotFound) {
return new Response(null, { status: Status.NotFound });
}
if (err instanceof Deno.errors.BadResource) {
return new Response(null, { status: Status.UnprocessableEntity });
}
}
return new Response(null, { status: Status.InternalServerError });
}
import { readableStreamFromReader as makeStream } from "https://deno.land/std/streams/mod.ts";
import { SERVE_PATH } from "./consts.ts";
export async function getContent(relativePath: string) {
const filePath = SERVE_PATH + relativePath;
const fileData = await Deno.stat(filePath);
if (!fileData.isFile) {
throw new Deno.errors.BadResource("Directories cannot be served");
}
const r = await Deno.open(filePath);
return {
len: fileData.size,
content: makeStream(r),
};
}
import { API_KEYS_PATH, SERVE_PATH } from "./consts.ts";
import { route } from "./router.ts";
import { serve } from "https://deno.land/std/http/mod.ts";
async function checkAccess() {
const servePath = Deno.args[0] || SERVE_PATH || "";
if (!servePath) {
console.error(
"Error: Serve path not specified in command line or config file",
);
Deno.exit(1);
}
if (
(await Deno.permissions.query({ name: "read", path: servePath }))
.state !== "granted"
) {
console.error("Error: Missing read permission to", servePath);
Deno.exit(1);
}
if (
(await Deno.permissions.query({ name: "read", path: API_KEYS_PATH }))
.state !== "granted"
) {
console.error("Error: Missing read permission to", API_KEYS_PATH);
Deno.exit(1);
}
}
await checkAccess();
console.log("Content server started...");
serve(route, { port: 8080 });
import { Status } from "https://deno.land/std/http/http_status.ts";
import { handleRequest } from "./controller.ts";
export async function route(req: Request): Promise<Response> {
if (req.method !== "GET") {
return new Response(null, { status: Status.MethodNotAllowed });
}
return await handleRequest(req);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment