EdgeOne 边缘函数 + Hono.js + Fauna 搭建个人博客

一、背景

虽然 “博客” 已经是很多很多年前流行的东西了,但是时至今日,仍然有一部分人在维护自己的博客站点,输出不少高质量的文章。

我使用过几种博客托管平台或静态博客生成框架,前段时间使用Hono.js+Fauna ,基于 EO 边缘函数搭建了一个博客网站样例,写一篇小小文章进行记录。

二、技术栈

2.1 Hono.js

Hono.js 是一个轻量、快速的 Edge Web 框架,适用于多种 JavaScript 运行时:Cloudflare Workers、Fastly Compute、Deno、Bun、Vercel、Netlify、AWS Lambda 等,同样也可以在 EO 边缘函数 Runtime 中运行起来。

在 EO 边缘函数中,最简单的 Hono 应用写法如下:

import { Hono } from 'hono'
const app = new Hono()app.get('/', (c) => c.text('Hono!'))app.fire();

2.2 Fauna

Fauna 作为 Cloud API 提供的分布式文档关系数据库。Fauna 使用 FQL 进行数据库查询(FQL: A relational database language with the flexibility of documents)。

因此,我准备将博客文章存放在 Fauna 中,在 Fauna JS Driver 的基础上,包装 RESTful API 供边缘函数调用。

注意:
- 目前 EO 边缘函数还不能完全支持 Fauna JS Driver,因此现阶段还不能直接使用 JS Driver 进行数据查询,需要将 Fauna API 服务搭建在其他 JS 环境中。
- EO 边缘函数后续将支持 KV,同时也会兼容 Fauna JS Driver 的写法,因此这里可以进行优化。
import { Client, fql } from 'fauna';
...router.get('/', async c => {const query = fql`Blogs.all()`;const result = await (c.var.faunaClient as Client).query<Blog>(query);return c.json(result.data);
});
...

三、搭建博客

3.1 路由

博客网站的路由比较简单,可以直接使用 Hono.js 进行处理:

import { Hono } from "hono";import { Page } from "./pages/page";
import { Home } from "./pages/home";const app = new Hono();...app.get("/", async (c) => {const blogs = await getBlogList();return c.html(<Home blogs={blogs} />);
});app.get("/blog/:id{[0-9]+}", async (c) => {const id = c.req.param("id");const blog = await getBlogInfo(id);if (!blog) return c.notFound();return c.html(<Page blog={blog} />);
});app.fire();

3.2 页面

Hono.js 中,可以直接按照 jsx 的语法定义页面结构和样式:

import { DateTime } from "luxon";import { Layout } from "./components/layout";...const Item = (props: { blog: Blog }) => {const { ts, id, title } = props.blog;const dt = DateTime.fromISO(ts.isoString);const formatDate = dt.toFormat("yyyy-MM-dd");return (<li><section><p style={{ fontSize: "14px", color: "gray" }}>{formatDate}</p><a href={`/blog/${id}`}>{title}</a></section></li>);
};const List = (props: { blogs: Blog[] }) => (<ul>{props.blogs.map((blog) => (<Item blog={blog} />))}</ul>
);export const Home = (props: { blogs: Blog[] }) => {const title = "Tomtomyang's Blog";return (<Layout title={title}><header><h1>{title}</h1></header><List blogs={props.blogs} /></Layout>);
};

详情页要比列表页稍微复杂一点,一方面需要将 markdown 文件转换,另一方面还需要计算生成文章目录:

import { parse } from "marked";
import { html, raw } from "hono/html";import { DateTime } from "luxon";import type { Blog } from "..";
import { Layout } from "./components/layout";
import { getRenderer } from "../utils/render";const renderer = getRenderer();const Toc = () => { ... };const Info = (props: { author: string; time: string }) => {const { author, time } = props;const dt = DateTime.fromISO(time);const formatDate = dt.toFormat("yyyy-MM-dd hh:mm:ss");return (<div style={{ paddingBottom: "0.6em" }}><span style={{ color: "gray" }}>{formatDate}</span><span style={{ marginLeft: "10px" }}>{author}</span></div>);
};const Content = (props: { content: string }) => {return (<article style={{ fontSize: "16px" }}>{html`${raw(props.content)}`}</article>);
};export const Page = (props: { blog: Blog }) => {const { title, author, content, ts } = props.blog;const parsedContent = parse(content, { renderer });return (<Layout title={title}><header><h1>{title}</h1></header><Info author={author} time={ts.isoString}></Info><Content content={parsedContent} /><Toc content={parsedContent} /></Layout>);
};

3.3 缓存

在边缘构建站点的优势之一是对缓存的控制比较灵活,首先,我准备首先缓存 Fauna API 的响应结果:

首页展示所有文章列表,新增文章后,我需要在首页展示出来,因此列表接口我设置不缓存或者缓存时间很短:

export const fetchWithCache = async (url: string) => {try {return await fetch(url, {eo: {cacheTtlByStatus: {200: 24 * 60 * 60 * 1000,},},});} catch (err) {return new Response(`FetchWithCache Error: ${err.massage}`, {status: 500,});}
};

