功能:完善用户与项目管理框架

This commit is contained in:
Coldsmile_7
2026-06-13 00:50:11 +08:00
parent db6d6de74a
commit 49c86bc14a
25 changed files with 3571 additions and 192 deletions
+77 -11
View File
@@ -1,18 +1,64 @@
import net from "node:net";
import tls from "node:tls";
import { db, setSetting } from "./db.mjs";
import { requireCmsUser } from "./auth.mjs";
import { requirePermission } from "./auth.mjs";
import { json, readBody } from "./http.mjs";
import { permissions } from "./permissions.mjs";
function requireAdmin(request, response) {
const user = requireCmsUser(request, response);
function getSettingValue(key, fallback = "") {
return db.prepare("SELECT value FROM system_settings WHERE key = ?").get(key)?.value ?? fallback;
}
if (!user) return undefined;
function testSmtpConnection({ host, port }) {
return new Promise((resolve, reject) => {
const socketFactory = Number(port) === 465 ? tls.connect : net.connect;
const socket = socketFactory({
host,
port: Number(port),
servername: host,
timeout: 10000,
});
let buffer = "";
if (user.role !== "system_admin") {
json(response, 403, { error: "System admin permission is required" });
return undefined;
}
const finish = (result) => {
socket.removeAllListeners();
socket.end();
resolve(result);
};
return user;
socket.on("connect", () => {
if (Number(port) === 465) {
socket.write("EHLO vitepress-cms.local\r\n");
}
});
socket.on("secureConnect", () => {
socket.write("EHLO vitepress-cms.local\r\n");
});
socket.on("data", (chunk) => {
buffer += chunk.toString("utf-8");
if (buffer.startsWith("220")) {
socket.write("EHLO vitepress-cms.local\r\n");
}
if (buffer.includes("\n250 ") || buffer.includes("\n250-") || buffer.startsWith("250")) {
socket.write("QUIT\r\n");
finish({
ok: true,
greeting: buffer.split(/\r?\n/).filter(Boolean).slice(0, 3).join(" / "),
});
}
});
socket.on("timeout", () => {
socket.destroy();
reject(new Error("SMTP connection timed out"));
});
socket.on("error", reject);
});
}
function publicSetting(row) {
@@ -25,7 +71,7 @@ function publicSetting(row) {
}
async function handleGetSettings(request, response) {
if (!requireAdmin(request, response)) return;
if (!requirePermission(request, response, permissions.settingsManage)) return;
const rows = db
.prepare("SELECT key, value, encrypted, updated_at FROM system_settings ORDER BY key")
@@ -35,7 +81,7 @@ async function handleGetSettings(request, response) {
}
async function handleSaveSettings(request, response) {
if (!requireAdmin(request, response)) return;
if (!requirePermission(request, response, permissions.settingsManage)) return;
const body = JSON.parse(await readBody(request) || "{}");
const settings = Array.isArray(body.settings) ? body.settings : [];
@@ -52,6 +98,21 @@ async function handleSaveSettings(request, response) {
json(response, 200, { ok: true });
}
async function handleTestSmtp(request, response) {
if (!requirePermission(request, response, permissions.settingsManage)) return;
const host = getSettingValue("smtp.host");
const port = getSettingValue("smtp.port", "587");
if (!host) {
json(response, 400, { error: "SMTP host is required" });
return;
}
const result = await testSmtpConnection({ host, port });
json(response, 200, result);
}
export async function handleSettingsApi(request, response, url) {
try {
if (url.pathname === "/api/admin/settings" && request.method === "GET") {
@@ -64,6 +125,11 @@ export async function handleSettingsApi(request, response, url) {
return true;
}
if (url.pathname === "/api/admin/settings/test-smtp" && request.method === "POST") {
await handleTestSmtp(request, response);
return true;
}
return false;
} catch (error) {
json(response, 500, {