exec(' CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT UNIQUE NOT NULL, password TEXT NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP ) '); // 创建笔记表(标题不加密,内容加密) $db->exec(' CREATE TABLE IF NOT EXISTS notes ( id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, content_enc TEXT NOT NULL, tags TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ) '); // 创建附件表 $db->exec(' CREATE TABLE IF NOT EXISTS attachments ( id INTEGER PRIMARY KEY AUTOINCREMENT, note_id INTEGER NOT NULL, filename TEXT NOT NULL, original_name TEXT NOT NULL, file_size INTEGER NOT NULL, file_type TEXT NOT NULL, upload_time DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (note_id) REFERENCES notes (id) ON DELETE CASCADE ) '); // 创建分享表 $db->exec(' CREATE TABLE IF NOT EXISTS shares ( id INTEGER PRIMARY KEY AUTOINCREMENT, note_id INTEGER NOT NULL, share_token TEXT UNIQUE NOT NULL, password TEXT, view_limit INTEGER DEFAULT 0, view_count INTEGER DEFAULT 0, expires_at DATETIME, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (note_id) REFERENCES notes (id) ON DELETE CASCADE ) '); // 创建索引 $db->exec('CREATE INDEX IF NOT EXISTS idx_notes_title ON notes(title)'); $db->exec('CREATE INDEX IF NOT EXISTS idx_notes_tags ON notes(tags)'); $db->exec('CREATE INDEX IF NOT EXISTS idx_notes_created ON notes(created_at)'); $db->exec('CREATE INDEX IF NOT EXISTS idx_attachments_note_id ON attachments(note_id)'); $db->exec('CREATE INDEX IF NOT EXISTS idx_shares_token ON shares(share_token)'); $db->exec('CREATE INDEX IF NOT EXISTS idx_shares_note_id ON shares(note_id)'); return $db; } catch (Exception $e) { die('数据库初始化失败: ' . $e->getMessage()); } } // 获取数据库连接 function getDB() { static $db = null; if ($db === null) { $db = initDatabase(); } return $db; } // 检查是否已初始化 function isInitialized() { $db = getDB(); $result = $db->querySingle('SELECT COUNT(*) FROM users'); return $result > 0; } // 验证用户登录 function verifyUser($username, $password) { $db = getDB(); $stmt = $db->prepare('SELECT password FROM users WHERE username = ?'); $stmt->bindValue(1, $username, SQLITE3_TEXT); $result = $stmt->execute(); $user = $result->fetchArray(SQLITE3_ASSOC); if ($user && password_verify($password, $user['password'])) { return true; } return false; } // 创建用户 function createUser($username, $password) { $db = getDB(); $hashedPassword = password_hash($password, PASSWORD_DEFAULT); $stmt = $db->prepare('INSERT INTO users (username, password) VALUES (?, ?)'); $stmt->bindValue(1, $username, SQLITE3_TEXT); $stmt->bindValue(2, $hashedPassword, SQLITE3_TEXT); return $stmt->execute(); } // 更新用户密码 function updatePassword($newPassword) { $db = getDB(); $hashedPassword = password_hash($newPassword, PASSWORD_DEFAULT); $stmt = $db->prepare('UPDATE users SET password = ? WHERE id = 1'); $stmt->bindValue(1, $hashedPassword, SQLITE3_TEXT); return $stmt->execute(); } // 获取笔记总数 function getNotesCount($search = '', $tag = '') { $db = getDB(); $sql = 'SELECT COUNT(*) FROM notes WHERE 1=1'; $params = []; if ($search) { // 现在可以搜索标题了 $sql .= ' AND (title LIKE ? OR tags LIKE ?)'; $searchParam = '%' . $search . '%'; $params[] = $searchParam; $params[] = $searchParam; } if ($tag) { $sql .= ' AND tags LIKE ?'; $params[] = '%' . $tag . '%'; } $stmt = $db->prepare($sql); foreach ($params as $i => $param) { $stmt->bindValue($i + 1, $param, SQLITE3_TEXT); } return $stmt->execute()->fetchArray()[0]; } // 获取笔记列表(分页) function getNotes($page = 1, $search = '', $tag = '') { $db = getDB(); $offset = ($page - 1) * NOTES_PER_PAGE; $sql = 'SELECT * FROM notes WHERE 1=1'; $params = []; if ($search) { $sql .= ' AND (title LIKE ? OR tags LIKE ?)'; $params[] = '%' . $search . '%'; $params[] = '%' . $search . '%'; } if ($tag) { $sql .= ' AND tags LIKE ?'; $params[] = '%' . $tag . '%'; } $sql .= ' ORDER BY updated_at DESC LIMIT ? OFFSET ?'; $params[] = NOTES_PER_PAGE; $params[] = $offset; $stmt = $db->prepare($sql); foreach ($params as $i => $param) { $stmt->bindValue($i + 1, $param, SQLITE3_TEXT); } $result = $stmt->execute(); $notes = []; while ($row = $result->fetchArray(SQLITE3_ASSOC)) { // 只解密内容,标题不需要解密 $row['content'] = Encryption::decrypt($row['content_enc']); unset($row['content_enc']); $notes[] = $row; } return $notes; } // 获取单条笔记 function getNote($id) { $db = getDB(); $stmt = $db->prepare('SELECT * FROM notes WHERE id = ?'); $stmt->bindValue(1, $id, SQLITE3_INTEGER); $result = $stmt->execute(); $note = $result->fetchArray(SQLITE3_ASSOC); if ($note) { // 只解密内容,标题不需要解密 $note['content'] = Encryption::decrypt($note['content_enc']); unset($note['content_enc']); } return $note; } // 创建笔记 function createNote($title, $content, $tags) { $db = getDB(); // 处理内容中的链接和图片 $processedContent = processContent($content); $stmt = $db->prepare('INSERT INTO notes (title, content_enc, tags) VALUES (?, ?, ?)'); $stmt->bindValue(1, $title, SQLITE3_TEXT); $stmt->bindValue(2, Encryption::encrypt($processedContent), SQLITE3_TEXT); $stmt->bindValue(3, $tags, SQLITE3_TEXT); $stmt->execute(); return $db->lastInsertRowID(); } // 更新笔记 function updateNote($id, $title, $content, $tags) { $db = getDB(); // 处理内容中的链接和图片 $processedContent = processContent($content); $stmt = $db->prepare('UPDATE notes SET title = ?, content_enc = ?, tags = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?'); $stmt->bindValue(1, $title, SQLITE3_TEXT); $stmt->bindValue(2, Encryption::encrypt($processedContent), SQLITE3_TEXT); $stmt->bindValue(3, $tags, SQLITE3_TEXT); $stmt->bindValue(4, $id, SQLITE3_INTEGER); return $stmt->execute(); } // 删除笔记 function deleteNote($id) { $db = getDB(); $stmt = $db->prepare('DELETE FROM notes WHERE id = ?'); $stmt->bindValue(1, $id, SQLITE3_INTEGER); return $stmt->execute(); } // 获取笔记附件 function getNoteAttachments($noteId) { $db = getDB(); $stmt = $db->prepare('SELECT * FROM attachments WHERE note_id = ? ORDER BY upload_time DESC'); $stmt->bindValue(1, $noteId, SQLITE3_INTEGER); $result = $stmt->execute(); $attachments = []; while ($row = $result->fetchArray(SQLITE3_ASSOC)) { $attachments[] = $row; } return $attachments; } // 添加附件 function addAttachment($noteId, $filename, $originalName, $fileSize, $fileType) { $db = getDB(); $stmt = $db->prepare('INSERT INTO attachments (note_id, filename, original_name, file_size, file_type) VALUES (?, ?, ?, ?, ?)'); $stmt->bindValue(1, $noteId, SQLITE3_INTEGER); $stmt->bindValue(2, $filename, SQLITE3_TEXT); $stmt->bindValue(3, $originalName, SQLITE3_TEXT); $stmt->bindValue(4, $fileSize, SQLITE3_INTEGER); $stmt->bindValue(5, $fileType, SQLITE3_TEXT); return $stmt->execute(); } // 删除附件 function deleteAttachment($id) { $db = getDB(); $stmt = $db->prepare('SELECT filename FROM attachments WHERE id = ?'); $stmt->bindValue(1, $id, SQLITE3_INTEGER); $result = $stmt->execute(); $attachment = $result->fetchArray(SQLITE3_ASSOC); if ($attachment && file_exists(UPLOAD_DIR . '/' . $attachment['filename'])) { unlink(UPLOAD_DIR . '/' . $attachment['filename']); } $stmt = $db->prepare('DELETE FROM attachments WHERE id = ?'); $stmt->bindValue(1, $id, SQLITE3_INTEGER); return $stmt->execute(); } // 获取所有标签 function getAllTags() { $db = getDB(); $result = $db->query('SELECT tags FROM notes WHERE tags IS NOT NULL AND tags != ""'); $tags = []; while ($row = $result->fetchArray(SQLITE3_ASSOC)) { $noteTags = explode(',', $row['tags']); foreach ($noteTags as $tag) { $tag = trim($tag); if ($tag && !in_array($tag, $tags)) { $tags[] = $tag; } } } sort($tags); return $tags; } // 处理文件上传 function handleFileUpload($file, $noteId) { if ($file['error'] !== UPLOAD_ERR_OK) { return false; } $allowedTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp', 'application/pdf', 'text/plain', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document']; $maxSize = 10 * 1024 * 1024; // 10MB if (!in_array($file['type'], $allowedTypes) || $file['size'] > $maxSize) { return false; } $extension = pathinfo($file['name'], PATHINFO_EXTENSION); $filename = uniqid() . '.' . $extension; $uploadPath = UPLOAD_DIR . '/' . $filename; if (move_uploaded_file($file['tmp_name'], $uploadPath)) { addAttachment($noteId, $filename, $file['name'], $file['size'], $file['type']); return $filename; } return false; } // Markdown转HTML(增强版) function markdownToHtml($markdown) { // 简单的Markdown解析 $html = $markdown; // 图片(支持本地上传的图片) $html = preg_replace('/!\[(.*?)\]\((.*?)\)/', '$1', $html); // 标题 $html = preg_replace('/^### (.*$)/m', '

