# [Projects] Vela 超简单日志应用程序接口

> [[Projects] Vela 超简单日志应用程序接口](https://www.ftls.xyz/posts/2026-07-13-vela/)
> Penned by [恐咖兵糖](https://www.ftls.xyz/) on 2026-07-13


Vela - Very Easy Log API 超简单日志应用程序接口

<!--more-->

## 缘起

最近我随手搞了一个程序，名字很直白——"超简单日志应用程序接口"（Very Easy Log API）。也就是个个人日志工具，或者说随便存 JSON 的。

个人这个范围很有意思。既然是给自己用的，很多"正规军"做法的东西就可以省了。不用写测试，不用考虑高并发，不用设计复杂的权限系统。

而这 Vela ，就像上学时的记作业本，除了作业单，还会随手记点灵感、画个小花什么的——乱七八糟，但全放进去就对了。对于网络版记作业本，Vela 被设计为存储各种 JSON。

这个日志系统起初没有这么复杂。一开始是当日志写来着，也就是说追加模式那种，一行一个 JSON，随便存。后来和 AI 聊了聊，最后搞成这个样子。

虽然最后的成果和最初的想法已经不太一样了。目前看还算可以用。理论上性能啦，拓展性之类的，会比文本日志更优。

## 选型

下面是一些技术选型和部分细节。

- TS 语言 + Hono 框架
- Bun 构建前端和测试后端 + Deno 构建运行后端
- 数据库 Turso

Vela 被设计成可以跑在边缘函数或云函数平台上，这就框定了数据库不能是本地文件型，必须是 CS 架构。

话虽如此，其实本地文件也不是完全不行——阿里函数计算就支持挂载对象存储或网络磁盘。里面放 SQLite 文件也能跑。。但这种方式性能肉眼可见地差，数据损坏的风险也高。我在其他平台踩过这个坑。毕竟是本地文件好网络协议模拟的本地文件差别还是挺大的。算是弊大于利的选择。

在 CS 架构的数据库里，libSQL 算是比较轻量的选择了。官方的云服务（Turso）用起来很方便，自己搭建也不算麻烦。此外，libSQL 的客户端库既支持本地文件也支持远程服务，切换起来非常灵活。于是就是它了。

其实，最开始我还考虑过 Deno KV。它的 key 是数组，val 能存任意 JS 对象，听起来很美好。但让 AI 写了一版之后，感觉还是不够灵活，就放弃了。能用，而且写起来简单，但毕竟只是 KVDB。

```ts
const kv = await Deno.openKv();

const key = ["users", crypto.randomUUID()];
const value = { name: "Alice" };
await kv.set(key, value);

const result = await kv.get(key);
result.value; // { name: "Alice" }
```

拓展一下就是这样的

```ts
import { Hono } from "hono";
import { logger } from "hono/logger";
import { cors } from "hono/cors";

declare const Deno: any;
const TOKEN = "Bearer your-secret-api-key-here";
// const DEBUG = process.env.DEV || false;
interface Req {
  act: "get" | "getMany" | "set" | "list" | "delete";
  args: any;
}

const app = new Hono();
const kv = await Deno.openKv("./data/kv.db");

app.use("*", cors());
app.use("*", logger());
app.use("*", async (c, next) => {
  const auth = c.req.header("Authorization");
  if (auth !== TOKEN) {
    return c.text("Unauthorized", 401);
  }
  await next();
});

app.get("/", (c) => c.text("API is running (authenticated)"));
app.post("/kv", async (c) => {
  const req: Req = await c.req.json();
  if (!req) {
    return c.text("Nothing", 400);
  }
  console.log("Req", req);
  switch (req.act) {
    case "get":
      // Req {"act":"get","args":[["a"]]}
      return c.json(await kv.get(req.args[0]));
    case "getMany":
      // Req {"act":"getMany","args":[[["a"],["a","b"]]]}
      return c.json(await kv.getMany(req.args[0]));
    case "set":
      // Req {"act":"set","args":[["a"],"b"]}
      return c.json(await kv.set(req.args[0], req.args[1]));
    case "list":
      // Req  {"act":"list","args":[{ "prefix": ["ab"] }]}
      const entries = kv.list(req.args[0]);
      const res = [];
      for await (const entry of entries) res.push(entry.value);
      return c.json(res);
    case "delete":
      // Req {"act":"delete","args":[["a"]]}
      return c.json(await kv.delete(req.args[0]));
    default:
      return c.text("Invalid action", 400);
  }
});

export default app;
```

不过 Deno KV API 可以在应用层自己模拟实现。Vela 借鉴了其思路，字段不多，但 text 字段存 JSON 解决了很多问题。daily 片段接口能传入任意类型（text、json 都行），后端自动加上 `id`、`time`、`type` 等等。daily API GET 时，自动将 text 字段转成对象。

```text
{"foo":"bar"}
[{"foo":"bar"}]
foo-bar
[1,2,3,"a","b","c",{"foo":"bar"},true,false]
```

上面每一行，对于 daily 片段 API 都是合法的。对象会加上 id ，time 被转为 JSON 字符串存储。

当然，这种效率上可能不如直接使用存 V8 数据二进制的 Deno KV，但是对于简单的应用来说，这种 API 已经足够了。

然后你看，就这么随手记，什么格式都有，但系统都能接住。

## 代码

项目放在 Codeberg 上了，不过这个平台在我博客网页上估计够呛能直接打开——平台对外部链接，AI 爬虫之类的限制得比较严，我 Kiwi 浏览器甚至打不开 Codeberg。项目列表加载速度也比我树莓派上跑的 Gitea 还要慢一点。所以如果条件允许，还是建议自己搭 Gitea 或 Forgejo。

- https://codeberg.org/kkbt/Vela

代码组织上算是 Monorepo，Web APP、Server、Client、MCP 都放在一起了。它们之间共用 `types.ts` 之类的代码，前端用的是 WaterCSS + Alpine.js，HTML 部分就是简单的 HTML + JS，没有其他复杂的构建工具。

目前可以部署在 Deno Deploy + Turso 上，Supabase、Appwrite 之类的平台大概也支持。大概算是 Hono + Turso 带来的好处——哪都能跑。

## 最后

最后是一个例行的高级玩法展望，也就是说，俺寻思可行，但是实际上还没有试验的想法。

1. **当 CMS 用**：加几个 API，配合 TipTap 编辑器写文章；写个页面打标签、设置公开/私有；Hugo 构建时调用展示。

2. **自动化处理**：设置定时任务抓取每天日志，用 AI 总结分析，或者自动备份到 WebDAV、S3、Obsidian、Trilium 等各种地方。

说到底，我的需求真的很简单——一个 JSON 数组就能解决的事。所以也没用什么复杂的东西。不过基于拓展性，可维护性等考虑，还是写的不是那么 "Very Easy"。

如果是有条件上 Deno，还可以加上 Deno KV 封装成那些的 API，应该会很稳固——核心代码几十行就搞定了，再根据读写需求加点外围代码。也没什么可说的。

这个项目还写了 MCP，拓展起来应该还算方便。目前看，够用，也还算优雅。

哦对了，项目还顺手写了个 MCP Server，算是个 bonus。

2026-07-11
