import { Pool, PoolClient } from "pg"; const dbConfig = { host: "118.89.161.243", port: 54201, user: "hjdave", password: "HJD13567840170", database: "hjdave-doc", max: 20, idleTimeoutMillis: 30000, connectionTimeoutMillis: 2000, }; // 全局连接池单例 let pool: Pool | null = null; export function getPool(): Pool { if (!pool) { pool = new Pool(dbConfig); pool.on("error", (err) => { console.error("Unexpected error on idle client", err); }); } return pool; } export async function query(sql: string, params?: any[]): Promise { const pool = getPool(); const start = Date.now(); try { const res = await pool.query(sql, params); const duration = Date.now() - start; console.log("Executed query", { sql, duration, rows: res.rowCount }); return res; } catch (error) { console.error("Database query error", error); throw error; } } export async function getClient(): Promise { const client = await getPool().connect(); const release = () => client.release(); return client as PoolClient & { release: () => void }; } // 初始化数据库表 export async function initializeDatabase(): Promise { // 创建用户表 await query(` CREATE TABLE IF NOT EXISTS users ( id SERIAL PRIMARY KEY, username VARCHAR(255) UNIQUE NOT NULL, email VARCHAR(255) UNIQUE, password_hash VARCHAR(255) NOT NULL, role VARCHAR(50) DEFAULT 'admin', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); `); // 创建文档表 await query(` CREATE TABLE IF NOT EXISTS docs ( id SERIAL PRIMARY KEY, slug VARCHAR(255) UNIQUE NOT NULL, title VARCHAR(255) NOT NULL, description TEXT, created_by INTEGER REFERENCES users(id), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); `); // 创建章节表 await query(` CREATE TABLE IF NOT EXISTS sections ( id SERIAL PRIMARY KEY, doc_id INTEGER REFERENCES docs(id) ON DELETE CASCADE, title VARCHAR(255) NOT NULL, content TEXT, code TEXT, display_order INTEGER DEFAULT 0, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); `); // 创建索引 await query("CREATE INDEX IF NOT EXISTS idx_docs_slug ON docs(slug)"); await query("CREATE INDEX IF NOT EXISTS idx_docs_created_by ON docs(created_by)"); await query("CREATE INDEX IF NOT EXISTS idx_sections_doc_id ON sections(doc_id)"); // 便签表 await query(` CREATE TABLE IF NOT EXISTS notes ( id SERIAL PRIMARY KEY, user_id INTEGER REFERENCES users(id) ON DELETE CASCADE, title VARCHAR(255) NOT NULL, content TEXT, color VARCHAR(20) DEFAULT '#fef08a', is_pinned BOOLEAN DEFAULT FALSE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); `); // 留言表 await query(` CREATE TABLE IF NOT EXISTS messages ( id SERIAL PRIMARY KEY, user_id INTEGER REFERENCES users(id) ON DELETE CASCADE, content TEXT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); `); // 便签索引 await query("CREATE INDEX IF NOT EXISTS idx_notes_user_id ON notes(user_id)"); await query("CREATE INDEX IF NOT EXISTS idx_notes_pinned ON notes(is_pinned)"); await query("CREATE INDEX IF NOT EXISTS idx_messages_user_id ON messages(user_id)"); // 初始化默认管理员账户 const existing = await query("SELECT id FROM users WHERE username = $1", ["admin"]); if (existing.rows.length === 0) { await query( "INSERT INTO users (username, email, password_hash, role) VALUES ($1, $2, $3, $4)", ["admin", "admin@docsite.com", "$2b$10$d1BPt0phBsRORuWnAacgZO.Y0LBZ3hLc8uKyZBN1BkPMR8j4GESHS", "admin"] ); console.log("Created default admin user"); } else { // 更新密码(如果之前存在但密码不正确) await query( "UPDATE users SET password_hash = $1 WHERE username = $2", ["$2b$10$d1BPt0phBsRORuWnAacgZO.Y0LBZ3hLc8uKyZBN1BkPMR8j4GESHS", "admin"] ); console.log("Updated admin password"); } console.log("Database initialized successfully"); } // 获取密码哈希 export async function getPasswordHash(username: string): Promise { const result = await query( "SELECT password_hash FROM users WHERE username = $1", [username] ); return result.rows[0]?.password_hash ?? null; } // 检查是否是管理员 export async function isAdmin(username: string): Promise { const result = await query( "SELECT role FROM users WHERE username = $1", [username] ); return result.rows[0]?.role === "admin"; } // 验证用户登录 export async function verifyUser(username: string, password: string): Promise { const hash = await getPasswordHash(username); if (!hash) return false; const bcrypt = await import("bcryptjs"); return bcrypt.compare(password, hash); } // 创建新用户 export async function createUser( username: string, password: string, email?: string, role: "admin" | "user" = "user" ): Promise { const bcrypt = await import("bcryptjs"); const passwordHash = await bcrypt.hash(password, 10); try { await query( "INSERT INTO users (username, email, password_hash, role) VALUES ($1, $2, $3, $4)", [username, email, passwordHash, role] ); return true; } catch (error) { console.error("Failed to create user:", error); return false; } } // 获取所有文档 export async function getDocs(): Promise { const result = await query( ` SELECT d.*, json_agg( json_build_object( 'id', s.id, 'title', s.title, 'content', s.content, 'code', s.code, 'display_order', s.display_order ) ORDER BY s.display_order ) as sections FROM docs d LEFT JOIN sections s ON s.doc_id = d.id GROUP BY d.id ORDER BY d.created_at DESC ` ); return result.rows.map((row: any) => ({ title: row.title, slug: row.slug, description: row.description, sections: JSON.parse(row.sections || "[]"), })); } // 添加文档 export async function addDoc( slug: string, title: string, description?: string ): Promise { try { await query( "INSERT INTO docs (slug, title, description) VALUES ($1, $2, $3)", [slug, title, description] ); return true; } catch (error) { console.error("Failed to add doc:", error); return false; } } // 添加章节 export async function addSection(docSlug: string, title: string, content: string, code?: string): Promise { const docResult = await query("SELECT id FROM docs WHERE slug = $1", [docSlug]); if (docResult.rows.length === 0) return false; const docId = docResult.rows[0].id; try { await query( "INSERT INTO sections (doc_id, title, content, code) VALUES ($1, $2, $3, $4)", [docId, title, content, code || null] ); return true; } catch (error) { console.error("Failed to add section:", error); return false; } } // 获取所有文档(简化版,直接返回 JSON) export async function getDocsSimple(): Promise { return [ { title: "快速开始", slug: "quick-start", description: "了解如何开始使用我们的平台", sections: [ { title: "简介", content: "欢迎使用我们的在线文档系统!", code: null }, { title: "安装", content: "开始之前,请确保你已经安装了 Node.js 和 npm。", code: "npm install\nnpm run dev" }, ], }, { title: "API 参考", slug: "api-reference", description: "API 接口文档", sections: [{ title: "获取文档列表", content: "获取所有可用文档的列表。", code: "GET /api/docs" }], }, ]; } // 便签相关 export async function getNotes(userId: number): Promise { const result = await query( `SELECT * FROM notes WHERE user_id = $1 ORDER BY is_pinned DESC, created_at DESC`, [userId] ); return result.rows; } export async function addNote(userId: number, title: string, content: string, color: string, isPinned: boolean): Promise { try { await query( "INSERT INTO notes (user_id, title, content, color, is_pinned) VALUES ($1, $2, $3, $4, $5)", [userId, title, content, color, isPinned] ); return true; } catch (error) { console.error("Failed to add note:", error); return false; } } export async function deleteNote(userId: number, noteId: number): Promise { try { await query("DELETE FROM notes WHERE id = $1 AND user_id = $2", [noteId, userId]); return true; } catch (error) { console.error("Failed to delete note:", error); return false; } } export async function togglePinNote(userId: number, noteId: number): Promise { try { await query("UPDATE notes SET is_pinned = NOT is_pinned WHERE id = $1 AND user_id = $2", [noteId, userId]); return true; } catch (error) { console.error("Failed to toggle pin:", error); return false; } } // 留言相关 export async function getMessages(): Promise { const result = await query(` SELECT m.*, u.username FROM messages m JOIN users u ON m.user_id = u.id ORDER BY m.created_at DESC LIMIT 100 `); return result.rows; } export async function addMessage(userId: number, content: string): Promise { try { await query( "INSERT INTO messages (user_id, content) VALUES ($1, $2)", [userId, content] ); return true; } catch (error) { console.error("Failed to add message:", error); return false; } } export async function deleteMessage(userId: number, messageId: number): Promise { try { await query("DELETE FROM messages WHERE id = $1 AND user_id = $2", [messageId, userId]); return true; } catch (error) { console.error("Failed to delete message:", error); return false; } }