Skip to content

Instantly share code, notes, and snippets.

@jsjoeio
Last active May 4, 2021 15:16
Show Gist options
  • Save jsjoeio/f1695355e44b0a90bb17619f2faf1016 to your computer and use it in GitHub Desktop.
Save jsjoeio/f1695355e44b0a90bb17619f2faf1016 to your computer and use it in GitHub Desktop.
oak - example static server which checks for HTML files with same name as directory for Next.js
/*
* This is an example of a server that will serve static content out of the
* $CWD/examples/static path.
*/
import {
bold,
cyan,
green,
red,
yellow,
} from "https://deno.land/[email protected]/fmt/colors.ts"
import { extname } from "https://deno.land/[email protected]/path/mod.ts"
import { Application, HttpError, Status } from "https://deno.land/x/oak/mod.ts"
const app = new Application()
// Error handler middleware
app.use(async (context, next) => {
try {
await next()
} catch (e) {
if (e instanceof HttpError) {
// deno-lint-ignore no-explicit-any
context.response.status = e.status as any
if (e.expose) {
context.response.body = `<!DOCTYPE html>
<html>
<body>
<h1>${e.status} - ${e.message}</h1>
</body>
</html>`
} else {
context.response.body = `<!DOCTYPE html>
<html>
<body>
<h1>${e.status} - ${Status[e.status]}</h1>
</body>
</html>`
}
} else if (e instanceof Error) {
context.response.status = 500
context.response.body = `<!DOCTYPE html>
<html>
<body>
<h1>500 - Internal Server Error</h1>
</body>
</html>`
console.log("Unhandled Error:", red(bold(e.message)))
console.log(e.stack)
}
}
})
// Logger
app.use(async (context, next) => {
await next()
const rt = context.response.headers.get("X-Response-Time")
console.log(
`${green(context.request.method)} ${cyan(
context.request.url.pathname
)} - ${bold(String(rt))}`
)
})
// Response Time
app.use(async (context, next) => {
const start = Date.now()
await next()
const ms = Date.now() - start
context.response.headers.set("X-Response-Time", `${ms}ms`)
})
// Send static content
app.use(async (context) => {
const root = `${Deno.cwd()}/examples/static`
const requestPath = context.request.url.pathname
const fullFilePath = `${root}${requestPath}`
const path = await handleFileToServe(requestPath, root)
await context.send({
root,
path,
index: "index.html",
})
})
app.addEventListener("listen", ({ hostname, port, serverType }) => {
console.log(bold("Start listening on ") + yellow(`${hostname}:${port}`))
console.log(bold(" using HTTP server: " + yellow(serverType)))
})
await app.listen({ hostname: "127.0.0.1", port: 8000 })
async function isDirectory(path: string) {
const fileInfo = await Deno.stat(path)
return fileInfo.isDirectory
}
async function hasHtmlFileForDir(fileName: string, parentDir: string) {
let hasHtmlFile = false
// Source: https://deno.land/[email protected]/http/file_server.ts#L167
for await (const entry of Deno.readDir(parentDir)) {
if (entry.name === `${fileName}.html` && entry.isFile) {
hasHtmlFile = true
break
}
}
return hasHtmlFile
}
async function handleFileToServe(path: string, root: string) {
if (path === "/") {
return `index.html`
}
const fileExt = extname(path)
const fullFilePath = `${root}${path}`
if (fileExt === "" && (await isDirectory(fullFilePath))) {
console.log("looks promising")
// TODO this only looks one level deep
const fileName = path.substr(1)
const parentDir = getParentDir(fullFilePath)
const hasHtmlFile = await hasHtmlFileForDir(fileName, parentDir)
console.log(`hasHtmlFile: ${hasHtmlFile}`)
if (hasHtmlFile) {
return `${path}.html`
}
}
return path
}
function getParentDir(path: string) {
// /Users/jp/Dev/testing/oak-server/examples/static/course
// Source: https://stackoverflow.com/a/16863827/3015595
return path.substring(0, path.lastIndexOf("/"))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment