Skip to content

Commit 8532802

Browse files
author
Colin Lee
authored
Merge pull request #4 from frontend1991/node-blog
Node blog
2 parents f1d7632 + 6ac2d91 commit 8532802

File tree

13 files changed

+313
-3
lines changed

13 files changed

+313
-3
lines changed

myblog/index.js

+20
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,26 @@ app.use(session({
3232
// flash 中间件,用来显示通知
3333
app.use(flash())
3434

35+
// 处理表单及文件上传的中间件
36+
app.use(require('express-formidable')({
37+
uploadDir: path.join(__dirname, 'public/img'), // 上传文件目录
38+
keepExtensions: true // 保留后缀
39+
}))
40+
41+
// 设置模板全局常量
42+
app.locals.blog = {
43+
title: pkg.name,
44+
description: pkg.description
45+
}
46+
47+
// 添加模板必需的三个变量
48+
app.use(function (req, res, next) {
49+
res.locals.user = req.session.user
50+
res.locals.success = req.flash('success').toString()
51+
res.locals.error = req.flash('error').toString()
52+
next()
53+
})
54+
3555
// 路由
3656
routes(app)
3757

myblog/lib/mongo.js

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
const config = require('config-lite')(__dirname)
2+
const Mongolass = require('mongolass')
3+
const mongolass = new Mongolass()
4+
mongolass.connect(config.mongodb)
5+
6+
exports.User = mongolass.model('User', {
7+
name: { type: 'string', required: true },
8+
password: { type: 'string', required: true },
9+
avatar: { type: 'string', required: true },
10+
gender: { type: 'string', enum: ['m', 'f', 'x'], default: 'x' },
11+
bio: { type: 'string', required: true }
12+
})
13+
14+
exports.User.index({ name: 1 }, { unique: true }).exec() // 根据用户名找到用户,用户名全局唯一

myblog/models/users.js

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
const User = require('../lib/mongo').User
2+
3+
module.exports = {
4+
// 注册一个用户
5+
create: function create (user) {
6+
return User.create(user).exec()
7+
}
8+
}

myblog/public/css/style.css

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/* ---------- 全局样式 ---------- */
2+
3+
body {
4+
width: 1100px;
5+
height: 100%;
6+
margin: 0 auto;
7+
padding-top: 40px;
8+
}
9+
10+
a:hover {
11+
border-bottom: 3px solid #4fc08d;
12+
}
13+
14+
.button {
15+
background-color: #4fc08d !important;
16+
color: #fff !important;
17+
}
18+
19+
.avatar {
20+
border-radius: 3px;
21+
width: 48px;
22+
height: 48px;
23+
float: right;
24+
}
25+
26+
/* ---------- nav ---------- */
27+
28+
.nav {
29+
margin-bottom: 20px;
30+
color: #999;
31+
text-align: center;
32+
}
33+
34+
.nav h1 {
35+
color: #4fc08d;
36+
display: inline-block;
37+
margin: 10px 0;
38+
}
39+
40+
/* ---------- nav-setting ---------- */
41+
42+
.nav-setting {
43+
position: fixed;
44+
right: 30px;
45+
top: 35px;
46+
z-index: 999;
47+
}
48+
49+
.nav-setting .ui.dropdown.button {
50+
padding: 10px 10px 0 10px;
51+
background-color: #fff !important;
52+
}
53+
54+
.nav-setting .icon.bars {
55+
color: #000;
56+
font-size: 18px;
57+
}
58+
59+
/* ---------- post-content ---------- */
60+
61+
.post-content h3 a {
62+
color: #4fc08d !important;
63+
}
64+
65+
.post-content .tag {
66+
font-size: 13px;
67+
margin-right: 5px;
68+
color: #999;
69+
}
70+
71+
.post-content .tag.right {
72+
float: right;
73+
margin-right: 0;
74+
}
75+
76+
.post-content .tag.right a {
77+
color: #999;
78+
}

myblog/routes/posts.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const checkLogin = require('../middlewares/check').checkLogin
55
// GET /posts 所有用户或者特定用户的文章页
66
// eg: GET /posts?author=xxx
77
router.get('/', function (req, res, next) {
8-
res.send('主页')
8+
res.render('posts')
99
})
1010

1111
// POST /posts/create 发表一篇文章

myblog/routes/signup.js

+74-2
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,87 @@
1+
const fs = require('fs')
2+
const path = require('path')
3+
const sha1 = require('sha1')
14
const express = require('express')
25
const router = express.Router()
6+
7+
const UserModel = require('../models/users')
38
const checkNotLogin = require('../middlewares/check').checkNotLogin
49

510
// GET /signup 注册页
611
router.get('/', checkNotLogin, function (req, res, next) {
7-
res.send('注册页')
12+
res.render('signup')
813
})
914

1015
// POST /signup 用户注册
1116
router.post('/', checkNotLogin, function (req, res, next) {
12-
res.send('注册')
17+
const name = req.fields.name
18+
const gender = req.fields.gender
19+
const bio = req.fields.bio
20+
const avatar = req.files.avatar.path.split(path.sep).pop()
21+
let password = req.fields.password
22+
const repassword = req.fields.repassword
23+
24+
// 校验参数
25+
try {
26+
if (!(name.length >= 1 && name.length <= 10)) {
27+
throw new Error('名字请限制在 1-10 个字符')
28+
}
29+
if (['m', 'f', 'x'].indexOf(gender) === -1) {
30+
throw new Error('性别只能是 m、f 或 x')
31+
}
32+
if (!(bio.length >= 1 && bio.length <= 30)) {
33+
throw new Error('个人简介请限制在 1-30 个字符')
34+
}
35+
if (!req.files.avatar.name) {
36+
throw new Error('缺少头像')
37+
}
38+
if (password.length < 6) {
39+
throw new Error('密码至少 6 个字符')
40+
}
41+
if (password !== repassword) {
42+
throw new Error('两次输入密码不一致')
43+
}
44+
} catch (e) {
45+
// 注册失败,异步删除上传的头像
46+
fs.unlink(req.files.avatar.path)
47+
req.flash('error', e.message)
48+
return res.redirect('/signup')
49+
}
50+
51+
// 明文密码加密
52+
password = sha1(password)
53+
54+
// 待写入数据库的用户信息
55+
let user = {
56+
name: name,
57+
password: password,
58+
gender: gender,
59+
bio: bio,
60+
avatar: avatar
61+
}
62+
// 用户信息写入数据库
63+
UserModel.create(user)
64+
.then(function (result) {
65+
// 此 user 是插入 mongodb 后的值,包含 _id
66+
user = result.ops[0]
67+
// 删除密码这种敏感信息,将用户信息存入 session
68+
delete user.password
69+
req.session.user = user
70+
// 写入 flash
71+
req.flash('success', '注册成功')
72+
// 跳转到首页
73+
res.redirect('/posts')
74+
})
75+
.catch(function (e) {
76+
// 注册失败,异步删除上传的头像
77+
fs.unlink(req.files.avatar.path)
78+
// 用户名被占用则跳回注册页,而不是错误页
79+
if (e.message.match('duplicate key')) {
80+
req.flash('error', '用户名已被占用')
81+
return res.redirect('/signup')
82+
}
83+
next(e)
84+
})
1385
})
1486

1587
module.exports = router
+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<div class="nav-setting">
2+
<div class="ui buttons">
3+
<div class="ui floating dropdown button">
4+
<i class="icon bars"></i>
5+
<div class="menu">
6+
<% if (user) { %>
7+
<a class="item" href="/posts?author=<%= user._id %>">个人主页</a>
8+
<div class="divider"></div>
9+
<a class="item" href="/posts/create">发表文章</a>
10+
<a class="item" href="/signout">登出</a>
11+
<% } else { %>
12+
<a class="item" href="/signin">登录</a>
13+
<a class="item" href="/signup">注册</a>
14+
<% } %>
15+
</div>
16+
</div>
17+
</div>
18+
</div>

myblog/views/components/nav.ejs

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<div class="nav">
2+
<div class="ui grid">
3+
<div class="four wide column"></div>
4+
5+
<div class="eight wide column">
6+
<a href="/posts"><h1><%= blog.title %></h1></a>
7+
<p><%= blog.description %></p>
8+
</div>
9+
</div>
10+
</div>
+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<div class="ui grid">
2+
<div class="four wide column"></div>
3+
<div class="eight wide column">
4+
5+
<% if (success) { %>
6+
<div class="ui success message">
7+
<p><%= success %></p>
8+
</div>
9+
<% } %>
10+
11+
<% if (error) { %>
12+
<div class="ui error message">
13+
<p><%= error %></p>
14+
</div>
15+
<% } %>
16+
17+
</div>
18+
</div>

myblog/views/footer.ejs

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<script type="text/javascript">
2+
$(document).ready(function () {
3+
// 点击按钮弹出下拉框
4+
$('.ui.dropdown').dropdown();
5+
6+
// 鼠标悬浮在头像上,弹出气泡提示框
7+
$('.post-content .avatar-link').popup({
8+
inline: true,
9+
position: 'bottom right',
10+
lastResort: 'bottom right'
11+
});
12+
})
13+
</script>
14+
</body>
15+
</html>

myblog/views/header.ejs

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8">
5+
<title><%= blog.title %></title>
6+
<link rel="stylesheet" href="//cdn.bootcss.com/semantic-ui/2.1.8/semantic.min.css">
7+
<link rel="stylesheet" href="/css/style.css">
8+
<script src="//cdn.bootcss.com/jquery/1.11.3/jquery.min.js"></script>
9+
<script src="//cdn.bootcss.com/semantic-ui/2.1.8/semantic.min.js"></script>
10+
</head>
11+
<body>
12+
<%- include('components/nav') %>
13+
<%- include('components/nav-setting') %>
14+
<%- include('components/notification') %>

myblog/views/posts.ejs

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<%- include('header') %>
2+
这是主页
3+
<%- include('footer') %>

myblog/views/signup.ejs

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<%- include('header') %>
2+
3+
<div class="ui grid">
4+
<div class="four wide column"></div>
5+
<div class="eight wide column">
6+
<form class="ui form segment" method="post" enctype="multipart/form-data">
7+
<div class="field required">
8+
<label>用户名</label>
9+
<input placeholder="用户名" type="text" name="name">
10+
</div>
11+
<div class="field required">
12+
<label>密码</label>
13+
<input placeholder="密码" type="password" name="password">
14+
</div>
15+
<div class="field required">
16+
<label>重复密码</label>
17+
<input placeholder="重复密码" type="password" name="repassword">
18+
</div>
19+
<div class="field required">
20+
<label>性别</label>
21+
<select class="ui compact selection dropdown" name="gender">
22+
<option value="m">男</option>
23+
<option value="f">女</option>
24+
<option value="x">保密</option>
25+
</select>
26+
</div>
27+
<div class="field required">
28+
<label>头像</label>
29+
<input type="file" name="avatar">
30+
</div>
31+
<div class="field required">
32+
<label>个人简介</label>
33+
<textarea name="bio" rows="5"></textarea>
34+
</div>
35+
<input type="submit" class="ui button fluid" value="注册">
36+
</form>
37+
</div>
38+
</div>
39+
40+
<%- include('footer') %>

0 commit comments

Comments
 (0)