文章详情页展示文章的具体内容,我个人的习惯是一篇文章写完后,才进行发布,因此后续文章内容发生变动的概率较低,我选择缓存更长的时间:

export const fetchWithCache = async (url: string) => {try {return await fetch(url, {eo: {cacheTtlByStatus: {200: 7 * 24 * 60 * 60 * 1000,},},});} catch (err) {return new Response(`FetchWithCache Error: ${err.massage}`, {status: 500,});}
};

同时,文章详情页还有一个需要注意的点是,通过 API 获取到文章内容后,我还会计算生成 文章目录,文章内容不变,生成的文章目录肯定也不会变,因此这部分重复的计算也可以通过缓存解决掉,方式是使用边缘函数 Runtime 中的 Cache API,将 c.html(<Page blog={blog} />) 生成的 HTML 字符串进行缓存,这样就解决了 Toc 重复计算的问题:

app.get("/blog/:id{[0-9]+}", async (c) => {const id = c.req.param("id");const cache = caches.default;const cacheKey = getCacheKey(id);try {const cacheResponse = await cache.match(cacheKey);if (cacheResponse) {return cacheResponse;}throw new Error(`Response not present in cache. Fetching and caching request.`);} catch {const blog = await getBlogInfo(id);if (!blog) return c.notFound();const html = await c.html(<Page blog={blog} />);html.headers.append("Cache-Control", "s-maxage=xxxx");c.event.waitUntil(cache.put(cacheKey, html));return html;}
});

3.4 部署

使用 Tef-CLI 的 publish 命令,直接将开发好的代码部署到 EdgeOne 边缘节点上;或者可以将 dist/edgefunction.js 文件中的代码,粘贴到 EdgeOne 控制台 - 边缘函数 - 新建函数 编辑框中进行部署。

四、总结

经过上面的步骤,我的博客站点就搭建好了:

列表页:

详情页:

整体来看,在边缘节点上搭建一个博客站点,可以更灵活、更高效的利用和操作 CDN 缓存,对于不同类型的页面,我可以设置不同的缓存策略;边缘 Serverless + Cloud API 的部署方式,让我能足够方便的更新博客,后续随着 EO 边缘函数的不断迭代,这种玩法还有很大的升级空间。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/35121.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

RK3568平台开发系列讲解(I2C篇)利用逻辑分析仪进行I2C总线的全面分析

🚀返回专栏总目录 文章目录 1. 基础协议1.1. 协议简介1.2. 物理信号1.3. 总线连接沉淀、分享、成长,让自己和他人都能有所收获!😄 1. 基础协议 1.1. 协议简介 IIC-BUS(Inter-IntegratedCircuit Bus)最早是由PHilip半导体(现在被NXP收购)于1982年开发。 主要是用来方…

将深度相机的实时三维坐标数据保存为excel文档(Python+Pyrealsense2+YOLOv8)

一、如何将数据保存为excel文档 1.excel文件库与相关使用 &#xff08;1&#xff09;导入相应的excel文件库&#xff0c;导入前先要进行pip安装&#xff0c;pip install xlwt import xlwt # 导入用于创建和写入Excel文件的库 (2) 建立一个excel文档&#xff0c;并在第0行写…

RabbitMQ中Direct交换机的用法

前言&#xff1a;比如我们的支付完成之后需要进行修改支付状态还要完成短信通知用户需要同时并发两条指令我们可以使用direct交换机进行指定两个不同的业务去完成这两件事 比如我们现在有direct.queue1/direct.queue2两个消息队列&#xff0c;一个direct交换机 我们创建完成两…

鸿蒙开发之--生命周期

开发官网 开发-HarmonyOS开发者-华为开发者联盟 UIAbility生命周期 1、首先执行onCreate(),用于页面初始化和设置页面逻辑 2、执行onWindowStageCreate()创建一个窗口&#xff0c;在这里可以使windowStage.loadContent(url&#xff0c;&#xff08;&#xff09;>{})打开一…

“拿来主义”学习无限滚动动画(附源码)

欢迎关注&#xff1a;xssy5431 小拾岁月 参考链接&#xff1a;https://mp.weixin.qq.com/s/xVTCwR1ZSn5goWmc2yimVA 动画效果 需求分析 需求中涉及无线滚动&#xff0c;说明需要使用 animation 动画。另外&#xff0c;为了方便用户点击操作&#xff0c;需要给滚动对象添加鼠标…

感谢我的辅导员—敬爱的罗老师

前言&#xff1a;快毕业了&#xff0c;想在毕业季感谢给予我帮助的老师&#xff0c;我的辅导员-罗老师是我最想感谢的大学老师。我不知道应该以什么样的方式去表达罗老师对我大学阶段的帮助&#xff0c;如果是直接发邮件&#xff0c;微信信息留言&#xff0c;可能在之后我和老师…

MySQL索引优化解决方案--索引优化(4)

排序优化 尽量避免使用Using FileSort方式排序。order by语句使用索引最左前列或使用where子句与order by子句条件组合满足索引最左前列。where子句中如果出现索引范围查询会导致order by索引失效。 优化案例 联表查询优化 分组查询优化 慢查询日志