$1

', $html); $html = preg_replace('/^## (.*$)/m', '

$1

', $html); $html = preg_replace('/^# (.*$)/m', '

$1

', $html); // 粗体和斜体 $html = preg_replace('/\*\*(.*?)\*\*/', '$1', $html); $html = preg_replace('/\*(.*?)\*/', '$1', $html); // 代码块 $html = preg_replace('/```(.*?)```/s', '
$1
', $html); $html = preg_replace('/`(.*?)`/', '$1', $html); // 链接 $html = preg_replace('/\[(.*?)\]\((.*?)\)/', '$1', $html); // 无序列表 $html = preg_replace('/^\* (.+)$/m', '
  • $1
  • ', $html); $html = preg_replace('/(
  • .*<\/li>)/s', '', $html); // 有序列表 $html = preg_replace('/^\d+\. (.+)$/m', '
  • $1
  • ', $html); // 引用 $html = preg_replace('/^> (.+)$/m', '
    $1
    ', $html); // 换行 $html = preg_replace('/\n\n/', '

    ', $html); $html = preg_replace('/\n/', '
    ', $html); // 段落 $html = '

    ' . $html . '

    '; // 清理多余的段落标签 $html = preg_replace('/

    <\/p>/', '', $html); $html = preg_replace('/

    ()/', '$1', $html); $html = preg_replace('/(<\/h[1-6]>)<\/p>/', '$1', $html); $html = preg_replace('/

    (

    )/', '$1', $html);
        $html = preg_replace('/(<\/pre>)<\/p>/', '$1', $html);
        $html = preg_replace('/

    (