【BUN】静态托管服务

index.ts

// index.ts
import type {Serve} from "bun"
import {stat, readdir} from 'node:fs/promises'
import ejs from 'ejs'

interface Dir {
  url: string,
  type: 'dir' | 'back' | 'file' | 'unknown',
  path: string,
  name: string
}

const BASEURL = '/public' // 托管项目下public文件夹

const promiseAwait = <T>(promise: Promise<T>) => {
  return promise.then(v => [null, v] as const).catch(e => [e, null] as const)
}

//构建dir目录
const buildDir = async (dir: string, pathname: string) => {
  const arr: Array<Dir> = []
  const files = await readdir(dir)
  if (pathname !== '/') {
    arr.push({
      url: dir,
      type: 'back',
      name: '../',
      path: `javascript:history.back()`
    })
  }
  for await (const file of files) {
    const url = `${dir}/${file}`
    const [, where] = await promiseAwait(stat(url))
    if (!where) continue
    const isDir = where.isDirectory()
    const isFile = where.isFile()
    arr.push({
      url,
      type: isDir ? 'dir' : isFile ? 'file' : 'unknown',
      name: file,
      path: `${pathname === '/' ? '' : pathname}/${file}`
    })
  }
  return arr
}

// Bun 托管静态文件
const server: Serve = {
  async fetch(req) {
    const pathname = new URL(req.url).pathname
    const path = `${process.cwd()}${BASEURL}/${pathname}`
    // 判断是文件还是目录
    const [error, where] = await promiseAwait(stat(path))
    if (!where) {
      return new Response(error)
    }
    const isDir = where.isDirectory()
    if (isDir) {
      const dirs = await buildDir(path, pathname)
      const html = await ejs.renderFile(`${import.meta.dir}/dir.ejs`, {dirs})
      return new Response(html, {
        headers: {
          'Content-Type': 'text/html'
        }
      })
    }
    const isFile = where.isFile()
    if (isFile) {
      const file = Bun.file(path)
      const exists = await file.exists()
      if (exists) return new Response(file)
    }
    return new Response(null, {status: 404})
  }
}

export default server

dir.ejs

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Directory</title>
    <style>
        li {
            padding: 6px;
        }
        li.dir::marker {
            content: '📁';
        }

        li.file::marker {
            content: '📄';
        }

        li.back::marker {
            content: '🔙';
        }
    </style>
</head>
<body>
<ul>
    <% for (let i = 0; i < dirs.length; i++) { %>
        <% const item = dirs[i] %>
        <li class="<%= item.type %>">
            <a href="<%= item.path %>">
                <%= item.name %>
            </a>
        </li>
    <% } %>
</ul>
</body>
</html>

usage

bun ./index.ts

example

image

image

posted @ 2025-01-03 11:27  demo_you  阅读(9)  评论(0编辑  收藏  举报