例图:
搭建 命令:
前提已装好node.js
开始创建项目结构
npm init -y
- package.json:
- {
- "name": "ex01",
- "version": "1.0.0",
- "main": "index.js",
- "scripts": {
- "test": "echo "Error: no test specified" && exit 1"
- },
- "keywords": [],
- "author": "",
- "license": "ISC",
- "description": ""
- }
复制代码 安装必要的依赖
npm install express sqlite3 ejs express-session body-parser
目次:
代码:
app.js
- const express = require('express');
- const session = require('express-session');
- const bodyParser = require('body-parser');
- const path = require('path');
- const db = require('./database');
- const app = express();
- // 配置中间件
- app.set('view engine', 'ejs');
- app.use(express.static(path.join(__dirname, 'public')));
- app.use(bodyParser.urlencoded({ extended: false }));
- app.use(session({
- secret: 'blog_secret_key',
- resave: false,
- saveUninitialized: true
- }));
- // 首页路由
- app.get('/', async (req, res) => {
- try {
- const category_id = req.query.category;
- const search = req.query.search;
- let posts;
- let categories = await db.all('SELECT * FROM categories');
-
- if (search) {
- // 搜索标题和内容
- posts = await db.all(`
- SELECT posts.*, categories.name as category_name
- FROM posts
- LEFT JOIN categories ON posts.category_id = categories.id
- WHERE title LIKE ? OR content LIKE ?
- ORDER BY created_at DESC`,
- [`%${search}%`, `%${search}%`]
- );
- } else if (category_id) {
- posts = await db.all(`
- SELECT posts.*, categories.name as category_name
- FROM posts
- LEFT JOIN categories ON posts.category_id = categories.id
- WHERE category_id = ?
- ORDER BY created_at DESC`, [category_id]);
- } else {
- posts = await db.all(`
- SELECT posts.*, categories.name as category_name
- FROM posts
- LEFT JOIN categories ON posts.category_id = categories.id
- ORDER BY created_at DESC`);
- }
-
- res.render('index', {
- posts,
- categories,
- current_category: category_id,
- search_query: search || ''
- });
- } catch (err) {
- res.status(500).send('数据库错误');
- }
- });
- // 创建博文页面
- app.get('/post/new', async (req, res) => {
- try {
- const categories = await db.all('SELECT * FROM categories');
- res.render('new', { categories });
- } catch (err) {
- res.status(500).send('获取分类失败');
- }
- });
- // 提交新博文
- app.post('/post/new', async (req, res) => {
- const { title, content, category_id } = req.body;
- try {
- await db.run(
- 'INSERT INTO posts (title, content, category_id, created_at) VALUES (?, ?, ?, ?)',
- [title, content, category_id, new Date().toISOString()]
- );
- res.redirect('/');
- } catch (err) {
- res.status(500).send('创建博文失败');
- }
- });
- // 查看单篇博文
- app.get('/post/:id', async (req, res) => {
- try {
- const post = await db.get(`
- SELECT posts.*, categories.name as category_name
- FROM posts
- LEFT JOIN categories ON posts.category_id = categories.id
- WHERE posts.id = ?`, [req.params.id]);
- if (post) {
- res.render('post', { post });
- } else {
- res.status(404).send('博文不存在');
- }
- } catch (err) {
- res.status(500).send('数据库错误');
- }
- });
- // 编辑博文页面
- app.get('/post/:id/edit', async (req, res) => {
- try {
- const post = await db.get('SELECT * FROM posts WHERE id = ?', [req.params.id]);
- const categories = await db.all('SELECT * FROM categories');
- if (post) {
- res.render('edit', { post, categories });
- } else {
- res.status(404).send('博文不存在');
- }
- } catch (err) {
- res.status(500).send('数据库错误');
- }
- });
- // 更新博文
- app.post('/post/:id/edit', async (req, res) => {
- const { title, content, category_id } = req.body;
- try {
- await db.run(
- 'UPDATE posts SET title = ?, content = ?, category_id = ? WHERE id = ?',
- [title, content, category_id, req.params.id]
- );
- res.redirect(`/post/${req.params.id}`);
- } catch (err) {
- res.status(500).send('更新博文失败');
- }
- });
- // 删除博文
- app.post('/post/:id/delete', async (req, res) => {
- try {
- await db.run('DELETE FROM posts WHERE id = ?', [req.params.id]);
- res.redirect('/');
- } catch (err) {
- res.status(500).send('删除博文失败');
- }
- });
- const PORT = process.env.PORT || 3000;
- app.listen(PORT, () => {
- console.log(`服务器运行在 http://localhost:${PORT}`);
- });
复制代码
database.js
- const sqlite3 = require('sqlite3').verbose();
- const path = require('path');
- // 创建数据库连接
- const db = new sqlite3.Database(path.join(__dirname, 'blog.db'), (err) => {
- if (err) {
- console.error('数据库连接失败:', err);
- } else {
- console.log('成功连接到数据库');
- initDatabase().catch(err => {
- console.error('数据库初始化失败:', err);
- });
- }
- });
- // 初始化数据库表
- async function initDatabase() {
- try {
- // 检查表是否存在
- const tablesExist = await get(`
- SELECT name FROM sqlite_master
- WHERE type='table' AND (name='posts' OR name='categories')
- `);
- if (!tablesExist) {
- console.log('首次运行,创建数据库表...');
-
- // 创建分类表
- await run(`
- CREATE TABLE IF NOT EXISTS categories (
- id INTEGER PRIMARY KEY AUTOINCREMENT,
- name TEXT NOT NULL UNIQUE
- )
- `);
- console.log('分类表创建成功');
- // 创建文章表
- await run(`
- CREATE TABLE IF NOT EXISTS posts (
- id INTEGER PRIMARY KEY AUTOINCREMENT,
- title TEXT NOT NULL,
- content TEXT NOT NULL,
- category_id INTEGER,
- created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
- FOREIGN KEY (category_id) REFERENCES categories(id)
- )
- `);
- console.log('文章表创建成功');
- // 插入默认分类
- await run(`
- INSERT INTO categories (name) VALUES
- ('技术'),
- ('生活'),
- ('随笔')
- `);
- console.log('默认分类创建成功');
- } else {
- console.log('数据库表已存在,跳过初始化');
- }
- } catch (err) {
- console.error('数据库初始化错误:', err);
- throw err;
- }
- }
- // Promise 包装数据库操作
- function run(sql, params = []) {
- return new Promise((resolve, reject) => {
- db.run(sql, params, function(err) {
- if (err) {
- console.error('SQL执行错误:', err);
- reject(err);
- } else {
- resolve(this);
- }
- });
- });
- }
- function get(sql, params = []) {
- return new Promise((resolve, reject) => {
- db.get(sql, params, (err, result) => {
- if (err) {
- console.error('SQL执行错误:', err);
- reject(err);
- } else {
- resolve(result);
- }
- });
- });
- }
- function all(sql, params = []) {
- return new Promise((resolve, reject) => {
- db.all(sql, params, (err, rows) => {
- if (err) {
- console.error('SQL执行错误:', err);
- reject(err);
- } else {
- resolve(rows);
- }
- });
- });
- }
- // 关闭数据库连接
- process.on('SIGINT', () => {
- db.close((err) => {
- if (err) {
- console.error('关闭数据库时出错:', err);
- } else {
- console.log('数据库连接已关闭');
- }
- process.exit(0);
- });
- });
- module.exports = {
- run,
- get,
- all
- };
复制代码
views\index.ejs
views\post.ejs
- <!DOCTYPE html>
- <html>
- <head>
- <title><%= post.title %> - 我的博客</title>
- <meta charset="UTF-8">
- <style>
- body {
- font-family: Arial, sans-serif;
- max-width: 800px;
- margin: 0 auto;
- padding: 20px;
- }
- .post-title {
- margin-bottom: 10px;
- }
- .post-meta {
- color: #666;
- margin-bottom: 20px;
- }
- .post-content {
- line-height: 1.6;
- white-space: pre-wrap;
- }
- .back-link {
- display: inline-block;
- margin-bottom: 20px;
- color: #007bff;
- text-decoration: none;
- }
- .post-category {
- display: inline-block;
- padding: 3px 8px;
- background-color: #e9ecef;
- border-radius: 3px;
- font-size: 0.9em;
- margin-right: 10px;
- }
- .action-buttons {
- margin: 20px 0;
- display: flex;
- gap: 10px;
- }
- .edit-btn {
- padding: 5px 15px;
- background-color: #28a745;
- color: white;
- text-decoration: none;
- border-radius: 3px;
- font-size: 0.9em;
- }
- .delete-btn {
- padding: 5px 15px;
- background-color: #dc3545;
- color: white;
- border: none;
- border-radius: 3px;
- cursor: pointer;
- font-size: 0.9em;
- }
- .delete-btn:hover {
- background-color: #c82333;
- }
- </style>
- </head>
- <body>
- <a href="/" class="back-link">← 返回首页</a>
-
- <article>
- <h1 class="post-title"><%= post.title %></h1>
- <div class="post-meta">
- <span class="post-category"><%= post.category_name || '未分类' %></span>
- <span class="post-date">发布时间: <%= new Date(post.created_at).toLocaleString() %></span>
- </div>
-
- <div class="action-buttons">
- <a href="/post/<%= post.id %>/edit" class="edit-btn">编辑文章</a>
- <form action="/post/<%= post.id %>/delete" method="POST" style="display: inline;"
- onsubmit="return confirm('确定要删除这篇文章吗?');">
- <button type="submit" class="delete-btn">删除文章</button>
- </form>
- </div>
-
- <div class="post-content">
- <%= post.content %>
- </div>
- </article>
- </body>
- </html>
复制代码
views\new.ejs
- <!DOCTYPE html>
- <html>
- <head>
- <title>写新文章 - 我的博客</title>
- <meta charset="UTF-8">
- <style>
- body {
- font-family: Arial, sans-serif;
- max-width: 800px;
- margin: 0 auto;
- padding: 20px;
- }
- form {
- display: flex;
- flex-direction: column;
- }
- input, textarea, select {
- margin: 10px 0;
- padding: 8px;
- border: 1px solid #ddd;
- border-radius: 4px;
- }
- textarea {
- height: 300px;
- }
- button {
- padding: 10px 20px;
- background-color: #007bff;
- color: white;
- border: none;
- border-radius: 5px;
- cursor: pointer;
- }
- button:hover {
- background-color: #0056b3;
- }
- .back-link {
- display: inline-block;
- margin-bottom: 20px;
- color: #007bff;
- text-decoration: none;
- }
- label {
- margin-top: 10px;
- color: #666;
- }
- </style>
- </head>
- <body>
- <a href="/" class="back-link">← 返回首页</a>
- <h1>写新文章</h1>
-
- <form action="/post/new" method="POST">
- <label for="title">文章标题</label>
- <input type="text" id="title" name="title" placeholder="文章标题" required>
-
- <label for="category">选择分类</label>
- <select id="category" name="category_id" required>
- <option value="">请选择分类</option>
- <% categories.forEach(function(category) { %>
- <option value="<%= category.id %>"><%= category.name %></option>
- <% }); %>
- </select>
-
- <label for="content">文章内容</label>
- <textarea id="content" name="content" placeholder="文章内容" required></textarea>
-
- <button type="submit">发布文章</button>
- </form>
- </body>
- </html>
复制代码
views\edit.ejs
- <!DOCTYPE html>
- <html>
- <head>
- <title>编辑文章 - 我的博客</title>
- <meta charset="UTF-8">
- <style>
- body {
- font-family: Arial, sans-serif;
- max-width: 800px;
- margin: 0 auto;
- padding: 20px;
- }
- form {
- display: flex;
- flex-direction: column;
- }
- input, textarea, select {
- margin: 10px 0;
- padding: 8px;
- border: 1px solid #ddd;
- border-radius: 4px;
- }
- textarea {
- height: 300px;
- }
- button {
- padding: 10px 20px;
- background-color: #007bff;
- color: white;
- border: none;
- border-radius: 5px;
- cursor: pointer;
- }
- button:hover {
- background-color: #0056b3;
- }
- .back-link {
- display: inline-block;
- margin-bottom: 20px;
- color: #007bff;
- text-decoration: none;
- }
- label {
- margin-top: 10px;
- color: #666;
- }
- </style>
- </head>
- <body>
- <a href="/post/<%= post.id %>" class="back-link">← 返回文章</a>
- <h1>编辑文章</h1>
-
- <form action="/post/<%= post.id %>/edit" method="POST">
- <label for="title">文章标题</label>
- <input type="text" id="title" name="title" value="<%= post.title %>" required>
-
- <label for="category">选择分类</label>
- <select id="category" name="category_id" required>
- <option value="">请选择分类</option>
- <% categories.forEach(function(category) { %>
- <option value="<%= category.id %>" <%= post.category_id == category.id ? 'selected' : '' %>>
- <%= category.name %>
- </option>
- <% }); %>
- </select>
-
- <label for="content">文章内容</label>
- <textarea id="content" name="content" required><%= post.content %></textarea>
-
- <button type="submit">更新文章</button>
- </form>
- </body>
- </html>
复制代码 运行:
node app.js
服务器运行在 http://localhost:3000
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |