Express 加 sqlite3 写一个简单博客

例图:

搭建 命令: 

前提已装好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

<!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;}.post {margin-bottom: 20px;padding: 15px;border: 1px solid #ddd;border-radius: 5px;}.post h2 {margin-top: 0;}.post-date {color: #666;font-size: 0.9em;}.new-post-btn {display: inline-block;padding: 10px 20px;background-color: #007bff;color: white;text-decoration: none;border-radius: 5px;margin-bottom: 20px;}.categories {margin: 20px 0;padding: 10px 0;border-bottom: 1px solid #eee;}.category-link {display: inline-block;padding: 5px 10px;margin-right: 10px;text-decoration: none;color: #666;border-radius: 3px;}.category-link.active {background-color: #007bff;color: white;}.post-category {display: inline-block;padding: 3px 8px;background-color: #e9ecef;border-radius: 3px;font-size: 0.9em;margin-right: 10px;}.search-box {margin: 20px 0;display: flex;gap: 10px;}.search-input {flex: 1;padding: 8px;border: 1px solid #ddd;border-radius: 4px;font-size: 1em;}.search-btn {padding: 8px 20px;background-color: #007bff;color: white;border: none;border-radius: 4px;cursor: pointer;}.search-btn:hover {background-color: #0056b3;}.search-results {margin-bottom: 20px;padding: 10px;background-color: #f8f9fa;border-radius: 4px;}.search-results-hidden {display: none;}</style>
</head>
<body><h1>博客文章列表</h1><a href="/post/new" class="new-post-btn">写新文章</a><form class="search-box" action="/" method="GET"><input type="text" name="search" class="search-input" placeholder="搜索文章标题或内容..." value="<%= search_query %>"><button type="submit" class="search-btn">搜索</button></form><div class="search-results <%= !search_query ? 'search-results-hidden' : '' %>">搜索结果: "<%= search_query || '' %>" - 找到 <%= posts ? posts.length : 0 %> 篇文章</div><div class="categories"><a href="/" class="category-link <%= !current_category ? 'active' : '' %>">全部</a><% categories.forEach(function(category) { %><a href="/?category=<%= category.id %>" class="category-link <%= current_category == category.id ? 'active' : '' %>"><%= category.name %></a><% }); %></div><% if (posts && posts.length > 0) { %><% posts.forEach(function(post) { %><div class="post"><h2><a href="/post/<%= post.id %>"><%= post.title %></a></h2><div class="post-meta"><span class="post-category"><%= post.category_name || '未分类' %></span><span class="post-date">发布时间: <%= new Date(post.created_at).toLocaleString() %></span></div><p><%= post.content.substring(0, 200) %>...</p></div><% }); %><% } else { %><p>还没有任何博客文章。</p><% } %>
</body>
</html> 

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/66329.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux双端口服务器:端口1的文件系统目录挂载到端口2

目录 一、服务器安装NFS服务并配置二、文件挂载三、持久化挂载总结为什么服务器配置多个端口 目前有一台服务器&#xff0c;不过他设置了两个SSH的端口&#xff0c;通过下面方法可以让这两个端口连接的主机能够共享同一个文件系统&#xff0c;原本这两个端口的文件系统是隔离的…

nginx-灰度发布策略(split_clients)

一. 简述&#xff1a; 基于客户端的灰度发布&#xff08;也称为蓝绿部署或金丝雀发布&#xff09;是一种逐步将新版本的服务或应用暴露给部分用户&#xff0c;以确保在出现问题时可以快速回滚并最小化影响的技术。对于 Nginx&#xff0c;可以通过配置和使用不同的模块来实现基于…

【NLP自然语言处理】Transformer模型的几大核心优势与应用前景

目录 &#x1f354; Transformer的并行计算 &#x1f354; Transformer架构的并行化过程 2.1 Transformer架构中Encoder的并行化 2.2 Transformer架构中Decoder的并行化 &#x1f354; Transformer的特征抽取能力 &#x1f354; 为什么说Transformer可以代替seq2seq? 4…

数据结构与算法之排序

9.1 排序的概念 1. 排序的定义 定义&#xff1a;排序是将表中的记录按关键字递增&#xff08;或递减&#xff09;有序排列的过程。说明&#xff1a;数据中可以存在相同关键字的记录。本章主要考虑递增排序。扩展&#xff1a;排序是数据处理中的基本操作之一&#xff0c;广泛应用…

《C++11》各种初始化方式的详细列举与对比

在 C 中&#xff0c;初始化对象的方式多种多样。随着 C 标准的演进&#xff0c;特别是 C11 的引入&#xff0c;初始化方式得到了显著的扩展和改进。本文将详细列举 C 中的各种初始化方式&#xff0c;并对它们进行对比&#xff0c;帮助开发者更好地理解和应用这些特性。 1. C98…

基于 Python Django 的西西家居全屋定制系统(源码+部署+文档)

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

25考研|重邮软件工程复试攻略!

与计算机一样&#xff0c;重邮复试不合格也很有可能被淘汰&#xff01;快快认真准备起来&#xff01; 一、复试内容 1、笔试&#xff1a;分值100 2、综合面试&#xff1a;满分100 主要考核考生的综合素质和业务能力&#xff0c;由各招生学院具体组织实施&#xff0c;综合面试…

如何制作重识别数据集及如何解决all query identities do not appear in gallery的问题

如何制作重识别数据集 数据集制作链接 注意点&#xff1a; 按照上述方式制作完成数据集之后&#xff0c;分别建立3个文件夹&#xff0c;分别为train&#xff0c;test&#xff0c;query&#xff0c; 值得注意的是&#xff0c;query文件里的相机编号要进行修改&#xff0c;修改…

链地址法(哈希桶)

链地址法&#xff08;哈希桶&#xff09; 解决冲突的思路 开放定址法中所有的元素都放到哈希表⾥&#xff0c;链地址法中所有的数据不再直接存储在哈希表中&#xff0c;哈希表 中存储⼀个指针&#xff0c;没有数据映射这个位置时&#xff0c;这个指针为空&#xff0c;有多个数…

【C语言】可移植性陷阱与缺陷(七): 除法运算时发生的截断

在C语言编程中&#xff0c;除法运算可能会引发一些与可移植性相关的问题&#xff0c;特别是当涉及到整数除法时发生的截断&#xff08;truncation&#xff09;。不同平台对于整数除法的行为和处理方式可能会有所不同&#xff0c;这可能导致代码在不同编译器或硬件平台上的行为不…

了解RabbitMQ的工作原理

RabbitMQ是一个开源的消息代理系统&#xff0c;实现了高级消息队列协议&#xff08;AMQP&#xff09;。在现代分布式系统中&#xff0c;特别是在微服务架构中&#xff0c;RabbitMQ有广泛的应用。本文将详细介绍RabbitMQ的工作原理&#xff0c;并通过实践案例帮助读者理解和应用…

分布式搜索引擎之elasticsearch基本使用3

分布式搜索引擎之elasticsearch基本使用3 1.部署单点es 1.1.创建网络 因为我们还需要部署kibana容器&#xff0c;因此需要让es和kibana容器互联。这里先创建一个网络&#xff1a; docker network create es-net1.2.加载镜像 这里我们采用elasticsearch的7.12.1版本的镜像&…

【FlutterDart】 拖动改变 widget 的窗口尺寸大小GestureDetector~简单实现(10 /100)

上效果 预期的是通过拖动一条边界线改变窗口大小&#xff0c;类似vscode里拖动效果。这个是简单的拖动实现 上代码&#xff1a; import package:flutter/material.dart;class MyDraggableViewDemo extends StatelessWidget {const MyDraggableViewDemo({super.key});override…

使用Dinky快速提交Flink operator任务

官网地址&#xff1a;K8s集成 | Dinky 1.目前使用版本 Dinky1.2.0、Flink1.18.1、Flink operator0.10.0 2.制作镜像 2.1创建DockerFile ARG FLINK_VERSION1.18.1 FROM flink:${FLINK_VERSION}-scala_2.12 RUN mkdir -p /opt/flink/usrlib COPY commons-cli-1.3.1.jar …

查找路由器的管理后台ip【通用找IP】

需求&#xff1a; 刚刚搞了个【小米】路由器&#xff0c;我想进路由的管理后台&#xff0c;提示&#xff1a;安装xx的路由管家&#xff0c;我不想安装 但是无法找到这个管理后台。 而且我是用这个路由作为中继&#xff0c;那么这个路由的ip就会经常更换 尝试通过网上搜索引擎来…

【大数据】(选修)实验4 安装熟悉HBase数据库并实践

实验4 安装熟悉HBase数据库并实践 1、实验目的 (1)理解HBase在Hadoop体系结构中的角色; (2)熟练使用HBase操作常用的Shell命令; (3)熟悉HBase操作常用的Java API。 2、实验平台 操作系统:Linux Hadoop版本:2.6.0或以上版本 HBase版本:1.1.2或以上版本 JDK版…

【通识安全】煤气中毒急救的处置

1.煤气中毒的主要症状与体征一氧化碳中毒&#xff0c;其中毒症状一般分为轻、中、重三种。 (1)轻度&#xff1a;仅有头晕、头痛、眼花、心慌、胸闷、恶心等症状。如迅速打开门窗&#xff0c;或将病人移出中毒环境&#xff0c;使之吸入新鲜空气和休息&#xff0c;给些热饮料&am…

【UI自动化测试】selenium八种定位方式

&#x1f3e1;个人主页&#xff1a;謬熙&#xff0c;欢迎各位大佬到访❤️❤️❤️~ &#x1f472;个人简介&#xff1a;本人编程小白&#xff0c;正在学习互联网求职知识…… 如果您觉得本文对您有帮助的话&#xff0c;记得点赞&#x1f44d;、收藏⭐️、评论&#x1f4ac;&am…

redis各种数据类型介绍

Redis 是一种高性能的键值存储数据库&#xff0c;它支持多种数据类型&#xff0c;使得开发者可以灵活地存储和操作数据。以下是 Redis 支持的主要数据类型及其介绍&#xff1a; 1. 字符串&#xff08;String&#xff09; 字符串是 Redis 中最基本的数据类型&#xff0c;它可以存…

【Linux】Linux命令

目录 ​编辑 系统维护命令 man man&#xff1a;查看 man 手册 sudo passwd 用户名&#xff1a;修改用户密码 su&#xff1a;切换用户 echo ”输出内容“&#xff1a;向终端输出内容&#xff0c;默认换行 date查看当前系统的日期 clear&#xff1a;清屏 df -Th /df -h&…