非常好的选择。用户权限管理是一个重要的功能,可以帮助我们更好地控制不同用户对博客系统的访问和操作权限。我们将实现管理员、作者和读者三种角色的权限管理。

第二十三部分:用户权限管理

  1. 首先,更新 src/db/schema.ts 文件,在 users 表中添加 role 字段:
import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";
 
export const users = sqliteTable("users", {
  id: integer("id").primaryKey(),
  username: text("username").notNull().unique(),
  email: text("email").notNull().unique(),
  password: text("password").notNull(),
  avatar: text("avatar"),
  role: text("role").notNull().default("reader"),
});
 
// ... 其他表定义保持不变
  1. 创建一个新的迁移文件 src/db/migrations/0007_add_user_role.ts
export async function up(db) {
  await db.run(sql`
    ALTER TABLE users ADD COLUMN role TEXT NOT NULL DEFAULT 'reader';
  `);
}
 
export async function down(db) {
  await db.run(sql`
    ALTER TABLE users DROP COLUMN role;
  `);
}
  1. 运行迁移:
bun run migrate
  1. 创建一个新文件 src/middleware/auth.ts 来处理权限验证:
import { Elysia } from "elysia";
 
export const authMiddleware = new Elysia()
  .derive(({ session }) => {
    return {
      user: session.user,
      isAdmin: session.user?.role === "admin",
      isAuthor: session.user?.role === "author" || session.user?.role === "admin",
    };
  })
  .onError(({ code, error, set }) => {
    if (code === "UNAUTHORIZED") {
      set.status = 401;
      return "Unauthorized";
    }
  });
 
export const adminOnly = authMiddleware.derive(({ isAdmin, set }) => {
  if (!isAdmin) {
    set.status = 403;
    throw new Error("UNAUTHORIZED");
  }
});
 
export const authorOnly = authMiddleware.derive(({ isAuthor, set }) => {
  if (!isAuthor) {
    set.status = 403;
    throw new Error("UNAUTHORIZED");
  }
});
  1. 更新 src/routes/users.ts 文件,添加用户角色管理功能:
import { Elysia } from "elysia";
import { db } from "../db";
import { users } from "../db/schema";
import { eq } from "drizzle-orm";
import { files } from "@elysiajs/files";
import { adminOnly, authMiddleware } from "../middleware/auth";
 
export const userRoutes = new Elysia()
  .use(authMiddleware)
  .use(files())
  // ... 保留之前的路由
  .get("/users", adminOnly, async ({ render }) => {
    const allUsers = await db.select().from(users).all();
    return render("users/index", { users: allUsers });
  })
  .get("/users/:id/edit", adminOnly, async ({ params, render }) => {
    const user = await db.select().from(users).where(eq(users.id, parseInt(params.id))).get();
    return render("users/edit", { user });
  })
  .post("/users/:id", adminOnly, async ({ params, body, set }) => {
    const { role } = body;
    await db.update(users).set({ role }).where(eq(users.id, parseInt(params.id)));
    set.redirect = "/users";
  });
  1. 创建 src/views/users/index.eta 文件:
<% layout('../layouts/main.eta', { title: 'User Management' }) %>
 
<h1>User Management</h1>
 
<table>
  <thead>
    <tr>
      <th>Username</th>
      <th>Email</th>
      <th>Role</th>
      <th>Actions</th>
    </tr>
  </thead>
  <tbody>
    <% it.users.forEach(user => { %>
      <tr>
        <td><%= user.username %></td>
        <td><%= user.email %></td>
        <td><%= user.role %></td>
        <td><a href="/users/<%= user.id %>/edit">Edit</a></td>
      </tr>
    <% }) %>
  </tbody>
</table>
  1. 创建 src/views/users/edit.eta 文件:
<% layout('../layouts/main.eta', { title: 'Edit User' }) %>
 
<h1>Edit User: <%= it.user.username %></h1>
 
<form action="/users/<%= it.user.id %>" method="POST">
  <div>
    <label for="role">Role:</label>
    <select name="role" id="role">
      <option value="reader" <%= it.user.role === 'reader' ? 'selected' : '' %>>Reader</option>
      <option value="author" <%= it.user.role === 'author' ? 'selected' : '' %>>Author</option>
      <option value="admin" <%= it.user.role === 'admin' ? 'selected' : '' %>>Admin</option>
    </select>
  </div>
  <button type="submit">Update Role</button>
</form>
  1. 更新 src/routes/posts.ts 文件,应用权限控制:
import { authorOnly, authMiddleware } from "../middleware/auth";
 
export const postRoutes = new Elysia()
  .use(authMiddleware)
  // ... 保留其他路由
  .get("/posts/new", authorOnly, async ({ render }) => {
    return render("posts/new");
  })
  .post("/posts", authorOnly, async ({ body, set, session }) => {
    // ... 创建文章的逻辑
  })
  .get("/posts/:id/edit", authorOnly, async ({ params, render }) => {
    // ... 编辑文章的逻辑
  })
  .post("/posts/:id", authorOnly, async ({ params, body, set }) => {
    // ... 更新文章的逻辑
  })
  .delete("/posts/:id", authorOnly, async ({ params, set }) => {
    // ... 删除文章的逻辑
  });
  1. 更新 src/views/layouts/main.eta 文件,根据用户角色显示不同的导航选项:
<header>
  <nav>
    <a href="/">Home</a>
    <% if (it.user) { %>
      <% if (it.user.role === 'admin' || it.user.role === 'author') { %>
        <a href="/posts/new">New Post</a>
      <% } %>
      <% if (it.user.role === 'admin') { %>
        <a href="/users">User Management</a>
      <% } %>
      <a href="/profile">Profile</a>
      <a href="/logout">Logout</a>
    <% } else { %>
      <a href="/login">Login</a>
      <a href="/register">Register</a>
    <% } %>
    <a href="/archive">Archive</a>
    <a href="/tags">Tag Cloud</a>
  </nav>
</header>
  1. 更新 src/routes/auth.ts 文件,在注册时设置默认角色:
export const authRoutes = new Elysia()
  // ... 保留其他路由
  .post("/register", async ({ body, set, session }) => {
    const { username, email, password } = body;
    const hashedPassword = await Bun.password.hash(password);
    
    const [user] = await db.insert(users).values({
      username,
      email,
      password: hashedPassword,
      role: "reader", // 设置默认角色为 reader
    }).returning();
 
    session.user = { id: user.id, username: user.username, role: user.role };
    set.redirect = "/";
  });

这些更改实现了基本的用户权限管理系统:

  1. 我们在用户表中添加了 role 字段,可以是 “admin”、“author” 或 “reader”。
  2. 创建了中间件来检查用户权限。
  3. 实现了管理员专用的用户管理页面,可以查看和编辑用户角色。
  4. 限制了某些操作(如创建、编辑、删除文章)只能由作者或管理员执行。
  5. 根据用户角色调整了导航栏的显示内容。

主要改进包括:

  • 更细致的访问控制,提高了系统安全性。
  • 管理员可以轻松管理用户角色。
  • 用户界面根据角色动态调整,提供更好的用户体验。

这个实现为博客系统提供了基本的角色基础访问控制(RBAC)。管理员可以全面管理系统,作者可以创建和管理内容,而读者则主要进行阅读和评论。

接下来,我们可以考虑以下方向来进一步完善系统:

  1. 实现更细粒度的权限控制,例如对特定文章或分类的权限。
  2. 添加用户审核机制,新注册的用户需要管理员批准。
  3. 实现文章的协作编辑功能。
  4. 添加用户活动日志,记录重要操作。

你希望继续哪个方向,或者有其他想法吗?