diff --git a/.gitignore b/.gitignore index 251dde1..15c460a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ .idea .svn node_modules -config.js assets.json logs *.log @@ -10,4 +9,8 @@ pids *.pid *.seed lib-cov -coverage \ No newline at end of file +coverage +public/**/*.debug.css +public/**/*.min.css +public/**/*.debug.js +public/**/*.min.js \ No newline at end of file diff --git a/Makefile b/Makefile deleted file mode 100644 index 1a499c3..0000000 --- a/Makefile +++ /dev/null @@ -1,47 +0,0 @@ -TESTS = $(shell find test -type f -name "*.test.js") -TEST_TIMEOUT = 10000 -MOCHA_REPORTER = spec -NPM_REGISTRY = "" - -preinstall: - @if ! test -f config.js; then \ - cp config.default.js config.js; \ - fi - @if ! test -d public/upload; then \ - mkdir public/upload; \ - fi - -install: preinstall - @npm install $(NPM_REGISTRY) - -test: install preinstall build - @NODE_ENV=test ./node_modules/mocha/bin/mocha \ - --reporter $(MOCHA_REPORTER) \ - -r should \ - -r test/env \ - --timeout $(TEST_TIMEOUT) \ - $(TESTS) - -test-cov cov: install preinstall build - @NODE_ENV=test node \ - node_modules/.bin/istanbul cover --preserve-comments \ - ./node_modules/.bin/_mocha \ - -- \ - -r should \ - -r test/env \ - --reporter $(MOCHA_REPORTER) \ - --timeout $(TEST_TIMEOUT) \ - $(TESTS) - -build: - @./node_modules/.bin/loader views . - -start: install build - @nohup ./node_modules/.bin/pm2 start app.js --name "gaoqiblog" -i max --node-args="--max-old-space-size=300" >> gaoqi-blog.log 2>&1 & - -restart: install build - @nohup ./node_modules/.bin/pm2 restart "gaoqiblog" >> gaoqi-blog.log 2>&1 & - -reboot: - @nohup ./node_modules/.bin/pm2 restart "gaoqiblog" >> gaoqi-blog.log 2>&1 & -.PHONY: install build start restart reboot \ No newline at end of file diff --git a/README.md b/README.md index 21a4ac6..e5749fb 100644 --- a/README.md +++ b/README.md @@ -26,32 +26,31 @@ gaoqi-blog # 安装运行 * 安装上面所需的3个环境 -* 安装前置 - * linux - * `make preinstall` - * `npm run prebuild` - * windows - * `npm run prebuild` -* `make install` 把依赖安装上 -* 如果`config.js`中debug 未设置为true,则需要`make build`(Windows 可使用`npm run build`来构建),压缩合并一下js、css文件 -* 使用`node app.js`运行,推荐`pm2`管理应用进程,安装后使用`make start` -* `make restart`重新编译重启,`make reboot`直接重启 -* 浏览`http://localhost:3001` - -# 测试 +* 安装依赖 `npm install` +* js、css压缩合并 `npm run build` +* 开发环境启动 `npm run dev` +* 生产环境启动 `npm start` +* 生产环境重启 `npm restart` +* 生产环境快速重启 `make reboot` +* 访问 `http://localhost:3001` + +# test * `npm test` -* `make test` -# 覆盖 +# coverage * `npm run test-cov` -* `make test-cov` -# 版本日志 +# changelog + +## 1.0.1 / 2016-10-25 +* 重构config files +* 更新readme +* 移除`Makefile`,全部使用`npm scripts`代替 -## 0.0.11 / 2016-8-13 -* 界面改版 +## 1.0.0 / 2016-10-21 +* 界面重构 ## 0.0.10 / 2016-7-16 * 升级依赖 diff --git a/app.js b/app.js index 7c85a17..5188efc 100644 --- a/app.js +++ b/app.js @@ -4,10 +4,6 @@ var config = require('./config'); -if (!config.debug && config.oneapm_key !== 'your oneapm key') { - require('oneapm'); -} - var path = require('path'); var Loader = require('loader'); var LoaderConnect = require('loader-connect') @@ -39,7 +35,7 @@ var app = express(); // 静态文件目录 var staticDir = path.join(__dirname, 'public'); var assets = {}; -if (config.mini_assets) { +if (process.env.NODE_ENV === 'production') { try { assets = require('./assets.json'); } catch (e) { diff --git a/bin/pre_install.js b/bin/pre_install.js index 2cf4b77..9d72296 100644 --- a/bin/pre_install.js +++ b/bin/pre_install.js @@ -5,31 +5,9 @@ var fs = require("fs"); var util = require("util"); -var cp = require("../util/cp"); -var configFileName = "config.js"; -var configDefaultFileName = "config.default.js"; var uploadDirPath = "public/upload"; -console.log(util.format("checking \x1b[36m %s \x1b[0m file.", configFileName)); - -//配置文件configFileName不存在的情况下,根据配置文件模板configDefaultFileName复制一份新的 -fs.access(configFileName, fs.F_OK, function(err) { - - if (err) { - - cp(configDefaultFileName, configFileName, function(err) { - if (err) { - console.log(err); - } else { - console.log(util.format("successfully creat file \x1b[36m %s \x1b[0m .", configFileName)); - } - }); - } else { - console.log(util.format("file \x1b[36m %s \x1b[0m exists.", configFileName)); - } -}); - console.log(util.format("checking \x1b[36m %s \x1b[0m directory.", uploadDirPath)); fs.access(uploadDirPath, fs.F_OK, function(err) { if (err) { diff --git a/common/cutter.js b/common/cutter.js index b056b3c..af2a423 100644 --- a/common/cutter.js +++ b/common/cutter.js @@ -17,13 +17,12 @@ exports.shorter = function (content, length) { var t = 0; for (var i = 0; i < length; i++) { if (cache.substr(i, 1).match("[\u4e00-\u9fa5]")) { + if (t + 2 > length) break; t = t + 2;//汉字 } else { + if (t + 1 > length) break; t = t + 1;//英文 } - if (t > length) { - break; - } } var result = cache.substring(0, t); if (len > length) { @@ -37,5 +36,5 @@ exports.shorter = function (content, length) { * @param {String} content 需要被清除的内容 */ exports.clearHtml = function (content) { - -}; + return content; +}; \ No newline at end of file diff --git a/common/tools.js b/common/tools.js index 686afbf..3ff48d1 100644 --- a/common/tools.js +++ b/common/tools.js @@ -47,7 +47,7 @@ exports.bhash = function (str) { }; -exports.bcompare = function (str, hash, callback) { +exports.bcompare = function (str, hash) { return new Promise(function (resolve, reject) { bcrypt.compare(str, hash, function(err, result) { if (err) return reject(err); diff --git a/config.default.js b/config.default.js deleted file mode 100644 index 6a6b462..0000000 --- a/config.default.js +++ /dev/null @@ -1,122 +0,0 @@ -/** - * config - */ - -var path = require('path'); - -var config = { - // debug 为 true 时,用于本地调试 - debug: false, - - get mini_assets() { - return !this.debug; - }, // 是否启用静态文件的合并压缩,详见视图中的Loader - - title: '首页 - 搞起博客 ', - name: '搞起博客', // 社区名字 - description: '搞起博客是分享、讨论、交流技术或者个人体会、经验的博客平台。', // 社区的描述 - keywords: '', - - site_logo: '/public/img/gaoqi_blog_logo.svg', // default is `name` - site_icon: '/public/favicon.ico', // 默认没有 favicon, 这里填写网址 - - // cdn host,如 http://static.gaoqixhb.com - site_static_host: 'http://static.gaoqixhb.com', // 静态文件存储域名 - - // 社区的域名 - host: 'localhost', - // 默认的Google tracker ID,自有站点请修改,申请地址:http://www.google.com/analytics/ - google_tracker_id: '', - // 默认的cnzz tracker ID,自有站点请修改,代码部分嵌入了百度统计,可自行修改 - cnzz_tracker_id: '', - - // mongodb 配置 - db: 'mongodb://127.0.0.1/gaoqi_blog', - - redis_host: '127.0.0.1', - redis_port: 6379, - redis_db: 0, - - session_secret: 'gaoqi_blog_secret', // 务必修改 - auth_cookie_name: 'gaoqi_blog', - - // 程序运行的端口 - port: 3001, - - // 话题列表显示的话题数量 - list_topic_count: 20, - //热门文章显示数量 - list_hot_topic_count: 10, - //热门标签显示的数量 - list_hot_tag_count: 10, - //显示最新评论条数 - list_latest_replies_count: 10, - - // 限制发帖时间间隔,单位:毫秒 - post_interval: 2000, - - // RSS配置 - rss: { - title: '关注前后端技术 - 搞起博客 gaoqixhb.com', - link: 'http://blog.gaoqixhb.com', - language: 'zh-cn', - description: '搞起博客是分享、讨论、交流前后端技术或者个人体会、经验的博客平台。', - //最多获取的RSS Item数量 - max_rss_items: 30 - }, - - // 邮箱配置 - mail_opts: { - host: 'smtp.gaoqixhb.com', - port: 25, - auth: { - user: 'system@gaoqixhb.com', - pass: 'xxxx' - } - }, - - //weibo app key - weibo_key: 10000000, - weibo_id: '', - - // admin 可删除话题,编辑标签,设某人为达人,管理员 - admins: { luoyjx: true }, - - // github 登陆的配置,需要到github配置授权 - GITHUB_OAUTH: { - clientID: 'your client id', - clientSecret: 'your client secret', - callbackURL: '' //回调的地址 如 http://blog.gaoqixhb.com/login/github/callback - }, - // 是否允许直接注册(否则只能走 github 的方式) - allow_sign_up: true, - - // oneapm 是个用来监控网站性能的服务 - oneapm_key: 'your oneapm key', - - //7牛的access信息,用于文件上传 - qn_access: { - accessKey: 'your access key', - secretKey: 'your secret key', - bucket: '', //空间名 如 gaoqixhb - origin: '' //静态域名 如 gaoqixhb.qiniudn.com - }, - - //文件上传配置 - //注:如果填写 qn_access,则会上传到 7牛,以下配置无效 - upload: { - path: path.join(__dirname, 'public/upload/'), - url: '/public/upload/' - }, - - // 分类 - tabs: [ - ['program', '开发', '#5cb85c'], - ['share', '分享', '#5cb85c'], - ['chat', '闲聊', '#5cb85c'], - ['push', '推送', '#5cb85c'] - ] - -}; - -module.exports = config; diff --git a/config/index.js b/config/index.js new file mode 100644 index 0000000..5043c06 --- /dev/null +++ b/config/index.js @@ -0,0 +1,11 @@ +/** + * config index + * @authors yanjixiong + * @date 2016-10-25 09:23:33 + */ + +var env = process.env.NODE_ENV === 'production' ? 'production' : 'development'; + +console.log('load %s config', env); + +module.exports = require('./env/' + env); \ No newline at end of file diff --git a/controllers/post.js b/controllers/post.js index ec27a04..b29bb05 100644 --- a/controllers/post.js +++ b/controllers/post.js @@ -134,7 +134,7 @@ exports.create = function(req, res, next) { }) .then(function(userFind) { userFind.score += 5; - userFind.topic_count += 1; + userFind.post_count += 1; userFind.save(); req.session.user = userFind; //发送at消息 diff --git a/controllers/reply.js b/controllers/reply.js index 3d990bb..8943e49 100644 --- a/controllers/reply.js +++ b/controllers/reply.js @@ -51,6 +51,8 @@ exports.add = function (req, res, next) { at.sendMessageToMentionUsers(newContent, post_id, req.session.user._id, reply._id, userFind.login_name, _post.title); _post.reply_count += 1; _post.update_at = new Date(); + _post.last_reply_at = new Date(); + _post.last_reply = req.session.user._id; _post.save(); return Promise.resolve(reply); }) diff --git a/controllers/sign.js b/controllers/sign.js index 7fb4f11..54b7e64 100644 --- a/controllers/sign.js +++ b/controllers/sign.js @@ -151,7 +151,7 @@ exports.signup = function (req, res, next) { return tools.bhash(pass); }) .then(function(passhash) { - var avatarUrl = User.makeGravatar(); + var avatarUrl = User.makeGravatar(email); User .newAndSave(loginname, loginname, passhash, email, avatarUrl, false) .then(function() { diff --git a/dao/user.js b/dao/user.js index cacfe85..4790c3a 100644 --- a/dao/user.js +++ b/dao/user.js @@ -1,6 +1,10 @@ /*! * user dao */ +var url = require('url'); +var qn = require('qn'); +var gravatar = require('gravatar'); +var request = require('request'); var Promise = require('bluebird'); var models = require('../models'); var User = models.User; @@ -8,6 +12,8 @@ var utility = require('utility'); var uuid = require('node-uuid'); var config = require('../config'); +var qnClient = qn.create(config.qn_avatar_access); + /** * 根据多个用户名查询多个用户信息 @@ -116,6 +122,18 @@ exports.newAndSave = function (name, login_name, pass, email, avatar_url, active * @param {String} email 邮箱 * @returns {string} avatar地址 */ -exports.makeGravatar = function () { - return config.site_static_host + '/avatar/default.png'; +exports.makeGravatar = function (email) { + var urlObj = url.parse(gravatar.url(email, {d: 'retro'})) + var avatarUrl = urlObj.path; + var avatarPath = avatarUrl.replace('//www.gravatar.com', ''); + var avatarKey = avatarPath.split('?')[0]; + console.log(avatarUrl) + console.log(avatarKey) + + qnClient.upload(request('http:' + avatarUrl), {key: avatarKey}, function(err, result) { + if (err) return console.error(err); + console.log(result); + }) + + return config.avatar_static_host + avatarKey; }; diff --git a/models/post.js b/models/post.js index 18d7cc4..96f5250 100644 --- a/models/post.js +++ b/models/post.js @@ -49,17 +49,4 @@ PostSchema.virtual('categoryName').get(function () { } }); -PostSchema.virtual('categoryColor').get(function () { - var tab = this.category; - var pair = _.find(config.tabs, function (_pair) { - return _pair[0] === tab; - }); - - if (pair) { - return pair[2]; - } else { - return ''; - } -}); - mongoose.model('Post', PostSchema); \ No newline at end of file diff --git a/oneapm.js b/oneapm.js deleted file mode 100644 index f18cba4..0000000 --- a/oneapm.js +++ /dev/null @@ -1,29 +0,0 @@ -var config = require('./config'); - -/** - * OneAPM agent configuration. - * - * See lib/config.defaults.js in the agent distribution for a more complete - * description of configuration variables and their potential values. - */ -exports.config = { - /** - * Array of application names. - */ - app_name : [config.name], - /** - * Your OneAPM license key. - */ - license_key : config.oneapm_key, - logging : { - /** - * Level at which to log. 'trace' is most useful to OneAPM when diagnosing - * issues with the agent, 'info' and higher will impose the least overhead on - * production applications. - */ - level : 'info' - }, - transaction_events: { - enabled: true - } -}; diff --git a/package.json b/package.json index 1a9b6fa..cca8d42 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gaoqi-blog", - "version": "0.0.11", + "version": "1.0.1", "private": true, "main": "app.js", "description": "A Node.js blog using MongoDB", @@ -12,15 +12,24 @@ "blog.gaoqixhb.com" ], "scripts": { - "preinstall": "node bin/pre_install.js", "dev": "node app.js", - "prestart": "npm run build", - "start": "./node_modules/.bin/pm2 start app.js --name 'gaoqiblog' -i max --node-args='--max-old-space-size=300' >> gaoqi-blog.log 2>&1", - "prebuild": "npm install", + + "preinstall": "node bin/pre_install.js", + "build": "./node_modules/.bin/loader views .", - "pretest": "npm run build", + + "prestart": "npm install && npm run build", + "start": "cross-env NODE_ENV=production ./node_modules/.bin/pm2 start app.js --name 'gaoqiblog' -i max --node-args='--max-old-space-size=300'", + + "prerestart": "npm install && npm run build", + "restart": "cross-env NODE_ENV=production ./node_modules/.bin/pm2 restart gaoqiblog", + + "reboot": "cross-env NODE_ENV=production ./node_modules/.bin/pm2 restart gaoqiblog", + + "pretest": "npm install", "test": "mocha --reporter spec -r should -r test/env --timeout 10000 ./test/**/*.test.js", - "pretest-cov": "npm run build", + + "pretest-cov": "npm install", "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- -r should -r test/env --timeout 10000 ./test/**/*.test.js -R spec" }, "engines": { @@ -35,12 +44,13 @@ "connect-redis": "^3.1.0", "cookie-parser": "^1.4.3", "cors": "^2.7.1", - "cp": "^0.2.0", + "cross-env": "^3.1.3", "csurf": "^1.9.0", "data2xml": "^1.2.5", "ejs-mate": "^2.3.0", "express": "^4.14.0", "express-session": "^1.14.0", + "gravatar": "^1.5.2", "html2markdown": "*", "ioredis": "^2.2.0", "loader": "^2.1.1", @@ -55,12 +65,12 @@ "node-uuid": "^1.4.7", "nodemailer": "^2.5.0", "nodemailer-smtp-transport": "^2.6.0", - "oneapm": "^1.2.20", "passport": "^0.3.2", "passport-github": "^1.1.0", "pm2": "^2.0.12", "qn": "^1.3.0", "ready": "^0.1.1", + "request": "^2.75.0", "response-time": "^2.3.1", "utility": "^1.8.0", "validator": "^6.0.0", diff --git a/public/css/base.less b/public/css/base.less index 1499564..8784422 100644 --- a/public/css/base.less +++ b/public/css/base.less @@ -1,5 +1,174 @@ +a { + color: #778087; + &:hover { + color: #4d5256; + } +} -.box { - background-color: #fff; +button, input, textarea, select { + &:focus { + outline: none; + } +} + +/* 按钮 */ + +.btn { + border: 0!important; + border-color: transparent!important; + box-shadow: none!important; + border-radius: 3px; +} + +/* 标徽 */ + +.badge { + float: right; + margin-right: 15px; + margin-top: 13px; + padding: 1px 7px; +} + +/* 标签 */ + +.tag-box { + display: inline-block; + margin-bottom: 10px; + margin-right: 5px; + .tag { + text-decoration: none; + padding: 2px 6px; + border: 1px solid #efefef; border-radius: 3px; + &:hover { + border-color: #ccc; + } + } +} + +/* 卡片 */ + +.panel { + border: 0; + .panel-heading { + border-bottom: 1px solid #f0f0f0; + background-color: #ffffff; + font-size: 13px; + padding: 10px; + .breadcrumb { + background-color: transparent; + margin-bottom: 0; + padding: 0; + } + } + .panel-body { + padding: 10px; + ul.posts { + word-break: break-all; + li { + margin-bottom: 10px; + div { + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + } + &:last-child { + margin-bottom: 0; + } + } + } + } +} + +/* 框架 */ + +.box { + background-color: #fff; + border-radius: 3px; +} + +.cell { + padding: 10px; + font-size: 12px; + line-height: 120%; + text-align: left; + border-bottom: 1px solid #f0f0f0; + &:last-child { + border-bottom: 0; + } +} + +/* 头像 */ + +.avatar { + width: 40px; + height: 40px; + border-radius: 3px; +} + +.avatar-sm { + width: 30px; + height: 30px; + border-radius: 3px; +} + +.avatar-lg { + width: 70px; + height: 70px; + border-radius: 3px; +} + +/* margin 和 padding */ + +.mr-5 { + margin-right: 5px; +} + +.mt-10 { + margin-top: 10px; +} + +.mt-0 { + margin-top: 0; +} + +.mb-0 { + margin-bottom: 0; +} + +.mb-10 { + margin-bottom: 10px +} + +.pr-5 { + padding-right: 5px; +} + +@media (max-width: 768px) { + .pr-5 { + padding-right: 15px; + } +} + +/* 字体大小 */ + +.f-12 { + font-size: 12px; +} + +/* 浮动 */ + +.fr { + float: right; + text-align: right; +} + +.fl { + float: left; + text-align: left; +} + +/* 颜色 */ +.color-hot { + color: #f2354f; } \ No newline at end of file diff --git a/public/css/body.less b/public/css/body.less new file mode 100644 index 0000000..35a3b64 --- /dev/null +++ b/public/css/body.less @@ -0,0 +1,12 @@ +/* 中间内容样式 */ +.wrap { + background-color: #e0e0e0; + padding: 20px 0 0 0; + .container { + .row { + article { + margin-bottom: 20px; + } + } + } +} \ No newline at end of file diff --git a/public/css/footer.less b/public/css/footer.less new file mode 100644 index 0000000..efcfb22 --- /dev/null +++ b/public/css/footer.less @@ -0,0 +1,34 @@ +/* 页脚 */ +.footer { + height: auto; + background-color: #ffffff; + font-size:12px; + padding: 30px 0 30px 0; + .container { + padding-top: 15px; + padding-left: 15px; + padding-right: 15px; + color: #555; + .row { + .text-muted { + margin: 20px 0; + } + .list-info { + list-style: none; + line-height: 20px; + li { + margin-bottom: 5px; + } + } + li.li-header { + font-weight: bold; + font-size: 14px; + margin-bottom: 20px; + display: block; + } + .env-info{ + vertical-align: bottom; + } + } + } +} \ No newline at end of file diff --git a/public/css/header.less b/public/css/header.less new file mode 100644 index 0000000..fb447ec --- /dev/null +++ b/public/css/header.less @@ -0,0 +1,16 @@ +.navbar { + margin-bottom: 0; + font-size: 13px; + + .logo { + width: 90px; + display: block; + line-height: 50px; + } + + @media (max-width: 768px) { + .logo { + margin-left: 15px; + } + } +} \ No newline at end of file diff --git a/public/css/page/post.detail.less b/public/css/page/post.detail.less new file mode 100644 index 0000000..8b19050 --- /dev/null +++ b/public/css/page/post.detail.less @@ -0,0 +1,7 @@ +.CodeMirror { + height: 200px!important; +} + +.reply-content { + margin-left: 45px; +} \ No newline at end of file diff --git a/public/css/page/post.edit.less b/public/css/page/post.edit.less new file mode 100644 index 0000000..8464202 --- /dev/null +++ b/public/css/page/post.edit.less @@ -0,0 +1,61 @@ +select { + display: inline-block; + height: 30px; + line-height: 30px; + padding: 4px 6px; + margin-bottom: 10px; + font-size: 14px; + line-height: 20px; + color: #555; + vertical-align: middle; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; + width: 220px; + border: 1px solid #ccc; +} + +.tagText { + border-radius: 4px; + width: 150px; + margin: 0 0 10px 0; +} + +/* tag 自动提示样式 start */ +.ac_results { + background-color: #ffffff; + border: 1px solid rgba(0,0,0,0.15); + border-radius: 3px; + padding: 5px 0; + margin: 2px 0 0; + font-size: 14px; + text-align: left; + z-index: 100; + ul { + list-style: none; + padding: 0; + li { + padding: 2px 5px; + } + } +} + +.overflow-hidden-x { + overflow-x: hidden; +} + +.ac_over { + color: #ffffff; + background-color: #3498DB; +} + +/* tag 自动提示样式 end */ + +.input-tags { + .form-group { + display: inline-block; + width: 150px; + margin-right: 5px; + margin-bottom: 5px; + } +} \ No newline at end of file diff --git a/public/css/page/site.index.less b/public/css/page/site.index.less new file mode 100644 index 0000000..0d37ce3 --- /dev/null +++ b/public/css/page/site.index.less @@ -0,0 +1,9 @@ +ul.avatars { + margin: 0; + padding: 0; + font-size: 0; + li { + margin: 0 7px 10px 10px; + padding: 0; + } +} \ No newline at end of file diff --git a/public/css/tabs.less b/public/css/tabs.less index 08fc3d9..d4b90fa 100644 --- a/public/css/tabs.less +++ b/public/css/tabs.less @@ -4,13 +4,14 @@ padding: 10px; border-bottom: 1px solid #f0f0f0; .tab { + text-decoration: none; margin: 0 5px 0 0; padding: 5px 8px 5px 8px; display: inline-block; + color: #555555; &:hover { background-color: #f3f3f3; border-radius: 3px; - color: #555555; } } .tab.active { diff --git a/public/css/variables.less b/public/css/variables.less new file mode 100644 index 0000000..b9a99f9 --- /dev/null +++ b/public/css/variables.less @@ -0,0 +1,14 @@ +@length-5 : 5px; +@length-10 : 10px; +@length-14 : 14px; +@length-15 : 15px; +@length-20 : 20px; +@white : #ffffff; +@blue : #3498DB; +@font-default-color : #555; +@addtion-default-color : #999; +@lighter-gray: #ccc; +@gray: #b2bac2; +@divider-line : #eeeeee; +@light-gray-backgroud : #e9e9e9; +@tag-normal-backgroud : #DDFFFF; \ No newline at end of file diff --git a/public/css/widget/markdown.less b/public/css/widget/markdown.less new file mode 100644 index 0000000..3e5ea2a --- /dev/null +++ b/public/css/widget/markdown.less @@ -0,0 +1,254 @@ +pre { + background: #fee9cc; + border: 1px dashed #ccc; + line-height: 22px; +} + + +div pre.prettyprint { + font-size: 14px; + border-radius: 0px; + padding: 10px; + border: none; + margin: 20px 0; + border-width: 1px 0px; + background: #efefef; + max-height: 600px; +} + +/* markdown editor */ +.markdown-text { + .h1, h1 { + color: #555; + margin: 20px 0 10px; + padding-bottom: 8px; + font-size: 22px; + font-weight: 700; + border-bottom: 2px solid #eeeeee; + } + + .h2, h2 { + color: #555; + margin: 20px 0 10px; + font-size: 18px; + padding-bottom: 5px; + font-weight: 700; + } + + .h3, h3 { + color: #555; + margin: 16px 0 10px; + font-size: 15px; + font-weight: 700; + } + + .h4, h4 { + color: #555; + font-size: 14px; + font-weight: 700; + } + + blockquote { + font-size: 14px; + p { + margin-top: 0; + } + } + + p { + white-space: pre-wrap; /* CSS3 */ + white-space: -moz-pre-wrap; /* Mozilla, since 1999 */ + white-space: -pre-wrap; /* Opera 4-6 */ + white-space: -o-pre-wrap; /* Opera 7 */ + word-wrap: break-word; /* Internet Explorer 5.5+ */ + margin: 1em 0; + line-height: 180%; + font-size: 16px; + } + a { + color: #3498DB; + &:hover { + + } + } + img { + max-width: 100%; + position: static !important; + } + + li { + font-size: 14px; + line-height: 2em; + } + + > *:first-child, .preview > *:first-child { + margin-top: 0; + } + + > *:last-child, .preview > *:last-child { + margin-bottom: 1em; + } + +} + +.article-content table, +.replies table { + padding: 0; + border-collapse: collapse; + border-spacing: 0; + font-size: 100%; + font: inherit; +} + +.article-content table tr, +.replies table tr { + border-top: 1px solid #ccc; + background-color: #fff; + margin: 0; + padding: 0; +} + +.article-content table tr:nth-child(2n), +.replies table tr:nth-child(2n) { + background-color: #f8f8f8; +} + +.article-content table tr th, +.replies table tr th { + font-weight: bold; +} + +.article-content table tr th, +.article-content table tr td, +.replies table tr th, +.replies table tr td { + border: 1px solid #ccc; + text-align: left; + margin: 0; + padding: 6px 13px; +} + +.article-content table tr th > :first-child, +.article-content table tr td > :first-child, +.replies table tr th > :first-child, +.replies table tr td > :first-child { + margin-top: 0; +} + +.article-content table tr th > :last-child, +.article-content table tr td > :last-child, +.replies table tr th > :last-child, +.replies table tr td > :last-child { + margin-bottom: 0; +} + +.preview { + padding: 0.5em; + font-size: 15px; + min-height: 200px; + word-break: break-all; + + .h1, h1 { + color: #555; + margin: 20px 0 10px; + padding-bottom: 8px; + font-size: 22px; + font-weight: 700; + border-bottom: 2px solid #eeeeee; + } + + .h2, h2 { + color: #555; + margin: 20px 0 10px; + font-size: 18px; + padding-bottom: 5px; + font-weight: 700; + } + + .h3, h3 { + color: #555; + margin: 16px 0 10px; + font-size: 15px; + font-weight: 700; + } + + .h4, h4 { + color: #555; + font-size: 14px; + font-weight: 700; + } + + blockquote { + font-size: 14px; + p { + margin-top: 0; + } + } + + p { + white-space: pre-wrap; /* CSS3 */ + white-space: -moz-pre-wrap; /* Mozilla, since 1999 */ + white-space: -pre-wrap; /* Opera 4-6 */ + white-space: -o-pre-wrap; /* Opera 7 */ + word-wrap: break-word; /* Internet Explorer 5.5+ */ + margin: 1em 0; + line-height: 180%; + } + a { + color: #3498DB; + &:hover { + + } + } + + p { + white-space: pre-wrap; /* CSS3 */ + white-space: -moz-pre-wrap; /* Mozilla, since 1999 */ + white-space: -pre-wrap; /* Opera 4-6 */ + white-space: -o-pre-wrap; /* Opera 7 */ + word-wrap: break-word; /* Internet Explorer 5.5+ */ + margin: 1em 0; + + > img { + display: block; + max-width: 100%; + box-shadow: 0px 0px 4px rgba(0, 0, 0, 0.6); + } + } + + li { + font-size: 14px; + line-height: 2em; + } +} + +.markdown_editor .markdown_in_preview, +.markdown_editor .markdown_in_editor { + display: none; +} + +.markdown_editor.in_preview .markdown_in_preview { + display: block; +} + +.markdown_editor.in_editor .markdown_in_editor { + display: block; + border-radius: 4px; + padding: 0 5px; +} + +textarea#title { + width: 98%; + border: none; + margin-bottom: 1em; + resize: none; + height: 20px; +} + +.editor_buttons { + margin-top: 10px; +} + +.editor_buttons > button { + vertical-align: baseline; +} \ No newline at end of file diff --git a/public/css/widget/post.list.less b/public/css/widget/post.list.less new file mode 100644 index 0000000..1bcc01f --- /dev/null +++ b/public/css/widget/post.list.less @@ -0,0 +1,69 @@ +.item-img { + float: left; + padding-right: 10px; +} + +.item-content { + margin-left: 50px; + .item-title { + margin-top: 0; + margin-bottom: 5px; + font-weight: 400; + font-size: 15px; + .badge-link { + .badge { + &:hover { + background-color: #555555; + } + } + } + + .badge-link:visited { + .badge { + background-color: #cccccc; + &:hover { + background-color: #555555; + } + } + } + } + + .category { + background-color: #f5f5f5; + color: #999; + cursor: pointer; + padding: 2px 4px; + font-weight: 400; + line-height: 1; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + border-radius: 3px; + text-decoration: none; + &:hover { + background-color: #e2e2e2; + color: #777; + } + } + + .item-attr { + line-height: 20px; + margin-bottom: 0; + .addtion { + color: #cccccc; + } + + .tags { + a.tag { + color: #cccccc; + &:hover { + color: #4d5256; + } + } + } + } +} + +.pagination { + margin: 10px 0 5px 10px; +} \ No newline at end of file diff --git a/public/css/widget/tag.list.less b/public/css/widget/tag.list.less new file mode 100644 index 0000000..6e5bd91 --- /dev/null +++ b/public/css/widget/tag.list.less @@ -0,0 +1,33 @@ +.row { + padding-right: 15px; + .tag-list { + padding-right: 0; + margin-bottom: 15px; + .tag-container { + position: relative; + height: 170px; + padding: 5px 0; + border-radius: 1px; + border: 1px solid #e3e3e3; + background-color: #fff; + .tag-title { + height: 21px; + margin: 0 8px; + overflow: hidden; + } + .tag-desc { + height: 98px; + margin-top: 10px; + margin: 0 8px; + overflow: hidden; + } + .tag-action { + position: absolute; + bottom: 1px; + width: 100%; + padding: 5px 8px; + background-color: #f6f6f6; + } + } + } +} \ No newline at end of file diff --git a/public/favicon.ico b/public/favicon.ico index d891aee..513e6f8 100644 Binary files a/public/favicon.ico and b/public/favicon.ico differ diff --git a/public/img/avatar/default.png b/public/img/avatar/default.png new file mode 100644 index 0000000..46d81c9 Binary files /dev/null and b/public/img/avatar/default.png differ diff --git a/public/img/gaoqi_blog_logo.png b/public/img/gaoqi_blog_logo.png new file mode 100644 index 0000000..12d4dda Binary files /dev/null and b/public/img/gaoqi_blog_logo.png differ diff --git a/public/img/gaoqi_blog_logo.svg b/public/img/gaoqi_blog_logo.svg deleted file mode 100644 index c3995a7..0000000 --- a/public/img/gaoqi_blog_logo.svg +++ /dev/null @@ -1,9 +0,0 @@ - - \ No newline at end of file diff --git a/test/common/at.test.js b/test/common/at.test.js new file mode 100644 index 0000000..9224ffa --- /dev/null +++ b/test/common/at.test.js @@ -0,0 +1,26 @@ +/** + * at test + * @authors yanjixiong + * @date 2016-10-11 11:02:10 + */ + +var should = require('should'); +var at = require('../../common/at'); + +describe('test/common/at.test.js', function() { + describe('fetchUsers()', function() { + it('should return a names array', function(done) { + var names = at.fetchUsers('@foo @bar') + names.should.be.Array; + done(); + }) + }) + + describe('linkUsers()', function() { + it('should return text contains `/u/` path ', function(done) { + var result = at.linkUsers('@foo @bar'); + result.should.containEql('/u/'); + done(); + }) + }) +}) \ No newline at end of file diff --git a/test/common/cache.test.js b/test/common/cache.test.js new file mode 100644 index 0000000..1d314e9 --- /dev/null +++ b/test/common/cache.test.js @@ -0,0 +1,17 @@ +/** + * cache test + * @authors yanjixiong + * @date 2016-10-11 10:56:59 + */ + +var cache = require('../../common/cache'); + +describe('test/common/cache.test.js', function() { + describe('set()', function() { + it('should set cache without expire be ok', function(done) { + cache.set('for', 'bar').then(function() { + done(); + }); + }) + }) +}); \ No newline at end of file diff --git a/test/common/cutter.test.js b/test/common/cutter.test.js new file mode 100644 index 0000000..9d6cb91 --- /dev/null +++ b/test/common/cutter.test.js @@ -0,0 +1,30 @@ +/** + * cutter test + * @authors yanjixiong + * @date 2016-10-11 10:30:06 + */ + +var should = require('should'); +var cutter = require('../../common/cutter'); + +describe('test/common/cutter.test.js', function() { + describe('shorter()', function() { + it('should return 10 chars with 20chars to 10chars', function(done) { + var result = cutter.shorter('abcdeabcdeabcdeabcde', 10); + result.length.should.equal(13); + done(); + }) + + it('should return 10 chinese words with 20 chinese words to 10 chinese words', function(done) { + var result = cutter.shorter('啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊', 10); + result.length.should.equal(13); + done(); + }) + + it('should return "" with "" ', function(done) { + var result = cutter.shorter('', 10); + result.length.should.equal(0); + done(); + }) + }) +}); \ No newline at end of file diff --git a/test/common/mail.test.js b/test/common/mail.test.js new file mode 100644 index 0000000..f787380 --- /dev/null +++ b/test/common/mail.test.js @@ -0,0 +1,30 @@ +/** + * mail test + * @authors yanjixiong + * @date 2016-10-11 11:44:12 + */ + +var mail = require('../../common/mail'); + +describe('test/common/mail.js', function() { + describe('sendActiveMail()', function() { + it('should sendActiveMail be ok ', function(done) { + mail.sendActiveMail('foo@bar.com', 'test_token', 'jack'); + done(); + }) + }) + + describe('sendResetPassMail()', function() { + it('should sendResetPassMail be ok ', function(done) { + mail.sendResetPassMail('foo@bar.com', 'test_token', 'jack'); + done(); + }) + }) + + describe('sendNotificationMail()', function() { + it('should sendNotificationMail be ok ', function(done) { + mail.sendNotificationMail('foo@bar.com', 'lilei', 'hanmeimei', '英语一', 'test_post_id', 'test_reply_id'); + done(); + }) + }) +}) \ No newline at end of file diff --git a/test/common/message.test.js b/test/common/message.test.js new file mode 100644 index 0000000..b1a0efc --- /dev/null +++ b/test/common/message.test.js @@ -0,0 +1,36 @@ +/** + * message test + * @authors yanjixiong + * @date 2016-10-11 11:51:50 + */ + +var should = require('should'); +var mongoose = require('mongoose'); +var ObjectId = mongoose.Schema.ObjectId; +var message = require('../../common/message'); + +describe('test/common/message.test.js', function() { + describe('sendReplyMessage()', function() { + it('should sendReplyMessage be ok', function(done) { + message + .sendReplyMessage(ObjectId(), ObjectId(), ObjectId(), ObjectId()) + .then(function(message) { + message.should.not.be.Null; + done(); + }) + .catch(done); + }) + }) + + describe('sendAtMessage()', function() { + it('should sendAtMessage be ok', function(done) { + message + .sendAtMessage(ObjectId(), ObjectId(), ObjectId(), ObjectId()) + .then(function(message) { + message.should.not.be.Null; + done(); + }) + .catch(done); + }) + }) +}) \ No newline at end of file diff --git a/test/common/tools.test.js b/test/common/tools.test.js new file mode 100644 index 0000000..e6180df --- /dev/null +++ b/test/common/tools.test.js @@ -0,0 +1,96 @@ +/** + * tools test + * @authors yanjixiong + * @date 2016-10-11 13:36:22 + */ + +var should = require('should'); +var tools = require('../../common/tools'); + +var strHashed = ''; + +describe('test/common/tools.test.js', function() { + describe('formatDate()', function() { + it('should return friendly', function(done) { + var date = new Date(); + setTimeout(function() { + var friendlyDate = tools.formatDate(date, true); + friendlyDate.should.containEql('前'); + done(); + }, 1000); + }) + + it('should return friendly', function(done) { + var date = new Date(); + setTimeout(function() { + var friendlyDate = tools.formatDate(date); + friendlyDate.should.containEql('-'); + friendlyDate.should.containEql(':'); + done(); + }, 1000); + }) + }) + + describe('formatPV()', function() { + it('should return 0 with NaN', function(done) { + var result = tools.formatPV('aaaa'); + result.should.equal(0); + done(); + }) + + it('should return contains `k` with 20000', function(done) { + var result = tools.formatPV(20000); + result.should.containEql('k'); + result.should.not.containEql('.'); + done(); + }) + + it('should return contains `k` and `.` with 2000', function(done) { + var result = tools.formatPV(2000); + result.should.containEql('k'); + result.should.containEql('.'); + done(); + }) + + it('should return origin value with 200', function(done) { + var result = tools.formatPV(200); + result.should.equal(200); + done(); + }) + }) + + describe('validateId()', function() { + it('should return true with valid id', function(done) { + var result = tools.validateId('abc123'); + result.should.be.ok; + done(); + }) + + it('should return false with invalid id', function(done) { + var result = tools.validateId('@#$%^&*('); + result.should.not.be.ok; + done(); + }) + }) + + describe('bhash()', function() { + it('should bhash be ok', function(done) { + tools + .bhash('实业误国,炒房兴邦') + .then(function(hashed) { + strHashed = hashed; + done(); + }); + }) + }) + + describe('bcompare()', function() { + it('should bcompare be ok', function(done) { + tools + .bcompare('实业误国,炒房兴邦', strHashed) + .then(function() { + done(); + }); + }) + }) +}) \ No newline at end of file diff --git a/test/controller/index.test.js b/test/controller/index.test.js index bab7a45..d28f7cc 100644 --- a/test/controller/index.test.js +++ b/test/controller/index.test.js @@ -17,6 +17,15 @@ describe('test/controller/index.test.js', function() { }); }); + it('should /?tab=program 200', function(done) { + request.get('/?tab=program').end(function(err, res) { + res.status.should.equal(200); + res.text.should.containEql('热门文章'); + res.text.should.containEql('热门标签'); + done(err); + }); + }); + it('should /?page=-1 200', function(done) { request.get('/?page=-1').end(function(err, res) { res.status.should.equal(200); diff --git a/test/util/cp.test.js b/test/util/cp.test.js deleted file mode 100644 index 55cf466..0000000 --- a/test/util/cp.test.js +++ /dev/null @@ -1,15 +0,0 @@ -var cp = require("../../util/cp"); - -console.time("async cp config.js"); -cp("config.default.js","config.test.js",function(err){ - if(err){ - console.log(err); - }else{ - console.log("successfully created"); - } -}); -console.timeEnd("async cp config.js"); -/* -console.time("sync cp config.js"); -cp.sync("config.default.js","config.js"); -console.timeEnd("sync cp config.js");*/ diff --git a/util/cp.js b/util/cp.js deleted file mode 100644 index 13986a4..0000000 --- a/util/cp.js +++ /dev/null @@ -1,59 +0,0 @@ -var fs = require("fs"), - MAX_BUFFER = 1024, - util = require("util"); - -exports = module.exports = function(src, dest,callBack) { - - var readStream = fs.createReadStream(src), - writeStream = fs.createWriteStream(dest); - - readStream.on("error",done); - writeStream.on("error",done); - writeStream.on("close",done); - readStream.pipe(writeStream); - - function done(err){ - if(err){ - clear(); - } - if(typeof callBack === "function") - callBack(err); - } - - /* - 当readStream或者writeStream有错误发生时,清理已经生成的目标文件 - */ - function clear(){ - - fs.unlink(writeStream.path,function(){ - console.log("delete the created "+ writeStream.path ); - }); - } -}; - -/** - 复制src到dest -*/ -exports.sync = function(src, dest) { - - try { - fs.accessSync(src); - } catch (e) { - throw new Error(util.format('file \x1b[36m %s \x1b[0m not exists.', src)); - } - - var buffer = new Buffer(MAX_BUFFER); - var bytesRead = MAX_BUFFER; - var pos = 0; - var read = fs.openSync(src, 'r'); - var write = fs.openSync(dest, 'w'); - - while (MAX_BUFFER == bytesRead) { - bytesRead = fs.readSync(read, buffer, 0, MAX_BUFFER, pos); - fs.writeSync(write, buffer, 0, bytesRead); - pos += bytesRead; - } - - fs.closeSync(read); - fs.closeSync(write); -}; \ No newline at end of file diff --git a/views/ads/side_aliyun_code.html b/views/ads/side_aliyun_code.html index 0bfc7b6..36a2bd5 100644 --- a/views/ads/side_aliyun_code.html +++ b/views/ads/side_aliyun_code.html @@ -1,5 +1,7 @@ -
本站部署于「阿里云 ECS」
-欢迎使用我的九折推荐码 pigtx1 购买阿里云服务(限新用户)
+本站部署于「阿里云 ECS」
+欢迎使用我的九折推荐码 pigtx1 购买阿里云服务(限新用户)
+
服务器搭建在阿里云
存储由七牛云存储提供
- 性能监控由OneAPM提供
- © 2014-2015 gaoqixhb. 鄂ICP备14001576号-1 <% if (config.cnzz_tracker_id && !config.debug) { %>
@@ -145,9 +145,18 @@ .js('/public/lib/markdownit.js') .js('/public/lib/code-prettify/prettify.js') .js('/public/js/common.js') - .done(assets, config.site_static_host, config.mini_assets) + .done(assets, config.site_assets_host, config.mini_assets) %> <%- block('script').toString() %> - +