功能:完善用户与项目管理框架
This commit is contained in:
@@ -0,0 +1,143 @@
|
||||
import { db } from "./db.mjs";
|
||||
import { requirePermission, publicUser } from "./auth.mjs";
|
||||
import { json, readBody } from "./http.mjs";
|
||||
import { permissions } from "./permissions.mjs";
|
||||
import { hashPassword } from "./security.mjs";
|
||||
|
||||
const roles = new Set(["system_admin", "admin", "editor", "viewer", "user"]);
|
||||
const statuses = new Set(["active", "disabled"]);
|
||||
|
||||
function listUsers() {
|
||||
return db
|
||||
.prepare(`
|
||||
SELECT id, email, name, avatar_url, role, status, email_verified, created_at, updated_at, last_login_at
|
||||
FROM users
|
||||
ORDER BY created_at DESC, id DESC
|
||||
`)
|
||||
.all()
|
||||
.map(publicUser);
|
||||
}
|
||||
|
||||
function validateEmail(email) {
|
||||
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
|
||||
}
|
||||
|
||||
async function handleListUsers(request, response) {
|
||||
if (!requirePermission(request, response, permissions.usersManage)) return;
|
||||
json(response, 200, { users: listUsers() });
|
||||
}
|
||||
|
||||
async function handleCreateUser(request, response) {
|
||||
if (!requirePermission(request, response, permissions.usersManage)) return;
|
||||
|
||||
const body = JSON.parse((await readBody(request)) || "{}");
|
||||
const email = String(body.email || "").trim().toLowerCase();
|
||||
const name = String(body.name || "").trim();
|
||||
const password = String(body.password || "");
|
||||
const role = String(body.role || "user");
|
||||
|
||||
if (!validateEmail(email)) {
|
||||
json(response, 400, { error: "A valid email is required" });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!name) {
|
||||
json(response, 400, { error: "Name is required" });
|
||||
return;
|
||||
}
|
||||
|
||||
if (password.length < 8) {
|
||||
json(response, 400, { error: "Password must be at least 8 characters" });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!roles.has(role)) {
|
||||
json(response, 400, { error: "Invalid role" });
|
||||
return;
|
||||
}
|
||||
|
||||
const existing = db.prepare("SELECT id FROM users WHERE email = ?").get(email);
|
||||
if (existing) {
|
||||
json(response, 409, { error: "Email already exists" });
|
||||
return;
|
||||
}
|
||||
|
||||
const result = db
|
||||
.prepare("INSERT INTO users (email, password_hash, name, role, email_verified) VALUES (?, ?, ?, ?, 1)")
|
||||
.run(email, hashPassword(password), name, role);
|
||||
|
||||
json(response, 201, { user: publicUser(db.prepare("SELECT * FROM users WHERE id = ?").get(result.lastInsertRowid)) });
|
||||
}
|
||||
|
||||
async function handleUpdateUser(request, response, userId) {
|
||||
const actor = requirePermission(request, response, permissions.usersManage);
|
||||
if (!actor) return;
|
||||
|
||||
const target = db.prepare("SELECT * FROM users WHERE id = ?").get(userId);
|
||||
if (!target) {
|
||||
json(response, 404, { error: "User not found" });
|
||||
return;
|
||||
}
|
||||
|
||||
const body = JSON.parse((await readBody(request)) || "{}");
|
||||
const nextRole = body.role === undefined ? target.role : String(body.role);
|
||||
const nextStatus = body.status === undefined ? target.status || "active" : String(body.status);
|
||||
|
||||
if (!roles.has(nextRole)) {
|
||||
json(response, 400, { error: "Invalid role" });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!statuses.has(nextStatus)) {
|
||||
json(response, 400, { error: "Invalid status" });
|
||||
return;
|
||||
}
|
||||
|
||||
if (Number(actor.id) === Number(userId) && nextStatus !== "active") {
|
||||
json(response, 400, { error: "You cannot disable your own account" });
|
||||
return;
|
||||
}
|
||||
|
||||
if (target.role === "system_admin" && nextRole !== "system_admin") {
|
||||
const adminCount = db.prepare("SELECT COUNT(*) AS count FROM users WHERE role = 'system_admin'").get().count;
|
||||
if (adminCount <= 1) {
|
||||
json(response, 400, { error: "At least one system admin is required" });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
db.prepare("UPDATE users SET role = ?, status = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?").run(
|
||||
nextRole,
|
||||
nextStatus,
|
||||
userId,
|
||||
);
|
||||
|
||||
json(response, 200, { user: publicUser(db.prepare("SELECT * FROM users WHERE id = ?").get(userId)) });
|
||||
}
|
||||
|
||||
export async function handleUsersApi(request, response, url) {
|
||||
try {
|
||||
if (url.pathname === "/api/admin/users" && request.method === "GET") {
|
||||
await handleListUsers(request, response);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (url.pathname === "/api/admin/users" && request.method === "POST") {
|
||||
await handleCreateUser(request, response);
|
||||
return true;
|
||||
}
|
||||
|
||||
const match = url.pathname.match(/^\/api\/admin\/users\/(\d+)$/);
|
||||
if (match && request.method === "PATCH") {
|
||||
await handleUpdateUser(request, response, Number(match[1]));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
} catch (error) {
|
||||
json(response, 500, {
|
||||
error: error instanceof Error ? error.message : "Unknown users API error",
|
||||
});
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user