Skip to content

Commit 54cd499

Browse files
sinchangJacksonTian
authored andcommitted
feat: add user api (#124)
* feat: add user api * chore: remove useless code * chore: fix typo * chore: update get accesstoken * chore: update
1 parent ac01b48 commit 54cd499

File tree

9 files changed

+278
-103
lines changed

9 files changed

+278
-103
lines changed

app/controller/api/user.js

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
'use strict';
2+
3+
const Controller = require('egg').Controller;
4+
const _ = require('lodash');
5+
6+
class UserController extends Controller {
7+
async show() {
8+
const { ctx } = this;
9+
const loginname = ctx.params.loginname;
10+
const user = await ctx.service.user.getUserByLoginName(loginname);
11+
12+
if (!user) {
13+
ctx.status = 404;
14+
ctx.body = { success: false, error_msg: '用户不存在' };
15+
return;
16+
}
17+
18+
const userId = user._id;
19+
const topics = await ctx.service.topic.getTopicsByQuery({ author_id: userId });
20+
const returnUser = _.pick(user, [ 'loginname', 'avatar_url', 'githubUsername', 'create_at', 'score' ]);
21+
const returnTopics = topics.map(topic => {
22+
return {
23+
id: topic._id,
24+
last_reply_at: topic.last_reply_at,
25+
title: topic.title,
26+
author: {
27+
loginname: user.loginname,
28+
avatar_url: user.avatar_url,
29+
},
30+
};
31+
});
32+
const data = returnUser;
33+
data.recent_topics = returnTopics;
34+
ctx.body = {
35+
success: true,
36+
data,
37+
};
38+
}
39+
40+
async verify() {
41+
const { ctx } = this;
42+
const user = ctx.request.user;
43+
ctx.body = {
44+
success: true,
45+
loginname: user.loginname,
46+
id: user._id,
47+
avatar_url: user.avatar_url,
48+
};
49+
}
50+
}
51+
52+
module.exports = UserController;

app/middleware/token_required.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
'use strict';
2+
3+
module.exports = () => {
4+
return async function(ctx, next) {
5+
let token = '';
6+
if (
7+
ctx.headers.authorization && ctx.headers.authorization.split(' ')[0] === 'Bearer'
8+
) {
9+
token = ctx.headers.authorization.split(' ')[1];
10+
} else if (ctx.query.accesstoken) {
11+
token = ctx.query.accesstoken;
12+
} else if (ctx.request.body.accesstoken) {
13+
token = ctx.request.body.accesstoken;
14+
}
15+
16+
const user = await ctx.service.user.getUserByToken(token);
17+
18+
if (!user) {
19+
ctx.status = 401;
20+
ctx.body = {
21+
success: false,
22+
error_msg: '错误的 accessToken',
23+
};
24+
return;
25+
}
26+
27+
if (user.is_block) {
28+
ctx.status = 403;
29+
ctx.body = {
30+
success: false,
31+
error_msg: '您的账户被禁用',
32+
};
33+
return;
34+
}
35+
36+
ctx.request.user = user;
37+
38+
await next();
39+
};
40+
};

app/router.js

Lines changed: 2 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -4,108 +4,7 @@
44
* @param {Egg.Application} app - egg application
55
*/
66
module.exports = app => {
7-
const { router, controller, config, middleware } = app;
8-
9-
const { site, sign, user, topic, rss,
10-
search, page, reply, message } = controller;
11-
12-
const userRequired = middleware.userRequired();
13-
const adminRequired = middleware.adminRequired();
14-
const createTopicLimit = middleware.createTopicLimit(config.topic);
15-
const createUserLimit = middleware.createUserLimit(config.create_user_per_ip);
16-
17-
// home page
18-
router.get('/', site.index);
19-
// sitemap
20-
router.get('/sitemap.xml', site.sitemap);
21-
// mobile app download
22-
router.get('/app/download', site.appDownload);
23-
24-
// sign controller
25-
if (config.allow_sign_up) {
26-
// 跳转到注册页面
27-
router.get('/signup', sign.showSignup);
28-
// 提交注册信息
29-
router.post('/signup', createUserLimit, sign.signup);
30-
} else {
31-
// 进行github验证
32-
router.redirect('/signup', '/passport/github');
33-
}
34-
35-
const localStrategy = app.passport.authenticate('local', {
36-
successRedirect: '/',
37-
failureRedirect: '/signin',
38-
});
39-
40-
router.get('/signin', sign.showLogin); // 进入登录页面
41-
router.post('/passport/local', localStrategy);
42-
router.all('/signout', sign.signout); // 登出
43-
router.get('/active_account', sign.activeAccount); // 帐号激活
44-
45-
// github oauth
46-
app.passport.mount('github');
47-
48-
router.get('/search_pass', sign.showSearchPass); // 找回密码页面
49-
router.post('/search_pass', sign.updateSearchPass); // 更新密码
50-
router.get('/reset_pass', sign.resetPass); // 进入重置密码页面
51-
router.post('/reset_pass', sign.updatePass); // 更新密码
52-
53-
// user controller
54-
router.get('/user/:name', user.index); // 用户个人主页
55-
router.get('/setting', userRequired, user.showSetting); // 用户个人设置页
56-
router.post('/setting', userRequired, user.setting); // 提交个人信息设置
57-
router.get('/stars', user.listStars); // 显示所有达人列表页
58-
router.get('/users/top100', user.top100); // 显示积分前一百用户页
59-
router.get('/user/:name/collections', user.listCollectedTopics); // 用户收藏的所有话题页
60-
router.get('/user/:name/topics', user.listTopics); // 用户发布的所有话题页
61-
router.get('/user/:name/replies', user.listReplies); // 用户参与的所有回复页
62-
router.post('/user/set_star', adminRequired, user.toggleStar); // 把某用户设为达人
63-
router.post('/user/cancel_star', adminRequired, user.toggleStar); // 取消某用户的达人身份
64-
router.post('/user/:name/block', adminRequired, user.block); // 禁言某用户
65-
router.post('/user/:name/delete_all', adminRequired, user.deleteAll); // 删除某用户所有发言
66-
67-
// message controler
68-
router.get('/my/messages', userRequired, message.index); // 用户个人的所有消息页
69-
70-
// topic
71-
72-
// 新建文章界面
73-
router.get('/topic/create', userRequired, topic.create);
74-
75-
router.get('/topic/:tid', topic.index); // 显示某个话题
76-
router.post('/topic/:tid/top', adminRequired, topic.top); // 将某话题置顶
77-
router.post('/topic/:tid/good', adminRequired, topic.good); // 将某话题加精
78-
router.get('/topic/:tid/edit', userRequired, topic.showEdit); // 编辑某话题
79-
router.post('/topic/:tid/lock', adminRequired, topic.lock); // 锁定主题,不能再回复
80-
81-
router.post('/topic/:tid/delete', userRequired, topic.delete);
82-
83-
// 保存新建的文章
84-
router.post('/topic/create', userRequired, createTopicLimit, topic.put);
85-
86-
router.post('/topic/:tid/edit', userRequired, topic.update);
87-
router.post('/topic/collect', userRequired, topic.collect); // 关注某话题
88-
router.post('/topic/de_collect', userRequired, topic.de_collect); // 取消关注某话题
89-
90-
// reply controller
91-
router.post('/:topic_id/reply', userRequired,
92-
// limit.peruserperday('create_reply', config.create_reply_per_day, { showJson: false }),
93-
reply.add); // 提交一级回复
94-
router.get('/reply/:reply_id/edit', userRequired, reply.showEdit); // 修改自己的评论页
95-
router.post('/reply/:reply_id/edit', userRequired, reply.update); // 修改某评论
96-
router.post('/reply/:reply_id/delete', userRequired, reply.delete); // 删除某评论
97-
router.post('/reply/:reply_id/up', userRequired, reply.up); // 为评论点赞
98-
router.post('/upload', userRequired, topic.upload); // 上传图片
99-
// static page
100-
router.get('/about', page.about);
101-
router.get('/faq', page.faq);
102-
router.get('/getstart', page.getstart);
103-
router.get('/robots.txt', page.robots);
104-
router.get('/api', page.api);
105-
106-
// rss
107-
router.get('/rss', rss.index);
108-
109-
router.get('/search', search.index);
7+
require('./router/web')(app);
8+
require('./router/api')(app);
1109
};
11110

app/router/api.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
'use strict';
2+
3+
/**
4+
* @param {Egg.Application} app - egg application
5+
*/
6+
module.exports = app => {
7+
const { router, controller, middleware } = app;
8+
9+
const { user } = controller.api;
10+
11+
const tokenRequired = middleware.tokenRequired();
12+
// const adminRequired = middleware.adminRequired();
13+
// const createTopicLimit = middleware.createTopicLimit(config.topic);
14+
// const createUserLimit = middleware.createUserLimit(config.create_user_per_ip);
15+
16+
router.get('/api/v1/user/:loginname', user.show);
17+
router.post('/api/v1/accesstoken', tokenRequired, user.verify);
18+
};

app/router/web.js

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
'use strict';
2+
3+
/**
4+
* @param {Egg.Application} app - egg application
5+
*/
6+
module.exports = app => {
7+
const { router, controller, config, middleware } = app;
8+
9+
const { site, sign, user, topic, rss,
10+
search, page, reply, message } = controller;
11+
12+
const userRequired = middleware.userRequired();
13+
const adminRequired = middleware.adminRequired();
14+
const createTopicLimit = middleware.createTopicLimit(config.topic);
15+
const createUserLimit = middleware.createUserLimit(config.create_user_per_ip);
16+
17+
// home page
18+
router.get('/', site.index);
19+
// sitemap
20+
router.get('/sitemap.xml', site.sitemap);
21+
// mobile app download
22+
router.get('/app/download', site.appDownload);
23+
24+
// sign controller
25+
if (config.allow_sign_up) {
26+
// 跳转到注册页面
27+
router.get('/signup', sign.showSignup);
28+
// 提交注册信息
29+
router.post('/signup', createUserLimit, sign.signup);
30+
} else {
31+
// 进行github验证
32+
router.redirect('/signup', '/passport/github');
33+
}
34+
35+
const localStrategy = app.passport.authenticate('local', {
36+
successRedirect: '/',
37+
failureRedirect: '/signin',
38+
});
39+
40+
router.get('/signin', sign.showLogin); // 进入登录页面
41+
router.post('/passport/local', localStrategy);
42+
router.all('/signout', sign.signout); // 登出
43+
router.get('/active_account', sign.activeAccount); // 帐号激活
44+
45+
// github oauth
46+
app.passport.mount('github');
47+
48+
router.get('/search_pass', sign.showSearchPass); // 找回密码页面
49+
router.post('/search_pass', sign.updateSearchPass); // 更新密码
50+
router.get('/reset_pass', sign.resetPass); // 进入重置密码页面
51+
router.post('/reset_pass', sign.updatePass); // 更新密码
52+
53+
// user controller
54+
router.get('/user/:name', user.index); // 用户个人主页
55+
router.get('/setting', userRequired, user.showSetting); // 用户个人设置页
56+
router.post('/setting', userRequired, user.setting); // 提交个人信息设置
57+
router.get('/stars', user.listStars); // 显示所有达人列表页
58+
router.get('/users/top100', user.top100); // 显示积分前一百用户页
59+
router.get('/user/:name/collections', user.listCollectedTopics); // 用户收藏的所有话题页
60+
router.get('/user/:name/topics', user.listTopics); // 用户发布的所有话题页
61+
router.get('/user/:name/replies', user.listReplies); // 用户参与的所有回复页
62+
router.post('/user/set_star', adminRequired, user.toggleStar); // 把某用户设为达人
63+
router.post('/user/cancel_star', adminRequired, user.toggleStar); // 取消某用户的达人身份
64+
router.post('/user/:name/block', adminRequired, user.block); // 禁言某用户
65+
router.post('/user/:name/delete_all', adminRequired, user.deleteAll); // 删除某用户所有发言
66+
67+
// message controler
68+
router.get('/my/messages', userRequired, message.index); // 用户个人的所有消息页
69+
70+
// topic
71+
72+
// 新建文章界面
73+
router.get('/topic/create', userRequired, topic.create);
74+
75+
router.get('/topic/:tid', topic.index); // 显示某个话题
76+
router.post('/topic/:tid/top', adminRequired, topic.top); // 将某话题置顶
77+
router.post('/topic/:tid/good', adminRequired, topic.good); // 将某话题加精
78+
router.get('/topic/:tid/edit', userRequired, topic.showEdit); // 编辑某话题
79+
router.post('/topic/:tid/lock', adminRequired, topic.lock); // 锁定主题,不能再回复
80+
81+
router.post('/topic/:tid/delete', userRequired, topic.delete);
82+
83+
// 保存新建的文章
84+
router.post('/topic/create', userRequired, createTopicLimit, topic.put);
85+
86+
router.post('/topic/:tid/edit', userRequired, topic.update);
87+
router.post('/topic/collect', userRequired, topic.collect); // 关注某话题
88+
router.post('/topic/de_collect', userRequired, topic.de_collect); // 取消关注某话题
89+
90+
// reply controller
91+
router.post('/:topic_id/reply', userRequired,
92+
// limit.peruserperday('create_reply', config.create_reply_per_day, { showJson: false }),
93+
reply.add); // 提交一级回复
94+
router.get('/reply/:reply_id/edit', userRequired, reply.showEdit); // 修改自己的评论页
95+
router.post('/reply/:reply_id/edit', userRequired, reply.update); // 修改某评论
96+
router.post('/reply/:reply_id/delete', userRequired, reply.delete); // 删除某评论
97+
router.post('/reply/:reply_id/up', userRequired, reply.up); // 为评论点赞
98+
router.post('/upload', userRequired, topic.upload); // 上传图片
99+
// static page
100+
router.get('/about', page.about);
101+
router.get('/faq', page.faq);
102+
router.get('/getstart', page.getstart);
103+
router.get('/robots.txt', page.robots);
104+
router.get('/api', page.api);
105+
106+
// rss
107+
router.get('/rss', rss.index);
108+
109+
router.get('/search', search.index);
110+
};

app/service/user.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,16 @@ class UserService extends Service {
3939
return this.ctx.model.User.findOne(query).exec();
4040
}
4141

42+
/*
43+
* 根据 token 查找用户
44+
* @param {String} token
45+
* @return {Promise[user]} 承载用户的 Promise 对象
46+
*/
47+
getUserByToken(accessToken) {
48+
const query = { accessToken };
49+
return this.ctx.model.User.findOne(query).exec();
50+
}
51+
4252
/*
4353
* 根据用户ID,查找用户
4454
* @param {String} id 用户ID

config/config.default.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,5 +160,11 @@ module.exports = appInfo => {
160160

161161
config.search = 'google'; // 'google', 'baidu', 'local'
162162

163+
config.security = {
164+
csrf: {
165+
ignore: '/api/*/*',
166+
},
167+
};
168+
163169
return config;
164170
};
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
'use strict';
2+
3+
const { app, assert } = require('egg-mock/bootstrap');
4+
5+
describe('test/app/controller/api/user.test.js', () => {
6+
let loginname;
7+
let token;
8+
9+
before(async function() {
10+
const ctx = app.mockContext();
11+
loginname = `user_loginname_${Date.now()}`;
12+
const email = `${loginname}@test.com`;
13+
const user = await ctx.service.user.newAndSave('name', loginname, ctx.helper.bhash('pass'), email, 'avatar_url', 'active');
14+
token = user.accessToken;
15+
assert(user.loginname === loginname);
16+
});
17+
18+
it('get /user/:loginname should ok', async () => {
19+
await app.httpRequest().get(`/api/v1/user/${loginname}`).expect(200);
20+
await app.httpRequest().get(`/api/v1/user/${loginname}test`).expect(404);
21+
});
22+
23+
it('post /accesstoken should ok', async () => {
24+
await app.httpRequest()
25+
.post('/api/v1/accesstoken')
26+
.send({ accesstoken: token })
27+
.expect(200);
28+
await app.httpRequest()
29+
.post('/api/v1/accesstoken')
30+
.expect(401);
31+
});
32+
});

0 commit comments

Comments
 (0)