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('/!\[(.*?)\]\((.*?)\)/', '', $html);
// 标题
$html = preg_replace('/^### (.*$)/m', '
$1', $html);
$html = preg_replace('/`(.*?)`/', '$1', $html);
// 链接
$html = preg_replace('/\[(.*?)\]\((.*?)\)/', '$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('/(<\/pre>)<\/p>/', '$1', $html);
$html = preg_replace('/)/', '$1', $html);
$html = preg_replace('/(<\/ul>)<\/p>/', '$1', $html);
$html = preg_replace('/
)/', '$1', $html);
$html = preg_replace('/(<\/blockquote>)<\/p>/', '$1', $html);
$html = preg_replace('/