Skip to content

Commit

Permalink
refactor: topic/create
Browse files Browse the repository at this point in the history
  • Loading branch information
forl committed Mar 14, 2018
1 parent 74600b9 commit a460ab5
Show file tree
Hide file tree
Showing 9 changed files with 112 additions and 150 deletions.
67 changes: 26 additions & 41 deletions app/controller/topic.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

const Controller = require('egg').Controller;
const _ = require('lodash');
// const validator = require('validator');
const path = require('path');
const fs = require('fs');
const awaitWriteStream = require('await-stream-ready').write;
Expand Down Expand Up @@ -102,60 +101,48 @@ class TopicController extends Controller {
async put() {
const { ctx, service } = this;
const { tabs } = this.config;
const title = ctx.request.body.title.trim();
const tab = ctx.request.body.tab.trim();
const content = ctx.request.body.t_content.trim();
const { body } = ctx.request;

// 得到所有的 tab, e.g. ['ask', 'share', ..]
const allTabs = tabs.map(function(tPair) {
return tPair[0];
});

// 验证
let editError;
if (title === '') {
editError = '标题不能是空的。';
} else if (title.length < 5 || title.length > 100) {
editError = '标题字数太多或太少。';
} else if (!tab || allTabs.indexOf(tab) === -1) {
editError = '必须选择一个版块。';
} else if (content === '') {
editError = '内容不可为空。';
}
// END 验证

if (editError) {
ctx.status = 422;
await ctx.render('topic/edit', {
edit_error: editError,
title,
content,
tabs,
});
return;
}
const allTabs = tabs.map(tPair => tPair[0]);

// 使用 egg_validate 验证
// TODO: 此处可以优化,将所有使用egg_validate的rules集中管理,避免即时新建对象
const RULE_CREATE = {
title: {
type: 'string',
max: 100,
min: 5,
},
content: {
type: 'string',
},
tab: {
type: 'enum',
values: allTabs,
},
};
ctx.validate(RULE_CREATE, ctx.request.body);

// 储存新主题帖
const topic = await service.topic.newAndSave(
title,
content,
tab,
body.title,
body.content,
body.tab,
ctx.user._id
);

// 发帖用户增加积分,增加发表主题数量
await service.user.incrementScoreAndReplyCount(topic.author_id, 5, 1);

ctx.redirect('/topic/' + topic._id);

// 通知被@的用户
await service.at.sendMessageToMentionUsers(
content,
body.content,
topic._id,
ctx.user._id
);

await ctx.redirect('/topic/' + topic._id);
ctx.redirect('/topic/' + topic._id);
}

/**
Expand Down Expand Up @@ -198,9 +185,7 @@ class TopicController extends Controller {
const { ctx, service, config } = this;

const topic_id = ctx.params.tid;
let title = ctx.request.body.title;
let tab = ctx.request.body.tab;
let content = ctx.request.body.t_content;
let { title, tab, content } = ctx.request.body;

const { topic } = await service.topic.getTopicById(topic_id);
if (!topic) {
Expand Down
64 changes: 0 additions & 64 deletions app/middleware/limit.js

This file was deleted.

29 changes: 29 additions & 0 deletions app/middleware/topic_per_day_limit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
'use strict';
const moment = require('moment');

module.exports = ({ perDayPerUserLimitCount = 10 }) => {

return async function(ctx, next) {
const { user, service } = ctx;
const YYYYMMDD = moment().format('YYYYMMDD');
const key = `topics_count_${user._id}_${YYYYMMDD}`;

let todayTopicsCount = (await service.cache.get(key)) || 0;
if (todayTopicsCount >= perDayPerUserLimitCount) {
ctx.status = 403;
await ctx.render('notify/notify',
{ error: `今天的话题发布数量已达到限制(${perDayPerUserLimitCount})` });
return;
}

await next();

if (ctx.status === 302) {
// 新建话题成功
todayTopicsCount += 1;
await service.cache.incr(key, 60 * 60 * 24);
ctx.set('X-RateLimit-Limit', perDayPerUserLimitCount);
ctx.set('X-RateLimit-Remaining', perDayPerUserLimitCount - todayTopicsCount);
}
};
};
5 changes: 2 additions & 3 deletions app/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ module.exports = app => {

const userRequired = middleware.userRequired();
const adminRequired = middleware.adminRequired();
const topicPerDayLimit = middleware.topicPerDayLimit(config.topic);

// home page
router.get('/', site.index);
Expand Down Expand Up @@ -78,9 +79,7 @@ module.exports = app => {
router.post('/topic/:tid/delete', userRequired, topic.delete);

// // 保存新建的文章
// router.post('/topic/create', userRequired, limit.peruserperday('create_topic', config.create_post_per_day, { showJson: false }), topic.put);

router.post('/topic/create', userRequired, topic.put);
router.post('/topic/create', userRequired, topicPerDayLimit, topic.put);

router.post('/topic/:tid/edit', userRequired, topic.update);
router.post('/topic/collect', userRequired, topic.collect); // 关注某话题
Expand Down
2 changes: 1 addition & 1 deletion app/view/topic/edit.html
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@

<div class='markdown_editor in_editor'>
<div class='markdown_in_editor'>
<textarea class='editor' name='t_content' rows='20'
<textarea class='editor' name='content' rows='20'
placeholder='文章支持 Markdown 语法, 请注意标记代码'
><%= typeof content !== 'undefined' && content || '' %></textarea>

Expand Down
4 changes: 4 additions & 0 deletions config/config.default.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,5 +137,9 @@ module.exports = appInfo => {
secret: process.env.EGG_ALINODE_SECRET || '',
};

config.topic = {
perDayPerUserLimitCount: 10,
};

return config;
};
5 changes: 5 additions & 0 deletions config/plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,8 @@ exports.alinode = {
package: 'egg-alinode',
env: [ 'prod' ],
};

exports.validate = {
enable: true,
package: 'egg-validate',
};
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"egg-passport-local": "^1.2.1",
"egg-redis": "^2.0.0",
"egg-scripts": "^2.5.0",
"egg-validate": "^1.0.0",
"egg-view-ejs": "^2.0.0",
"loader": "^2.1.1",
"loader-koa": "^2.0.1",
Expand Down
85 changes: 44 additions & 41 deletions test/app/controller/topic.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,51 +92,54 @@ describe('test/app/controller/topic.test.js', () => {
await app.httpRequest().get(`/topic/${topicId}/edit`).expect(200);
});

it('should POST /topic/create ok', async () => {
const body = {
title: '',
tab: '',
t_content: '',
};
it('should POST /topic/create forbidden', async () => {
app.mockCsrf();
await app.httpRequest().post('/topic/create').expect(403);
});

it('should POST /topic/create forbidden', async () => {
mockUser();
app.mockCsrf();
await app.httpRequest().post('/topic/create')
.send({
invalid_field: 'not make sense',
})
.expect(422);
});

const r1 = await app
.httpRequest()
.post('/topic/create')
.send(body);
assert(r1.text.includes('标题不能是空的。'));

body.title = 'hi';
const r2 = await app
.httpRequest()
.post('/topic/create')
.send(body);
assert(r2.text.includes('标题字数太多或太少。'));

body.title = '这是一个大标题';
const r4 = await app
.httpRequest()
.post('/topic/create')
.send(body);
assert(r4.text.includes('必须选择一个版块。'));

body.tab = 'share';
const r3 = await app
.httpRequest()
.post('/topic/create')
.send(body);
assert(r3.text.includes('内容不可为空。'));

body.t_content = 'hi';

await app
.httpRequest()
.post('/topic/create')
.send(body)
it('should POST /topic/create ok', async () => {
mockUser();
app.mockCsrf();
await app.httpRequest().post('/topic/create')
.send({
tab: 'share',
title: 'topic测试标题',
content: 'topic test topic content',
})
.expect(302);
});

it('should POST /topic/create per day limit works', async () => {
mockUser();
app.mockCsrf();
for (let i = 0; i < 9; i++) {
await app.httpRequest().post('/topic/create')
.send({
tab: 'share',
title: `topic测试标题${i + 1}`,
content: 'topic test topic content',
})
.expect(302);
}
await app.httpRequest().post('/topic/create')
.send({
tab: 'share',
title: 'topic测试标题11',
content: 'topic test topic content',
})
.expect(403);
});

it('should POST /topic/:tid/top ok', async () => {
mockUser();
const res = await app.httpRequest().post(`/topic/${topicId}/top`);
Expand Down Expand Up @@ -168,7 +171,7 @@ describe('test/app/controller/topic.test.js', () => {
const body = {
title: '',
tab: '',
t_content: '',
content: '',
};

fakeUser();
Expand Down Expand Up @@ -212,7 +215,7 @@ describe('test/app/controller/topic.test.js', () => {
.send(body);
assert(r3.text.includes('内容不可为空。'));

body.t_content = 'hi';
body.content = 'hi';
await app
.httpRequest()
.post(`/topic/${topicId}/edit`)
Expand Down

0 comments on commit a460ab5

Please sign in to comment.