好的,我们可以使用 Turso 数据库和 Drizzle ORM 来添加数据库管理功能。Turso 是一个分布式 SQLite 数据库,而 Drizzle ORM 是一个 TypeScript ORM,它们都可以很好地与 Bun 一起使用。

让我们更新我们的 Blog 系统以包含这些功能:

  1. 首先,安装必要的依赖:
bun add @libsql/client drizzle-orm drizzle-kit
  1. 创建一个新的文件 src/db/schema.ts 来定义数据库模式:
import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core';
 
export const posts = sqliteTable('posts', {
  id: integer('id').primaryKey(),
  title: text('title').notNull(),
  content: text('content').notNull(),
  createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(sql`CURRENT_TIMESTAMP`),
});
  1. 创建一个新的文件 src/db/index.ts 来设置数据库连接:
import { drizzle } from 'drizzle-orm/libsql';
import { createClient } from '@libsql/client';
import * as schema from './schema';
 
const client = createClient({
  url: process.env.TURSO_DATABASE_URL!,
  authToken: process.env.TURSO_AUTH_TOKEN!,
});
 
export const db = drizzle(client, { schema });
  1. 更新 src/index.ts 文件以使用数据库:
import { Elysia } from 'elysia';
import { html } from '@elysiajs/html';
import { staticPlugin } from '@elysiajs/static';
import { renderToString } from 'react-dom/server';
import { Blog } from './components/Blog';
import { db } from './db';
import { posts } from './db/schema';
import { eq } from 'drizzle-orm';
 
const app = new Elysia()
  .use(html())
  .use(staticPlugin())
  .get('/', async ({ html }) => {
    const allPosts = await db.select().from(posts).all();
 
    const blogHtml = renderToString(Blog({ posts: allPosts }));
 
    return html(`
      <!DOCTYPE html>
      <html lang="en">
        <head>
          <meta charset="UTF-8">
          <meta name="viewport" content="width=device-width, initial-scale=1.0">
          <title>My Blog</title>
          <link rel="stylesheet" href="/public/styles.css">
          <script src="https://unpkg.com/htmx.org@1.9.0"></script>
        </head>
        <body>
          <div id="root">${blogHtml}</div>
        </body>
      </html>
    `);
  })
  .get('/api/post/:id', async ({ params }) => {
    const post = await db.select().from(posts).where(eq(posts.id, parseInt(params.id))).get();
 
    if (!post) {
      return new Response('Post not found', { status: 404 });
    }
 
    return `
      <article>
        <h2>${post.title}</h2>
        <p>${post.content}</p>
        <small>Created at: ${new Date(post.createdAt).toLocaleString()}</small>
      </article>
    `;
  })
  .post('/api/post', async ({ body }) => {
    const { title, content } = body as { title: string; content: string };
    const newPost = await db.insert(posts).values({ title, content }).returning().get();
    return newPost;
  })
  .put('/api/post/:id', async ({ params, body }) => {
    const { title, content } = body as { title: string; content: string };
    const updatedPost = await db.update(posts)
      .set({ title, content })
      .where(eq(posts.id, parseInt(params.id)))
      .returning()
      .get();
    return updatedPost;
  })
  .delete('/api/post/:id', async ({ params }) => {
    await db.delete(posts).where(eq(posts.id, parseInt(params.id))).run();
    return { success: true };
  })
  .listen(3000);
 
console.log(`Server is running at http://localhost:${app.server?.port}`);
  1. 更新 src/components/Blog.tsx 以包含创建新文章的表单:
import React from 'react';
 
interface Post {
  id: number;
  title: string;
  content: string;
  createdAt: number;
}
 
interface BlogProps {
  posts: Post[];
}
 
export const Blog: React.FC<BlogProps> = ({ posts }) => {
  return (
    <div>
      <h1>My Blog</h1>
      <form hx-post="/api/post" hx-swap="beforeend" hx-target="#posts">
        <input type="text" name="title" placeholder="Title" required />
        <textarea name="content" placeholder="Content" required></textarea>
        <button type="submit">Create Post</button>
      </form>
      <div id="posts">
        {posts.map(post => (
          <article key={post.id}>
            <h2>{post.title}</h2>
            <p>{post.content}</p>
            <small>Created at: {new Date(post.createdAt).toLocaleString()}</small>
            <button hx-get={`/api/post/${post.id}`} hx-target="closest article" hx-swap="outerHTML">
              Load Full Post
            </button>
            <button hx-delete={`/api/post/${post.id}`} hx-target="closest article" hx-swap="outerHTML">
              Delete Post
            </button>
          </article>
        ))}
      </div>
    </div>
  );
};
  1. 创建一个新文件 drizzle.config.ts 在项目根目录:
import type { Config } from 'drizzle-kit';
 
export default {
  schema: './src/db/schema.ts',
  out: './drizzle',
  driver: 'turso',
  dbCredentials: {
    url: process.env.TURSO_DATABASE_URL!,
    authToken: process.env.TURSO_AUTH_TOKEN!,
  },
} satisfies Config;
  1. 添加以下脚本到 package.json
{
  "scripts": {
    "generate": "drizzle-kit generate:sqlite",
    "push": "drizzle-kit push:sqlite"
  }
}
  1. 运行迁移:
bun run generate
bun run push
  1. 确保在运行应用之前设置环境变量:
export TURSO_DATABASE_URL=your_database_url
export TURSO_AUTH_TOKEN=your_auth_token
  1. 运行应用:
bun run src/index.ts

这个更新后的 Blog 系统现在使用 Turso 数据库和 Drizzle ORM 来管理博客文章。它包括以下功能:

  • 从数据库获取所有文章
  • 创建新文章
  • 获取单篇文章的完整内容
  • 删除文章

你可以进一步扩展这个系统,例如:

  • 添加文章编辑功能
  • 实现用户认证和授权
  • 添加评论系统
  • 实现分页
  • 添加搜索功能

记住,在实际部署时,你需要 properly 管理你的环境变量和数据库凭证。此外,你可能还需要添加错误处理和输入验证来使应用更加健壮。