架构是怎样练成的-楼宇监控系统案例

目录 概要 项目背景 原系统设计方案 改进后的设计方案 小结 概要 绝大多数人掌握的架构都是直接学习&#xff0c;慢慢地才能体会到一个架构的好处。架构是一种抽象&#xff0c;是为了复用目的而对代码做的抽象。通过一个项目的改造&#xff0c;理解架构是如何产生的&…

Kubernetes Prometheus 系例 | kubernetes 部署 Kafka exporter监控Kafka集群

prometheus 监控 kafka 常见的有两种开源方案&#xff1b; 部署 exporter 或 jmx 配置监控。 项目地址&#xff1a; kafka_exporter&#xff1a;https://github.com/danielqsj/kafka_exporter jmx_exporter&#xff1a;https://github.com/prometheus/jmx_exporter 本文采用kaf…

【日常记录】【JS】优雅检测用户是否在指定元素的外部点击

文章目录 1、界面基本布局2、代码实现3、参考链接 1、界面基本布局 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0">…

搜索python包的说明

当我发现bug时&#xff0c;就怀疑是sns包的版本问题了&#xff08;原代码是原作者以前成功运行的代码&#xff09;&#xff0c;于是直接到网上搜&#xff0c;找到对应的说明文档 根据该示例代码进行改写&#xff1a; 达成目的。

【漏洞复现】用友 UFIDA saveDoc.ajax 任意文件上传漏洞

免责声明&#xff1a; 本文内容旨在提供有关特定漏洞或安全漏洞的信息&#xff0c;以帮助用户更好地了解可能存在的风险。公布此类信息的目的在于促进网络安全意识和技术进步&#xff0c;并非出于任何恶意目的。阅读者应该明白&#xff0c;在利用本文提到的漏洞信息或进行相关测…

ONLYOFFICE 8.1版本桌面编辑器测评

https://www.onlyoffice.com/zh/ 随着工作方式的不断演变&#xff0c;文档编辑软件成为了我们日常工作中不可或缺的一部分。而ONLYOFFICE作为一款开源且功能丰富的办公套件&#xff0c;其最新推出的8.1版本在原有基础上进行了大量的优化与更新&#xff0c;旨在提供更流畅、更安…

后端返回base64文件流下载

后端返回base64文件流: 前端处理&#xff1a; downloadTemplate () {this.$API.downloadTemplate().then(({ data }) > {const binaryString atob(data) // 解码base64字符串const byteArray new Uint8Array(binaryString.length) // 创建一个Uint8Arrayfor (let i 0; i…

2.优化算法之滑动窗口1

1.长度最小的子数组 . - 力扣&#xff08;LeetCode&#xff09; &#xff08;1&#xff09;题目描述 给定一个含有 n 个正整数的数组和一个正整数 target 。 找出该数组中满足其总和大于等于 target 的长度最小的 子数组 [numsl, numsl1, ..., numsr-1, numsr] &#xff0c;…

计算机视觉——opencv快速入门(一) opencv的介绍与安装

什么是opencv OpenCV&#xff08;Open Source Computer Vision Library&#xff09;是一个开源的计算机视觉和机器学习软件库。它旨在提供广泛的图像和视频处理功能&#xff0c;支持多种编程语言&#xff08;主要包括C, Python, Java等&#xff09;和操作系统&#xff08;如Li…

生产环境:CentOS 7 Docker 20.10.19离线部署(为离线部署k8s做准备)

背景描述&#xff1a;离线部署Docker环境 在现代IT基础设施中&#xff0c;Docker已经成为应用容器化的标准工具。它简化了应用程序的部署和管理&#xff0c;使开发者和运维工程师能够以更高的效率和一致性进行工作。然而&#xff0c;在某些场景下&#xff0c;由于安全性、网络…

代码随想录算法训练营第三十四天|56. 合并区间、738.单调递增的数字、968.监控二叉树

56. 合并区间 题目链接&#xff1a;56. 合并区间 文档讲解&#xff1a;代码随想录 状态&#xff1a;无语&#xff0c;这题从右边界排序做不了&#xff01; 思路&#xff1a; 排序&#xff1a;按照区间的起始位置进行排序&#xff0c;这样后面处理时可以顺序合并重叠区间。合并…

数据结构-线性表的链式表示

目录 前言一、线性表的链式表示和实现1.1 线性表的表示1.2 基本操作的实现1.3 线性表的链式表示的优缺点 总结 前言 本篇文章主要介绍线性表的链式表示 一、线性表的链式表示和实现 1.1 线性表的表示 线性表的链式表示又称为链式存储结构或链式映像 链式存储定义&#xff1…

emlogpro文件上传漏洞代码审计(CVE-2023-44974)(CVE-2023-44973)

【产品介绍】 emlog 是 “Every Memory Log” 的简称&#xff0c;意即&#xff1a;点滴记忆。它是一款基于PHP语言和MySQL数据库的开源、免费、功能强大的个人或多人联合撰写的博客系统(blog)。基于PHP和MySQL的功能强大的博客及CMS建站系统。致力于提供快速、稳定&#xff0c…