├── README.md ├── api.md ├── backend ├── koa2-api │ ├── .gitignore │ ├── app.js │ ├── bin │ │ └── www │ ├── config │ │ └── db.js │ ├── controller │ │ ├── blogController.js │ │ └── userController.js │ ├── db │ │ └── mysql.js │ ├── logs │ │ ├── err.log │ │ └── out.log │ ├── middleware │ │ ├── loginCheck.js │ │ └── readme.md │ ├── model │ │ └── resModel.js │ ├── package-lock.json │ ├── package.json │ ├── pm2.config.json │ ├── public │ │ └── stylesheets │ │ │ └── style.css │ ├── routes │ │ ├── blog.js │ │ ├── index.js │ │ └── user.js │ ├── utils │ │ ├── cryps.js │ │ └── data.js │ └── views │ │ ├── error.pug │ │ ├── index.pug │ │ └── layout.pug └── node │ ├── .gitignore │ ├── app.js │ ├── bin │ └── www │ ├── config │ └── db.js │ ├── controller │ ├── blogController.js │ └── userController.js │ ├── db │ ├── mysql.js │ └── redis.js │ ├── middleware │ ├── loginCheck.js │ └── readme.md │ ├── model │ └── resModel.js │ ├── package-lock.json │ ├── package.json │ ├── routes │ ├── blog.js │ └── user.js │ └── utils │ ├── cryps.js │ └── data.js ├── config.md └── frontend ├── .babelrc ├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .postcssrc.js ├── README.md ├── build ├── build.js ├── check-versions.js ├── logo.png ├── utils.js ├── vue-loader.conf.js ├── webpack.base.conf.js ├── webpack.dev.conf.js └── webpack.prod.conf.js ├── config ├── dev.env.js ├── index.js ├── prod.env.js └── test.env.js ├── favicon.ico ├── index.html ├── package-lock.json ├── package.json ├── src ├── assets │ ├── images │ │ ├── BlogLogo.png │ │ ├── aritcle-pic1.jpg │ │ ├── aritcle-pic2.jpg │ │ ├── aritcle-pic3.jpg │ │ ├── bg-summer-day.png │ │ └── noData.png │ ├── logo.png │ ├── readme.md │ └── style │ │ ├── iconfont.css │ │ ├── main.scss │ │ ├── markdown.styl │ │ ├── pagecss │ │ ├── myfooter.scss │ │ └── myheader.scss │ │ └── reset.css ├── common │ ├── Article │ │ ├── ArticleCard.vue │ │ └── SideBar.vue │ ├── footer │ │ ├── FooterBar.vue │ │ └── MyFooter.vue │ ├── goTop │ │ └── GoTop.vue │ ├── header │ │ └── MyHeader.vue │ └── readme.md ├── main.js ├── router │ └── index.js ├── store │ └── index.js └── views │ └── blog │ ├── article │ ├── Article.vue │ └── ArticleDetail.vue │ ├── home │ └── Home.vue │ ├── index │ └── Index.vue │ ├── phoneLogin │ └── Login.vue │ ├── recommend │ └── Recommend.vue │ └── write │ ├── Write.vue │ └── components │ └── MyHeader.vue ├── static └── .gitkeep └── test ├── e2e ├── custom-assertions │ └── elementCount.js ├── nightwatch.conf.js ├── runner.js └── specs │ └── test.js └── unit ├── .eslintrc ├── jest.conf.js ├── setup.js └── specs └── HelloWorld.spec.js /README.md: -------------------------------------------------------------------------------- 1 | # 一款基于vue.js 和node构建个人博客项目 2 | 3 | ### 前言 4 | 5 | 本项目是一款个人学习的博客项目,主要是为了学习vue2 和 node.js。另外涉及到MySQL redis nginx 等技术栈知识 [项目地址](https://github.com/HunterXing/404blog) 6 | 7 | ### 预览地址 (PC或者手机) 8 | #### > 注意:PC端支持markdown编辑博客,但是手机端不支持编辑,仅支持查看博客内容 9 | 10 | #### [博客预览地址:node后台版本](https://notfound404.cn:4431) 11 | 预览账号 12 | ``` 13 | 账号:test 14 | 密码:123 15 | ``` 16 | ### 技术栈 17 | 18 | 前端:html、css、sass、ES6、webpack、vue-cli、vue2、vuex、vue-router、axios、element-ui 19 | 20 | 后端:Node、Mysql、redis、nginx 21 | 22 | 23 | ### 目标功能 24 | > 目标很简单 基本的增删改就满足于学习 node 和 vue 实现前后端分离 另外还有redis 的session保存 、ngnix的反向代理、 pm2的进程守护 25 | - [x] 登录 退出(100%) (个人博客没有增加注册项,很好实现) 26 | - [x] 个人博客(100%) 27 | - [x] 推荐博客(100%) 28 | - [x] 添加博客(100%) 29 | - [x] 博客访问量(100%) 30 | - [x] 删除博客(100%) 31 | - [x] 编辑博客(100%) 32 | - [x] 移动端适应(100%) 33 | 34 | - [x] php后台(100%) 35 | - [x] express后台(100%) 36 | - [x] koa后台(100%) 37 | 38 | 39 | ### 项目截图 40 | 41 | #### 首页 42 | ![首页](https://raw.githubusercontent.com/HunterXing/resourse/master/images/20191020181055.png) 43 | 44 | #### 查看博客 45 | ![查看博客](https://raw.githubusercontent.com/HunterXing/resourse/master/images/20191020181158.png) 46 | 47 | #### markdown写博客 48 | ![markdown写博客](https://raw.githubusercontent.com/HunterXing/resourse/master/images/20191020181402.png) 49 | 50 | #### 移动端首页 51 | ![移动端首页](https://upload-images.jianshu.io/upload_images/13711753-010be93c0ed7790f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 52 | 53 | #### 移动端查看博客 54 | 55 | ![移动端查看博客](https://upload-images.jianshu.io/upload_images/13711753-01bce9fdbc25f9e2.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 56 | 57 | 58 | -------------------------------------------------------------------------------- /api.md: -------------------------------------------------------------------------------- 1 | # 项目的需求和Api 2 | - 登录 3 | 4 | ``` 5 | api/user/login post 6 | ``` 7 | 8 | | 参数名 | 描述 | 9 | | -------- | ------ | 10 | | username | 用户名 | 11 | | password | 密码 | 12 | 13 | 14 | - 退出 15 | 16 | ``` 17 | api/user/exit get 18 | ``` 19 | `不需要参数` 20 | 21 | 22 | - 登录验证 23 | 24 | ``` 25 | api/user/loginCheck get 26 | ``` 27 | `不需要参数` 28 | 29 | 30 | - 我的所有文章首页 31 | 32 | ``` 33 | api/blog/list get 34 | ``` 35 | 36 | | 参数名 | 描述 | 37 | | -------- | ------ | 38 | | author | 用户名 | 39 | | keyword | 关键词 | 40 | 41 | - 推荐文章 42 | 43 | ``` 44 | api/blog/getMyArticle post 45 | ``` 46 | `不需要参数` 47 | 48 | - 文章详情 49 | 50 | ``` 51 | api/blog/detail post 52 | ``` 53 | 54 | | 参数名 | 描述 | 55 | | -------- | ------ | 56 | | blogId | 博客的id | 57 | 58 | 59 | - 添加文章 60 | 61 | ``` 62 | api/blog/addArticle post 63 | ``` 64 | 65 | | 参数名 | 描述 | 66 | | -------- | ------ | 67 | | title | 博客的标题 | 68 | | content | 博客的内容 | 69 | | link | 博客转载的链接 | 70 | | author_id | 作者的id | 71 | | markdown | 博客的markdown文档内容 | 72 | | type | 博客的类型 转载或者原创 | 73 | 74 | 75 | 76 | - 得到需要编辑文章的详情 77 | 78 | ``` 79 | api/blog/getEditArticle post 80 | ``` 81 | 82 | | 参数名 | 描述 | 83 | | -------- | ------ | 84 | | blogId | 博客的id | 85 | 86 | 87 | - 编辑文章 88 | 89 | ``` 90 | api/blog/editArticle post 91 | ``` 92 | 93 | | 参数名 | 描述 | 94 | | -------- | ------ | 95 | | blogId | 博客的id | 96 | | title | 博客的标题 | 97 | | detail | 博客的内容 | 98 | | link | 博客转载的链接 | 99 | | markdown | 博客的markdown文档内容 | 100 | | type | 博客的类型 转载或者原创 | 101 | 102 | 103 | - 删除文章 104 | 105 | ``` 106 | api/blog/delArticle post 107 | ``` 108 | 109 | | 参数名 | 描述 | 110 | | -------- | ------ | 111 | | blogId | 博客的id | 112 | 113 | - 增加文章的预览人数 114 | 115 | ``` 116 | api/blog/addview post 117 | ``` 118 | 119 | | 参数名 | 描述 | 120 | | -------- | ------ | 121 | | blogId | 博客的id | 122 | 123 | 124 | 125 | # 数据库设计 126 | 127 | tb_users 128 | 129 | | 字段 | 描述 | 130 | | -------- | ------ | 131 | | id | 用户id | 132 | | username | 用户名 | 133 | | password | 密码 | 134 | | job | 职业 | 135 | | | | 136 | 137 | tb_blogs 138 | 139 | 140 | | 字段 | 描述 | 141 | | -------------- | ------------- | 142 | | id | 博客id | 143 | | author_id | 外键 user的id | 144 | | title | 文章标题 | 145 | | create_time | 创建时间 | 146 | | detail | 详细内容 | 147 | | preview_number | 浏览量 | 148 | | state | 状态 0 删除 1 展示 | 149 | | show | 状态 0 删除 1 展示 | 150 | | type | 0 原创 1转载 | 151 | | link | 转载链接 | -------------------------------------------------------------------------------- /backend/koa2-api/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | /dist/ 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | /test/unit/coverage/ 8 | /test/e2e/reports/ 9 | selenium-debug.log 10 | 11 | # Editor directories and files 12 | .idea 13 | .vscode 14 | *.suo 15 | *.ntvs* 16 | *.njsproj 17 | *.sln 18 | -------------------------------------------------------------------------------- /backend/koa2-api/app.js: -------------------------------------------------------------------------------- 1 | const Koa = require('koa') 2 | const app = new Koa() 3 | const views = require('koa-views') 4 | const json = require('koa-json') 5 | const onerror = require('koa-onerror') 6 | const bodyparser = require('koa-bodyparser') 7 | const logger = require('koa-logger') 8 | const session = require('koa-generic-session') 9 | const redisStore = require('koa-redis') 10 | const { REDIS_CONF } = require('./config/db') 11 | 12 | const index = require('./routes/index') 13 | const user = require('./routes/user') 14 | const blog = require('./routes/blog') 15 | 16 | // error handler 17 | onerror(app) 18 | 19 | // middlewares 20 | app.use(bodyparser({ 21 | enableTypes:['json', 'form', 'text'] 22 | })) 23 | app.use(json()) 24 | app.use(logger()) 25 | app.use(require('koa-static')(__dirname + '/public')) 26 | 27 | app.use(views(__dirname + '/views', { 28 | extension: 'pug' 29 | })) 30 | 31 | // logger 32 | app.use(async (ctx, next) => { 33 | const start = new Date() 34 | await next() 35 | const ms = new Date() - start 36 | console.log(`${ctx.method} ${ctx.url} - ${ms}ms`) 37 | }) 38 | // 一定要在注册路由之前写 39 | app.keys = ['XHiol#23123_'] 40 | app.use(session({ 41 | cookie: { 42 | // path: '/', // 默认配置 43 | // httpOnly: true, // 默认配置 44 | maxAge: 24 * 60 * 60 * 1000 45 | }, 46 | // session 存储到redis 47 | store: redisStore({ 48 | // all : '' 49 | all: `${REDIS_CONF.host}:${REDIS_CONF.port}` 50 | }) 51 | })) 52 | // routes 53 | app.use(index.routes(), index.allowedMethods()) 54 | app.use(user.routes(), user.allowedMethods()) 55 | app.use(blog.routes(), blog.allowedMethods()) 56 | 57 | // error-handling 58 | app.on('error', (err, ctx) => { 59 | console.error('server error', err, ctx) 60 | }); 61 | 62 | module.exports = app 63 | -------------------------------------------------------------------------------- /backend/koa2-api/bin/www: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var app = require('../app'); 8 | var debug = require('debug')('demo:server'); 9 | var http = require('http'); 10 | 11 | /** 12 | * Get port from environment and store in Express. 13 | */ 14 | 15 | var port = normalizePort(process.env.PORT || '8000'); 16 | // app.set('port', port); 17 | 18 | /** 19 | * Create HTTP server. 20 | */ 21 | 22 | var server = http.createServer(app.callback()); 23 | 24 | /** 25 | * Listen on provided port, on all network interfaces. 26 | */ 27 | 28 | server.listen(port); 29 | server.on('error', onError); 30 | server.on('listening', onListening); 31 | 32 | /** 33 | * Normalize a port into a number, string, or false. 34 | */ 35 | 36 | function normalizePort(val) { 37 | var port = parseInt(val, 10); 38 | 39 | if (isNaN(port)) { 40 | // named pipe 41 | return val; 42 | } 43 | 44 | if (port >= 0) { 45 | // port number 46 | return port; 47 | } 48 | 49 | return false; 50 | } 51 | 52 | /** 53 | * Event listener for HTTP server "error" event. 54 | */ 55 | 56 | function onError(error) { 57 | if (error.syscall !== 'listen') { 58 | throw error; 59 | } 60 | 61 | var bind = typeof port === 'string' 62 | ? 'Pipe ' + port 63 | : 'Port ' + port; 64 | 65 | // handle specific listen errors with friendly messages 66 | switch (error.code) { 67 | case 'EACCES': 68 | console.error(bind + ' requires elevated privileges'); 69 | process.exit(1); 70 | break; 71 | case 'EADDRINUSE': 72 | console.error(bind + ' is already in use'); 73 | process.exit(1); 74 | break; 75 | default: 76 | throw error; 77 | } 78 | } 79 | 80 | /** 81 | * Event listener for HTTP server "listening" event. 82 | */ 83 | 84 | function onListening() { 85 | var addr = server.address(); 86 | var bind = typeof addr === 'string' 87 | ? 'pipe ' + addr 88 | : 'port ' + addr.port; 89 | debug('Listening on ' + bind); 90 | } 91 | -------------------------------------------------------------------------------- /backend/koa2-api/config/db.js: -------------------------------------------------------------------------------- 1 | const env = process.env.NODE_ENV // 环境参数 2 | console.log(env) 3 | 4 | // 配置 5 | let MYSQL_CONF 6 | let REDIS_CONF 7 | 8 | if (env === 'dev') { 9 | // mysql 10 | MYSQL_CONF = { 11 | host: 'localhost', 12 | user: 'xingheng', 13 | password: '123456', 14 | port: '3306', 15 | database: 'db_myblog' 16 | } 17 | 18 | // redis 19 | REDIS_CONF = { 20 | port: 6379, 21 | host: '127.0.0.1' 22 | } 23 | } 24 | 25 | if (env === 'production') { 26 | // mysql 27 | MYSQL_CONF = { 28 | host: 'localhost', 29 | user: 'xingheng', 30 | password: '123456', 31 | port: '3306', 32 | database: 'db_myblog' 33 | } 34 | 35 | // redis 36 | REDIS_CONF = { 37 | port: 6379, 38 | host: '127.0.0.1' 39 | } 40 | } 41 | 42 | module.exports = { 43 | MYSQL_CONF, 44 | REDIS_CONF 45 | } -------------------------------------------------------------------------------- /backend/koa2-api/controller/blogController.js: -------------------------------------------------------------------------------- 1 | const { 2 | exec, 3 | escape 4 | } = require('./../db/mysql') 5 | const { timestampToTime } = require('../utils/data') 6 | // 得到博客推荐列表 7 | const getList = async (author, keyword) => { 8 | let sql = 9 | `select * , tb_blogs.id as articleid 10 | from tb_blogs, tb_users 11 | where tb_blogs.author_id=tb_users.id 12 | and tb_blogs.show=1 13 | ` 14 | if (author) { 15 | author = escape(author) 16 | sql += `and author=${author} ` 17 | } 18 | if (keyword) { 19 | keyword = escape(keyword) 20 | sql += `and title like %${keyword}% ` 21 | } 22 | sql += `order by tb_blogs.createtime desc;` 23 | console.log(sql) 24 | // 返回 promise 25 | return await exec(sql) 26 | } 27 | 28 | // 得到自己的博客 29 | const getMyArticle = async (username) => { 30 | let sql = 31 | ` 32 | select *, tb_blogs.id as articleid 33 | from tb_blogs, tb_users 34 | where tb_blogs.author_id=tb_users.id 35 | and tb_users.username= '${username}' 36 | and tb_blogs.show=1 37 | order by tb_blogs.createtime desc; 38 | ` 39 | // console.log(sql) 40 | const rows = await exec(sql) 41 | return rows 42 | 43 | } 44 | // 得到博客详情 45 | const getBlogDetail = async (blogId) => { 46 | let sql = 47 | `select * 48 | from tb_blogs, tb_users 49 | where tb_blogs.author_id=tb_users.id 50 | and tb_blogs.id='${blogId}' 51 | ` 52 | console.log(sql) 53 | // 返回 promise 54 | let data = await exec(sql) 55 | // console.log(data) 56 | return data[0] 57 | } 58 | // 得到需要编辑的博客的详情 59 | const getEditArticle = async (blogId) => { 60 | let sql = 61 | ` 62 | select * from tb_blogs where id = ${blogId} 63 | ` 64 | // console.log(sql) 65 | const rows = await exec(sql) 66 | return rows[0] || {} 67 | 68 | 69 | } 70 | // 编辑博客 71 | const editArticle = async (blogId, title, detail, type, link, markdown) => { 72 | detail = escape(detail) 73 | markdown = escape(markdown) 74 | 75 | title = escape(title) 76 | // console.log(typeof(type),type) 77 | if (type === 0 + '') { 78 | link = '' 79 | } 80 | link = escape(link) 81 | let sql = 82 | ` 83 | update tb_blogs 84 | set 85 | title = ${title}, 86 | content =${detail}, 87 | type = ${type}, 88 | link = ${link}, 89 | markdown =${markdown} 90 | where id = ${blogId} 91 | ` 92 | console.log(sql) 93 | let updateData = await exec(sql) 94 | if (updateData.affectedRows > 0) { 95 | return true 96 | } 97 | return false 98 | } 99 | 100 | // 添加博客 101 | const addArticle = async (title, content, link, author_id,markdown,type) => { 102 | content = escape(content) 103 | markdown = escape(markdown) 104 | title = escape(title) 105 | link = escape(link) 106 | let createtime = timestampToTime(Date.now()) 107 | let sql = 108 | ` 109 | insert into tb_blogs (title, content, link, createtime, author_id, markdown, type) 110 | values (${title}, ${content}, ${link}, '${createtime}', '${author_id}', ${markdown}, '${type}'); 111 | ` 112 | console.log(sql) 113 | let insertData = await exec(sql) 114 | return { 115 | id: insertData.insertId 116 | } 117 | } 118 | 119 | // 删除博客 120 | const delArticle = async (blogId) => { 121 | // id 就是要删除博客的 id 122 | // const sql = `delete from tb_blogs where id='${id}' and author='${author}';` 123 | // return exec(sql).then(delData => { 124 | // // console.log('delData is ', delData) 125 | // if (delData.affectedRows > 0) { 126 | // return true 127 | // } 128 | // return false 129 | // }) 130 | 131 | // 软删除 132 | let sql = 133 | ` 134 | update tb_blogs 135 | set 136 | tb_blogs.show = 0 137 | where id = ${blogId} 138 | ` 139 | console.log(sql) 140 | let updateData = await exec(sql) 141 | if (updateData.affectedRows > 0) { 142 | return true 143 | } 144 | return false 145 | } 146 | 147 | // 浏览人数添加 148 | const addview =async (blogId) => { 149 | let sql = 150 | ` 151 | update tb_blogs 152 | set 153 | tb_blogs.preview = tb_blogs.preview + 1 154 | where id = ${blogId} 155 | ` 156 | // console.log(sql) 157 | let updateData = await exec(sql) 158 | if (updateData.affectedRows > 0) { 159 | return true 160 | } 161 | return false 162 | } 163 | 164 | module.exports = { 165 | getList, 166 | getBlogDetail, 167 | getEditArticle, 168 | editArticle, 169 | getMyArticle, 170 | addArticle, 171 | delArticle, 172 | addview 173 | } -------------------------------------------------------------------------------- /backend/koa2-api/controller/userController.js: -------------------------------------------------------------------------------- 1 | const { exec, escape } = require('../db/mysql') 2 | const { genPassword } = require('../utils/cryps') 3 | 4 | // 登录 5 | const login = async (username, password) => { 6 | username = escape(username) 7 | 8 | // 生成加密密码 9 | password = genPassword(password) 10 | password = escape(password) 11 | 12 | const sql = 13 | ` 14 | select * from tb_users where username=${username} and password=${password} 15 | ` 16 | console.log('sql is', sql) 17 | let rows = await exec(sql) 18 | return rows[0] || {} 19 | } 20 | 21 | 22 | 23 | // 用户信息 24 | const userInfo = async (username) => { 25 | username = escape(username) 26 | const sql = 27 | ` 28 | select tb_users.id ,username from tb_users where username=${username} 29 | ` 30 | // console.log('sql is', sql) 31 | let rows = await exec(sql) 32 | return rows[0] || {} 33 | } 34 | 35 | module.exports = { 36 | login, 37 | userInfo 38 | } -------------------------------------------------------------------------------- /backend/koa2-api/db/mysql.js: -------------------------------------------------------------------------------- 1 | const mysql = require('mysql') 2 | const { MYSQL_CONF } = require('../config/db') 3 | 4 | // 创建链接对象 5 | const con = mysql.createConnection(MYSQL_CONF) 6 | 7 | // 开始链接 8 | con.connect() 9 | 10 | // 统一执行 sql 的函数 11 | function exec(sql) { 12 | const promise = new Promise((resolve, reject) => { 13 | con.query(sql, (err, result) => { 14 | if (err) { 15 | reject(err) 16 | return 17 | } 18 | resolve(result) 19 | }) 20 | }) 21 | return promise 22 | } 23 | 24 | module.exports = { 25 | exec, 26 | escape: mysql.escape 27 | } -------------------------------------------------------------------------------- /backend/koa2-api/logs/err.log: -------------------------------------------------------------------------------- 1 | 2019-09-01 20:51:02: { Error: connect ECONNREFUSED 127.0.0.1:3306 2 | 2019-09-01 20:51:02: at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1117:14) 3 | 2019-09-01 20:51:02: -------------------- 4 | 2019-09-01 20:51:02: at Protocol._enqueue (C:\xampp\htdocs\404blog\backend\koa2-api\node_modules\mysql\lib\protocol\Protocol.js:144:48) 5 | 2019-09-01 20:51:02: at Protocol.handshake (C:\xampp\htdocs\404blog\backend\koa2-api\node_modules\mysql\lib\protocol\Protocol.js:51:23) 6 | 2019-09-01 20:51:02: at Connection.connect (C:\xampp\htdocs\404blog\backend\koa2-api\node_modules\mysql\lib\Connection.js:119:18) 7 | 2019-09-01 20:51:02: at Object. (C:\xampp\htdocs\404blog\backend\koa2-api\db\mysql.js:8:5) 8 | 2019-09-01 20:51:02: at Module._compile (internal/modules/cjs/loader.js:689:30) 9 | 2019-09-01 20:51:02: at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10) 10 | 2019-09-01 20:51:02: at Module.load (internal/modules/cjs/loader.js:599:32) 11 | 2019-09-01 20:51:02: at tryModuleLoad (internal/modules/cjs/loader.js:538:12) 12 | 2019-09-01 20:51:02: at Function.Module._load (internal/modules/cjs/loader.js:530:3) 13 | 2019-09-01 20:51:02: at Module.require (internal/modules/cjs/loader.js:637:17) 14 | 2019-09-01 20:51:02: at Module.Hook._require.Module.require (C:\Users\10482\AppData\Roaming\npm\node_modules\pm2\node_modules\require-in-the-middle\index.js:70:37) 15 | 2019-09-01 20:51:02: at require (internal/modules/cjs/helpers.js:22:18) 16 | 2019-09-01 20:51:02: at Object. (C:\xampp\htdocs\404blog\backend\koa2-api\controller\userController.js:1:88) 17 | 2019-09-01 20:51:02: at Module._compile (internal/modules/cjs/loader.js:689:30) 18 | 2019-09-01 20:51:02: at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10) 19 | 2019-09-01 20:51:02: at Module.load (internal/modules/cjs/loader.js:599:32) 20 | 2019-09-01 20:51:02: errno: 'ECONNREFUSED', 21 | 2019-09-01 20:51:02: code: 'ECONNREFUSED', 22 | 2019-09-01 20:51:02: syscall: 'connect', 23 | 2019-09-01 20:51:02: address: '127.0.0.1', 24 | 2019-09-01 20:51:02: port: 3306, 25 | 2019-09-01 20:51:02: fatal: true } 26 | 2019-09-01 20:51:06: { Error: connect ECONNREFUSED 127.0.0.1:3306 27 | 2019-09-01 20:51:06: at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1117:14) 28 | 2019-09-01 20:51:06: -------------------- 29 | 2019-09-01 20:51:06: at Protocol._enqueue (C:\xampp\htdocs\404blog\backend\koa2-api\node_modules\mysql\lib\protocol\Protocol.js:144:48) 30 | 2019-09-01 20:51:06: at Protocol.handshake (C:\xampp\htdocs\404blog\backend\koa2-api\node_modules\mysql\lib\protocol\Protocol.js:51:23) 31 | 2019-09-01 20:51:06: at Connection.connect (C:\xampp\htdocs\404blog\backend\koa2-api\node_modules\mysql\lib\Connection.js:119:18) 32 | 2019-09-01 20:51:06: at Object. (C:\xampp\htdocs\404blog\backend\koa2-api\db\mysql.js:8:5) 33 | 2019-09-01 20:51:06: at Module._compile (internal/modules/cjs/loader.js:689:30) 34 | 2019-09-01 20:51:06: at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10) 35 | 2019-09-01 20:51:06: at Module.load (internal/modules/cjs/loader.js:599:32) 36 | 2019-09-01 20:51:06: at tryModuleLoad (internal/modules/cjs/loader.js:538:12) 37 | 2019-09-01 20:51:06: at Function.Module._load (internal/modules/cjs/loader.js:530:3) 38 | 2019-09-01 20:51:06: at Module.require (internal/modules/cjs/loader.js:637:17) 39 | 2019-09-01 20:51:06: at Module.Hook._require.Module.require (C:\Users\10482\AppData\Roaming\npm\node_modules\pm2\node_modules\require-in-the-middle\index.js:70:37) 40 | 2019-09-01 20:51:06: at require (internal/modules/cjs/helpers.js:22:18) 41 | 2019-09-01 20:51:06: at Object. (C:\xampp\htdocs\404blog\backend\koa2-api\controller\userController.js:1:88) 42 | 2019-09-01 20:51:06: at Module._compile (internal/modules/cjs/loader.js:689:30) 43 | 2019-09-01 20:51:06: at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10) 44 | 2019-09-01 20:51:06: at Module.load (internal/modules/cjs/loader.js:599:32) 45 | 2019-09-01 20:51:06: errno: 'ECONNREFUSED', 46 | 2019-09-01 20:51:06: code: 'ECONNREFUSED', 47 | 2019-09-01 20:51:06: syscall: 'connect', 48 | 2019-09-01 20:51:06: address: '127.0.0.1', 49 | 2019-09-01 20:51:06: port: 3306, 50 | 2019-09-01 20:51:06: fatal: true } 51 | 2019-09-01 20:51:09: { Error: connect ECONNREFUSED 127.0.0.1:3306 52 | 2019-09-01 20:51:09: at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1117:14) 53 | 2019-09-01 20:51:09: -------------------- 54 | 2019-09-01 20:51:09: at Protocol._enqueue (C:\xampp\htdocs\404blog\backend\koa2-api\node_modules\mysql\lib\protocol\Protocol.js:144:48) 55 | 2019-09-01 20:51:09: at Protocol.handshake (C:\xampp\htdocs\404blog\backend\koa2-api\node_modules\mysql\lib\protocol\Protocol.js:51:23) 56 | 2019-09-01 20:51:09: at Connection.connect (C:\xampp\htdocs\404blog\backend\koa2-api\node_modules\mysql\lib\Connection.js:119:18) 57 | 2019-09-01 20:51:09: at Object. (C:\xampp\htdocs\404blog\backend\koa2-api\db\mysql.js:8:5) 58 | 2019-09-01 20:51:09: at Module._compile (internal/modules/cjs/loader.js:689:30) 59 | 2019-09-01 20:51:09: at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10) 60 | 2019-09-01 20:51:09: at Module.load (internal/modules/cjs/loader.js:599:32) 61 | 2019-09-01 20:51:09: at tryModuleLoad (internal/modules/cjs/loader.js:538:12) 62 | 2019-09-01 20:51:09: at Function.Module._load (internal/modules/cjs/loader.js:530:3) 63 | 2019-09-01 20:51:09: at Module.require (internal/modules/cjs/loader.js:637:17) 64 | 2019-09-01 20:51:09: at Module.Hook._require.Module.require (C:\Users\10482\AppData\Roaming\npm\node_modules\pm2\node_modules\require-in-the-middle\index.js:70:37) 65 | 2019-09-01 20:51:09: at require (internal/modules/cjs/helpers.js:22:18) 66 | 2019-09-01 20:51:09: at Object. (C:\xampp\htdocs\404blog\backend\koa2-api\controller\userController.js:1:88) 67 | 2019-09-01 20:51:09: at Module._compile (internal/modules/cjs/loader.js:689:30) 68 | 2019-09-01 20:51:09: at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10) 69 | 2019-09-01 20:51:09: at Module.load (internal/modules/cjs/loader.js:599:32) 70 | 2019-09-01 20:51:09: errno: 'ECONNREFUSED', 71 | 2019-09-01 20:51:09: code: 'ECONNREFUSED', 72 | 2019-09-01 20:51:09: syscall: 'connect', 73 | 2019-09-01 20:51:09: address: '127.0.0.1', 74 | 2019-09-01 20:51:09: port: 3306, 75 | 2019-09-01 20:51:09: fatal: true } 76 | 2019-09-01 20:51:12: { Error: connect ECONNREFUSED 127.0.0.1:3306 77 | 2019-09-01 20:51:12: at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1117:14) 78 | 2019-09-01 20:51:12: -------------------- 79 | 2019-09-01 20:51:12: at Protocol._enqueue (C:\xampp\htdocs\404blog\backend\koa2-api\node_modules\mysql\lib\protocol\Protocol.js:144:48) 80 | 2019-09-01 20:51:12: at Protocol.handshake (C:\xampp\htdocs\404blog\backend\koa2-api\node_modules\mysql\lib\protocol\Protocol.js:51:23) 81 | 2019-09-01 20:51:12: at Connection.connect (C:\xampp\htdocs\404blog\backend\koa2-api\node_modules\mysql\lib\Connection.js:119:18) 82 | 2019-09-01 20:51:12: at Object. (C:\xampp\htdocs\404blog\backend\koa2-api\db\mysql.js:8:5) 83 | 2019-09-01 20:51:12: at Module._compile (internal/modules/cjs/loader.js:689:30) 84 | 2019-09-01 20:51:12: at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10) 85 | 2019-09-01 20:51:12: at Module.load (internal/modules/cjs/loader.js:599:32) 86 | 2019-09-01 20:51:12: at tryModuleLoad (internal/modules/cjs/loader.js:538:12) 87 | 2019-09-01 20:51:12: at Function.Module._load (internal/modules/cjs/loader.js:530:3) 88 | 2019-09-01 20:51:12: at Module.require (internal/modules/cjs/loader.js:637:17) 89 | 2019-09-01 20:51:12: at Module.Hook._require.Module.require (C:\Users\10482\AppData\Roaming\npm\node_modules\pm2\node_modules\require-in-the-middle\index.js:70:37) 90 | 2019-09-01 20:51:12: at require (internal/modules/cjs/helpers.js:22:18) 91 | 2019-09-01 20:51:12: at Object. (C:\xampp\htdocs\404blog\backend\koa2-api\controller\userController.js:1:88) 92 | 2019-09-01 20:51:12: at Module._compile (internal/modules/cjs/loader.js:689:30) 93 | 2019-09-01 20:51:12: at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10) 94 | 2019-09-01 20:51:12: at Module.load (internal/modules/cjs/loader.js:599:32) 95 | 2019-09-01 20:51:12: errno: 'ECONNREFUSED', 96 | 2019-09-01 20:51:12: code: 'ECONNREFUSED', 97 | 2019-09-01 20:51:12: syscall: 'connect', 98 | 2019-09-01 20:51:12: address: '127.0.0.1', 99 | 2019-09-01 20:51:12: port: 3306, 100 | 2019-09-01 20:51:12: fatal: true } 101 | 2019-09-01 20:51:16: { Error: connect ECONNREFUSED 127.0.0.1:3306 102 | 2019-09-01 20:51:16: at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1117:14) 103 | 2019-09-01 20:51:16: -------------------- 104 | 2019-09-01 20:51:16: at Protocol._enqueue (C:\xampp\htdocs\404blog\backend\koa2-api\node_modules\mysql\lib\protocol\Protocol.js:144:48) 105 | 2019-09-01 20:51:16: at Protocol.handshake (C:\xampp\htdocs\404blog\backend\koa2-api\node_modules\mysql\lib\protocol\Protocol.js:51:23) 106 | 2019-09-01 20:51:16: at Connection.connect (C:\xampp\htdocs\404blog\backend\koa2-api\node_modules\mysql\lib\Connection.js:119:18) 107 | 2019-09-01 20:51:16: at Object. (C:\xampp\htdocs\404blog\backend\koa2-api\db\mysql.js:8:5) 108 | 2019-09-01 20:51:16: at Module._compile (internal/modules/cjs/loader.js:689:30) 109 | 2019-09-01 20:51:16: at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10) 110 | 2019-09-01 20:51:16: at Module.load (internal/modules/cjs/loader.js:599:32) 111 | 2019-09-01 20:51:16: at tryModuleLoad (internal/modules/cjs/loader.js:538:12) 112 | 2019-09-01 20:51:16: at Function.Module._load (internal/modules/cjs/loader.js:530:3) 113 | 2019-09-01 20:51:16: at Module.require (internal/modules/cjs/loader.js:637:17) 114 | 2019-09-01 20:51:16: at Module.Hook._require.Module.require (C:\Users\10482\AppData\Roaming\npm\node_modules\pm2\node_modules\require-in-the-middle\index.js:70:37) 115 | 2019-09-01 20:51:16: at require (internal/modules/cjs/helpers.js:22:18) 116 | 2019-09-01 20:51:16: at Object. (C:\xampp\htdocs\404blog\backend\koa2-api\controller\userController.js:1:88) 117 | 2019-09-01 20:51:16: at Module._compile (internal/modules/cjs/loader.js:689:30) 118 | 2019-09-01 20:51:16: at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10) 119 | 2019-09-01 20:51:16: at Module.load (internal/modules/cjs/loader.js:599:32) 120 | 2019-09-01 20:51:16: errno: 'ECONNREFUSED', 121 | 2019-09-01 20:51:16: code: 'ECONNREFUSED', 122 | 2019-09-01 20:51:16: syscall: 'connect', 123 | 2019-09-01 20:51:16: address: '127.0.0.1', 124 | 2019-09-01 20:51:16: port: 3306, 125 | 2019-09-01 20:51:16: fatal: true } 126 | 2019-09-01 20:51:19: { Error: connect ECONNREFUSED 127.0.0.1:3306 127 | 2019-09-01 20:51:19: at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1117:14) 128 | 2019-09-01 20:51:19: -------------------- 129 | 2019-09-01 20:51:19: at Protocol._enqueue (C:\xampp\htdocs\404blog\backend\koa2-api\node_modules\mysql\lib\protocol\Protocol.js:144:48) 130 | 2019-09-01 20:51:19: at Protocol.handshake (C:\xampp\htdocs\404blog\backend\koa2-api\node_modules\mysql\lib\protocol\Protocol.js:51:23) 131 | 2019-09-01 20:51:19: at Connection.connect (C:\xampp\htdocs\404blog\backend\koa2-api\node_modules\mysql\lib\Connection.js:119:18) 132 | 2019-09-01 20:51:19: at Object. (C:\xampp\htdocs\404blog\backend\koa2-api\db\mysql.js:8:5) 133 | 2019-09-01 20:51:19: at Module._compile (internal/modules/cjs/loader.js:689:30) 134 | 2019-09-01 20:51:19: at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10) 135 | 2019-09-01 20:51:19: at Module.load (internal/modules/cjs/loader.js:599:32) 136 | 2019-09-01 20:51:19: at tryModuleLoad (internal/modules/cjs/loader.js:538:12) 137 | 2019-09-01 20:51:19: at Function.Module._load (internal/modules/cjs/loader.js:530:3) 138 | 2019-09-01 20:51:19: at Module.require (internal/modules/cjs/loader.js:637:17) 139 | 2019-09-01 20:51:19: at Module.Hook._require.Module.require (C:\Users\10482\AppData\Roaming\npm\node_modules\pm2\node_modules\require-in-the-middle\index.js:70:37) 140 | 2019-09-01 20:51:19: at require (internal/modules/cjs/helpers.js:22:18) 141 | 2019-09-01 20:51:19: at Object. (C:\xampp\htdocs\404blog\backend\koa2-api\controller\userController.js:1:88) 142 | 2019-09-01 20:51:19: at Module._compile (internal/modules/cjs/loader.js:689:30) 143 | 2019-09-01 20:51:19: at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10) 144 | 2019-09-01 20:51:19: at Module.load (internal/modules/cjs/loader.js:599:32) 145 | 2019-09-01 20:51:19: errno: 'ECONNREFUSED', 146 | 2019-09-01 20:51:19: code: 'ECONNREFUSED', 147 | 2019-09-01 20:51:19: syscall: 'connect', 148 | 2019-09-01 20:51:19: address: '127.0.0.1', 149 | 2019-09-01 20:51:19: port: 3306, 150 | 2019-09-01 20:51:19: fatal: true } 151 | 2019-09-01 20:51:22: { Error: connect ECONNREFUSED 127.0.0.1:3306 152 | 2019-09-01 20:51:22: at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1117:14) 153 | 2019-09-01 20:51:22: -------------------- 154 | 2019-09-01 20:51:22: at Protocol._enqueue (C:\xampp\htdocs\404blog\backend\koa2-api\node_modules\mysql\lib\protocol\Protocol.js:144:48) 155 | 2019-09-01 20:51:22: at Protocol.handshake (C:\xampp\htdocs\404blog\backend\koa2-api\node_modules\mysql\lib\protocol\Protocol.js:51:23) 156 | 2019-09-01 20:51:22: at Connection.connect (C:\xampp\htdocs\404blog\backend\koa2-api\node_modules\mysql\lib\Connection.js:119:18) 157 | 2019-09-01 20:51:22: at Object. (C:\xampp\htdocs\404blog\backend\koa2-api\db\mysql.js:8:5) 158 | 2019-09-01 20:51:22: at Module._compile (internal/modules/cjs/loader.js:689:30) 159 | 2019-09-01 20:51:22: at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10) 160 | 2019-09-01 20:51:22: at Module.load (internal/modules/cjs/loader.js:599:32) 161 | 2019-09-01 20:51:22: at tryModuleLoad (internal/modules/cjs/loader.js:538:12) 162 | 2019-09-01 20:51:22: at Function.Module._load (internal/modules/cjs/loader.js:530:3) 163 | 2019-09-01 20:51:22: at Module.require (internal/modules/cjs/loader.js:637:17) 164 | 2019-09-01 20:51:22: at Module.Hook._require.Module.require (C:\Users\10482\AppData\Roaming\npm\node_modules\pm2\node_modules\require-in-the-middle\index.js:70:37) 165 | 2019-09-01 20:51:22: at require (internal/modules/cjs/helpers.js:22:18) 166 | 2019-09-01 20:51:22: at Object. (C:\xampp\htdocs\404blog\backend\koa2-api\controller\userController.js:1:88) 167 | 2019-09-01 20:51:22: at Module._compile (internal/modules/cjs/loader.js:689:30) 168 | 2019-09-01 20:51:22: at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10) 169 | 2019-09-01 20:51:22: at Module.load (internal/modules/cjs/loader.js:599:32) 170 | 2019-09-01 20:51:22: errno: 'ECONNREFUSED', 171 | 2019-09-01 20:51:22: code: 'ECONNREFUSED', 172 | 2019-09-01 20:51:22: syscall: 'connect', 173 | 2019-09-01 20:51:22: address: '127.0.0.1', 174 | 2019-09-01 20:51:22: port: 3306, 175 | 2019-09-01 20:51:22: fatal: true } 176 | 2019-09-01 20:51:26: { Error: connect ECONNREFUSED 127.0.0.1:3306 177 | 2019-09-01 20:51:26: at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1117:14) 178 | 2019-09-01 20:51:26: -------------------- 179 | 2019-09-01 20:51:26: at Protocol._enqueue (C:\xampp\htdocs\404blog\backend\koa2-api\node_modules\mysql\lib\protocol\Protocol.js:144:48) 180 | 2019-09-01 20:51:26: at Protocol.handshake (C:\xampp\htdocs\404blog\backend\koa2-api\node_modules\mysql\lib\protocol\Protocol.js:51:23) 181 | 2019-09-01 20:51:26: at Connection.connect (C:\xampp\htdocs\404blog\backend\koa2-api\node_modules\mysql\lib\Connection.js:119:18) 182 | 2019-09-01 20:51:26: at Object. (C:\xampp\htdocs\404blog\backend\koa2-api\db\mysql.js:8:5) 183 | 2019-09-01 20:51:26: at Module._compile (internal/modules/cjs/loader.js:689:30) 184 | 2019-09-01 20:51:26: at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10) 185 | 2019-09-01 20:51:26: at Module.load (internal/modules/cjs/loader.js:599:32) 186 | 2019-09-01 20:51:26: at tryModuleLoad (internal/modules/cjs/loader.js:538:12) 187 | 2019-09-01 20:51:26: at Function.Module._load (internal/modules/cjs/loader.js:530:3) 188 | 2019-09-01 20:51:26: at Module.require (internal/modules/cjs/loader.js:637:17) 189 | 2019-09-01 20:51:26: at Module.Hook._require.Module.require (C:\Users\10482\AppData\Roaming\npm\node_modules\pm2\node_modules\require-in-the-middle\index.js:70:37) 190 | 2019-09-01 20:51:26: at require (internal/modules/cjs/helpers.js:22:18) 191 | 2019-09-01 20:51:26: at Object. (C:\xampp\htdocs\404blog\backend\koa2-api\controller\userController.js:1:88) 192 | 2019-09-01 20:51:26: at Module._compile (internal/modules/cjs/loader.js:689:30) 193 | 2019-09-01 20:51:26: at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10) 194 | 2019-09-01 20:51:26: at Module.load (internal/modules/cjs/loader.js:599:32) 195 | 2019-09-01 20:51:26: errno: 'ECONNREFUSED', 196 | 2019-09-01 20:51:26: code: 'ECONNREFUSED', 197 | 2019-09-01 20:51:26: syscall: 'connect', 198 | 2019-09-01 20:51:26: address: '127.0.0.1', 199 | 2019-09-01 20:51:26: port: 3306, 200 | 2019-09-01 20:51:26: fatal: true } 201 | 2019-09-01 20:51:29: { Error: connect ECONNREFUSED 127.0.0.1:3306 202 | 2019-09-01 20:51:29: at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1117:14) 203 | 2019-09-01 20:51:29: -------------------- 204 | 2019-09-01 20:51:29: at Protocol._enqueue (C:\xampp\htdocs\404blog\backend\koa2-api\node_modules\mysql\lib\protocol\Protocol.js:144:48) 205 | 2019-09-01 20:51:29: at Protocol.handshake (C:\xampp\htdocs\404blog\backend\koa2-api\node_modules\mysql\lib\protocol\Protocol.js:51:23) 206 | 2019-09-01 20:51:29: at Connection.connect (C:\xampp\htdocs\404blog\backend\koa2-api\node_modules\mysql\lib\Connection.js:119:18) 207 | 2019-09-01 20:51:29: at Object. (C:\xampp\htdocs\404blog\backend\koa2-api\db\mysql.js:8:5) 208 | 2019-09-01 20:51:29: at Module._compile (internal/modules/cjs/loader.js:689:30) 209 | 2019-09-01 20:51:29: at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10) 210 | 2019-09-01 20:51:29: at Module.load (internal/modules/cjs/loader.js:599:32) 211 | 2019-09-01 20:51:29: at tryModuleLoad (internal/modules/cjs/loader.js:538:12) 212 | 2019-09-01 20:51:29: at Function.Module._load (internal/modules/cjs/loader.js:530:3) 213 | 2019-09-01 20:51:29: at Module.require (internal/modules/cjs/loader.js:637:17) 214 | 2019-09-01 20:51:29: at Module.Hook._require.Module.require (C:\Users\10482\AppData\Roaming\npm\node_modules\pm2\node_modules\require-in-the-middle\index.js:70:37) 215 | 2019-09-01 20:51:29: at require (internal/modules/cjs/helpers.js:22:18) 216 | 2019-09-01 20:51:29: at Object. (C:\xampp\htdocs\404blog\backend\koa2-api\controller\userController.js:1:88) 217 | 2019-09-01 20:51:29: at Module._compile (internal/modules/cjs/loader.js:689:30) 218 | 2019-09-01 20:51:29: at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10) 219 | 2019-09-01 20:51:29: at Module.load (internal/modules/cjs/loader.js:599:32) 220 | 2019-09-01 20:51:29: errno: 'ECONNREFUSED', 221 | 2019-09-01 20:51:29: code: 'ECONNREFUSED', 222 | 2019-09-01 20:51:29: syscall: 'connect', 223 | 2019-09-01 20:51:29: address: '127.0.0.1', 224 | 2019-09-01 20:51:29: port: 3306, 225 | 2019-09-01 20:51:29: fatal: true } 226 | 2019-09-01 20:51:32: { Error: connect ECONNREFUSED 127.0.0.1:3306 227 | 2019-09-01 20:51:32: at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1117:14) 228 | 2019-09-01 20:51:32: -------------------- 229 | 2019-09-01 20:51:32: at Protocol._enqueue (C:\xampp\htdocs\404blog\backend\koa2-api\node_modules\mysql\lib\protocol\Protocol.js:144:48) 230 | 2019-09-01 20:51:32: at Protocol.handshake (C:\xampp\htdocs\404blog\backend\koa2-api\node_modules\mysql\lib\protocol\Protocol.js:51:23) 231 | 2019-09-01 20:51:32: at Connection.connect (C:\xampp\htdocs\404blog\backend\koa2-api\node_modules\mysql\lib\Connection.js:119:18) 232 | 2019-09-01 20:51:32: at Object. (C:\xampp\htdocs\404blog\backend\koa2-api\db\mysql.js:8:5) 233 | 2019-09-01 20:51:32: at Module._compile (internal/modules/cjs/loader.js:689:30) 234 | 2019-09-01 20:51:32: at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10) 235 | 2019-09-01 20:51:32: at Module.load (internal/modules/cjs/loader.js:599:32) 236 | 2019-09-01 20:51:32: at tryModuleLoad (internal/modules/cjs/loader.js:538:12) 237 | 2019-09-01 20:51:32: at Function.Module._load (internal/modules/cjs/loader.js:530:3) 238 | 2019-09-01 20:51:32: at Module.require (internal/modules/cjs/loader.js:637:17) 239 | 2019-09-01 20:51:32: at Module.Hook._require.Module.require (C:\Users\10482\AppData\Roaming\npm\node_modules\pm2\node_modules\require-in-the-middle\index.js:70:37) 240 | 2019-09-01 20:51:32: at require (internal/modules/cjs/helpers.js:22:18) 241 | 2019-09-01 20:51:32: at Object. (C:\xampp\htdocs\404blog\backend\koa2-api\controller\userController.js:1:88) 242 | 2019-09-01 20:51:32: at Module._compile (internal/modules/cjs/loader.js:689:30) 243 | 2019-09-01 20:51:32: at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10) 244 | 2019-09-01 20:51:32: at Module.load (internal/modules/cjs/loader.js:599:32) 245 | 2019-09-01 20:51:32: errno: 'ECONNREFUSED', 246 | 2019-09-01 20:51:32: code: 'ECONNREFUSED', 247 | 2019-09-01 20:51:32: syscall: 'connect', 248 | 2019-09-01 20:51:32: address: '127.0.0.1', 249 | 2019-09-01 20:51:32: port: 3306, 250 | 2019-09-01 20:51:32: fatal: true } 251 | 2019-09-01 20:51:36: { Error: connect ECONNREFUSED 127.0.0.1:3306 252 | 2019-09-01 20:51:36: at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1117:14) 253 | 2019-09-01 20:51:36: -------------------- 254 | 2019-09-01 20:51:36: at Protocol._enqueue (C:\xampp\htdocs\404blog\backend\koa2-api\node_modules\mysql\lib\protocol\Protocol.js:144:48) 255 | 2019-09-01 20:51:36: at Protocol.handshake (C:\xampp\htdocs\404blog\backend\koa2-api\node_modules\mysql\lib\protocol\Protocol.js:51:23) 256 | 2019-09-01 20:51:36: at Connection.connect (C:\xampp\htdocs\404blog\backend\koa2-api\node_modules\mysql\lib\Connection.js:119:18) 257 | 2019-09-01 20:51:36: at Object. (C:\xampp\htdocs\404blog\backend\koa2-api\db\mysql.js:8:5) 258 | 2019-09-01 20:51:36: at Module._compile (internal/modules/cjs/loader.js:689:30) 259 | 2019-09-01 20:51:36: at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10) 260 | 2019-09-01 20:51:36: at Module.load (internal/modules/cjs/loader.js:599:32) 261 | 2019-09-01 20:51:36: at tryModuleLoad (internal/modules/cjs/loader.js:538:12) 262 | 2019-09-01 20:51:36: at Function.Module._load (internal/modules/cjs/loader.js:530:3) 263 | 2019-09-01 20:51:36: at Module.require (internal/modules/cjs/loader.js:637:17) 264 | 2019-09-01 20:51:36: at Module.Hook._require.Module.require (C:\Users\10482\AppData\Roaming\npm\node_modules\pm2\node_modules\require-in-the-middle\index.js:70:37) 265 | 2019-09-01 20:51:36: at require (internal/modules/cjs/helpers.js:22:18) 266 | 2019-09-01 20:51:36: at Object. (C:\xampp\htdocs\404blog\backend\koa2-api\controller\userController.js:1:88) 267 | 2019-09-01 20:51:36: at Module._compile (internal/modules/cjs/loader.js:689:30) 268 | 2019-09-01 20:51:36: at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10) 269 | 2019-09-01 20:51:36: at Module.load (internal/modules/cjs/loader.js:599:32) 270 | 2019-09-01 20:51:36: errno: 'ECONNREFUSED', 271 | 2019-09-01 20:51:36: code: 'ECONNREFUSED', 272 | 2019-09-01 20:51:36: syscall: 'connect', 273 | 2019-09-01 20:51:36: address: '127.0.0.1', 274 | 2019-09-01 20:51:36: port: 3306, 275 | 2019-09-01 20:51:36: fatal: true } 276 | 2019-09-01 20:51:39: { Error: connect ECONNREFUSED 127.0.0.1:3306 277 | 2019-09-01 20:51:39: at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1117:14) 278 | 2019-09-01 20:51:39: -------------------- 279 | 2019-09-01 20:51:39: at Protocol._enqueue (C:\xampp\htdocs\404blog\backend\koa2-api\node_modules\mysql\lib\protocol\Protocol.js:144:48) 280 | 2019-09-01 20:51:39: at Protocol.handshake (C:\xampp\htdocs\404blog\backend\koa2-api\node_modules\mysql\lib\protocol\Protocol.js:51:23) 281 | 2019-09-01 20:51:39: at Connection.connect (C:\xampp\htdocs\404blog\backend\koa2-api\node_modules\mysql\lib\Connection.js:119:18) 282 | 2019-09-01 20:51:39: at Object. (C:\xampp\htdocs\404blog\backend\koa2-api\db\mysql.js:8:5) 283 | 2019-09-01 20:51:39: at Module._compile (internal/modules/cjs/loader.js:689:30) 284 | 2019-09-01 20:51:39: at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10) 285 | 2019-09-01 20:51:39: at Module.load (internal/modules/cjs/loader.js:599:32) 286 | 2019-09-01 20:51:39: at tryModuleLoad (internal/modules/cjs/loader.js:538:12) 287 | 2019-09-01 20:51:39: at Function.Module._load (internal/modules/cjs/loader.js:530:3) 288 | 2019-09-01 20:51:39: at Module.require (internal/modules/cjs/loader.js:637:17) 289 | 2019-09-01 20:51:39: at Module.Hook._require.Module.require (C:\Users\10482\AppData\Roaming\npm\node_modules\pm2\node_modules\require-in-the-middle\index.js:70:37) 290 | 2019-09-01 20:51:39: at require (internal/modules/cjs/helpers.js:22:18) 291 | 2019-09-01 20:51:39: at Object. (C:\xampp\htdocs\404blog\backend\koa2-api\controller\userController.js:1:88) 292 | 2019-09-01 20:51:39: at Module._compile (internal/modules/cjs/loader.js:689:30) 293 | 2019-09-01 20:51:39: at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10) 294 | 2019-09-01 20:51:39: at Module.load (internal/modules/cjs/loader.js:599:32) 295 | 2019-09-01 20:51:39: errno: 'ECONNREFUSED', 296 | 2019-09-01 20:51:39: code: 'ECONNREFUSED', 297 | 2019-09-01 20:51:39: syscall: 'connect', 298 | 2019-09-01 20:51:39: address: '127.0.0.1', 299 | 2019-09-01 20:51:39: port: 3306, 300 | 2019-09-01 20:51:39: fatal: true } 301 | 2019-09-01 20:51:42: { Error: connect ECONNREFUSED 127.0.0.1:3306 302 | 2019-09-01 20:51:42: at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1117:14) 303 | 2019-09-01 20:51:42: -------------------- 304 | 2019-09-01 20:51:42: at Protocol._enqueue (C:\xampp\htdocs\404blog\backend\koa2-api\node_modules\mysql\lib\protocol\Protocol.js:144:48) 305 | 2019-09-01 20:51:42: at Protocol.handshake (C:\xampp\htdocs\404blog\backend\koa2-api\node_modules\mysql\lib\protocol\Protocol.js:51:23) 306 | 2019-09-01 20:51:42: at Connection.connect (C:\xampp\htdocs\404blog\backend\koa2-api\node_modules\mysql\lib\Connection.js:119:18) 307 | 2019-09-01 20:51:42: at Object. (C:\xampp\htdocs\404blog\backend\koa2-api\db\mysql.js:8:5) 308 | 2019-09-01 20:51:42: at Module._compile (internal/modules/cjs/loader.js:689:30) 309 | 2019-09-01 20:51:42: at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10) 310 | 2019-09-01 20:51:42: at Module.load (internal/modules/cjs/loader.js:599:32) 311 | 2019-09-01 20:51:42: at tryModuleLoad (internal/modules/cjs/loader.js:538:12) 312 | 2019-09-01 20:51:42: at Function.Module._load (internal/modules/cjs/loader.js:530:3) 313 | 2019-09-01 20:51:42: at Module.require (internal/modules/cjs/loader.js:637:17) 314 | 2019-09-01 20:51:42: at Module.Hook._require.Module.require (C:\Users\10482\AppData\Roaming\npm\node_modules\pm2\node_modules\require-in-the-middle\index.js:70:37) 315 | 2019-09-01 20:51:42: at require (internal/modules/cjs/helpers.js:22:18) 316 | 2019-09-01 20:51:42: at Object. (C:\xampp\htdocs\404blog\backend\koa2-api\controller\userController.js:1:88) 317 | 2019-09-01 20:51:42: at Module._compile (internal/modules/cjs/loader.js:689:30) 318 | 2019-09-01 20:51:42: at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10) 319 | 2019-09-01 20:51:42: at Module.load (internal/modules/cjs/loader.js:599:32) 320 | 2019-09-01 20:51:42: errno: 'ECONNREFUSED', 321 | 2019-09-01 20:51:42: code: 'ECONNREFUSED', 322 | 2019-09-01 20:51:42: syscall: 'connect', 323 | 2019-09-01 20:51:42: address: '127.0.0.1', 324 | 2019-09-01 20:51:42: port: 3306, 325 | 2019-09-01 20:51:42: fatal: true } 326 | 2019-09-01 20:51:45: { Error: connect ECONNREFUSED 127.0.0.1:3306 327 | 2019-09-01 20:51:45: at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1117:14) 328 | 2019-09-01 20:51:45: -------------------- 329 | 2019-09-01 20:51:45: at Protocol._enqueue (C:\xampp\htdocs\404blog\backend\koa2-api\node_modules\mysql\lib\protocol\Protocol.js:144:48) 330 | 2019-09-01 20:51:45: at Protocol.handshake (C:\xampp\htdocs\404blog\backend\koa2-api\node_modules\mysql\lib\protocol\Protocol.js:51:23) 331 | 2019-09-01 20:51:45: at Connection.connect (C:\xampp\htdocs\404blog\backend\koa2-api\node_modules\mysql\lib\Connection.js:119:18) 332 | 2019-09-01 20:51:45: at Object. (C:\xampp\htdocs\404blog\backend\koa2-api\db\mysql.js:8:5) 333 | 2019-09-01 20:51:45: at Module._compile (internal/modules/cjs/loader.js:689:30) 334 | 2019-09-01 20:51:45: at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10) 335 | 2019-09-01 20:51:45: at Module.load (internal/modules/cjs/loader.js:599:32) 336 | 2019-09-01 20:51:45: at tryModuleLoad (internal/modules/cjs/loader.js:538:12) 337 | 2019-09-01 20:51:45: at Function.Module._load (internal/modules/cjs/loader.js:530:3) 338 | 2019-09-01 20:51:45: at Module.require (internal/modules/cjs/loader.js:637:17) 339 | 2019-09-01 20:51:45: at Module.Hook._require.Module.require (C:\Users\10482\AppData\Roaming\npm\node_modules\pm2\node_modules\require-in-the-middle\index.js:70:37) 340 | 2019-09-01 20:51:45: at require (internal/modules/cjs/helpers.js:22:18) 341 | 2019-09-01 20:51:45: at Object. (C:\xampp\htdocs\404blog\backend\koa2-api\controller\userController.js:1:88) 342 | 2019-09-01 20:51:45: at Module._compile (internal/modules/cjs/loader.js:689:30) 343 | 2019-09-01 20:51:45: at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10) 344 | 2019-09-01 20:51:45: at Module.load (internal/modules/cjs/loader.js:599:32) 345 | 2019-09-01 20:51:45: errno: 'ECONNREFUSED', 346 | 2019-09-01 20:51:45: code: 'ECONNREFUSED', 347 | 2019-09-01 20:51:45: syscall: 'connect', 348 | 2019-09-01 20:51:45: address: '127.0.0.1', 349 | 2019-09-01 20:51:45: port: 3306, 350 | 2019-09-01 20:51:45: fatal: true } 351 | 2019-09-01 20:51:49: { Error: connect ECONNREFUSED 127.0.0.1:3306 352 | 2019-09-01 20:51:49: at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1117:14) 353 | 2019-09-01 20:51:49: -------------------- 354 | 2019-09-01 20:51:49: at Protocol._enqueue (C:\xampp\htdocs\404blog\backend\koa2-api\node_modules\mysql\lib\protocol\Protocol.js:144:48) 355 | 2019-09-01 20:51:49: at Protocol.handshake (C:\xampp\htdocs\404blog\backend\koa2-api\node_modules\mysql\lib\protocol\Protocol.js:51:23) 356 | 2019-09-01 20:51:49: at Connection.connect (C:\xampp\htdocs\404blog\backend\koa2-api\node_modules\mysql\lib\Connection.js:119:18) 357 | 2019-09-01 20:51:49: at Object. (C:\xampp\htdocs\404blog\backend\koa2-api\db\mysql.js:8:5) 358 | 2019-09-01 20:51:49: at Module._compile (internal/modules/cjs/loader.js:689:30) 359 | 2019-09-01 20:51:49: at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10) 360 | 2019-09-01 20:51:49: at Module.load (internal/modules/cjs/loader.js:599:32) 361 | 2019-09-01 20:51:49: at tryModuleLoad (internal/modules/cjs/loader.js:538:12) 362 | 2019-09-01 20:51:49: at Function.Module._load (internal/modules/cjs/loader.js:530:3) 363 | 2019-09-01 20:51:49: at Module.require (internal/modules/cjs/loader.js:637:17) 364 | 2019-09-01 20:51:49: at Module.Hook._require.Module.require (C:\Users\10482\AppData\Roaming\npm\node_modules\pm2\node_modules\require-in-the-middle\index.js:70:37) 365 | 2019-09-01 20:51:49: at require (internal/modules/cjs/helpers.js:22:18) 366 | 2019-09-01 20:51:49: at Object. (C:\xampp\htdocs\404blog\backend\koa2-api\controller\userController.js:1:88) 367 | 2019-09-01 20:51:49: at Module._compile (internal/modules/cjs/loader.js:689:30) 368 | 2019-09-01 20:51:49: at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10) 369 | 2019-09-01 20:51:49: at Module.load (internal/modules/cjs/loader.js:599:32) 370 | 2019-09-01 20:51:49: errno: 'ECONNREFUSED', 371 | 2019-09-01 20:51:49: code: 'ECONNREFUSED', 372 | 2019-09-01 20:51:49: syscall: 'connect', 373 | 2019-09-01 20:51:49: address: '127.0.0.1', 374 | 2019-09-01 20:51:49: port: 3306, 375 | 2019-09-01 20:51:49: fatal: true } 376 | 2019-09-01 20:51:52: { Error: connect ECONNREFUSED 127.0.0.1:3306 377 | 2019-09-01 20:51:52: at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1117:14) 378 | 2019-09-01 20:51:52: -------------------- 379 | 2019-09-01 20:51:52: at Protocol._enqueue (C:\xampp\htdocs\404blog\backend\koa2-api\node_modules\mysql\lib\protocol\Protocol.js:144:48) 380 | 2019-09-01 20:51:52: at Protocol.handshake (C:\xampp\htdocs\404blog\backend\koa2-api\node_modules\mysql\lib\protocol\Protocol.js:51:23) 381 | 2019-09-01 20:51:52: at Connection.connect (C:\xampp\htdocs\404blog\backend\koa2-api\node_modules\mysql\lib\Connection.js:119:18) 382 | 2019-09-01 20:51:52: at Object. (C:\xampp\htdocs\404blog\backend\koa2-api\db\mysql.js:8:5) 383 | 2019-09-01 20:51:52: at Module._compile (internal/modules/cjs/loader.js:689:30) 384 | 2019-09-01 20:51:52: at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10) 385 | 2019-09-01 20:51:52: at Module.load (internal/modules/cjs/loader.js:599:32) 386 | 2019-09-01 20:51:52: at tryModuleLoad (internal/modules/cjs/loader.js:538:12) 387 | 2019-09-01 20:51:52: at Function.Module._load (internal/modules/cjs/loader.js:530:3) 388 | 2019-09-01 20:51:52: at Module.require (internal/modules/cjs/loader.js:637:17) 389 | 2019-09-01 20:51:52: at Module.Hook._require.Module.require (C:\Users\10482\AppData\Roaming\npm\node_modules\pm2\node_modules\require-in-the-middle\index.js:70:37) 390 | 2019-09-01 20:51:52: at require (internal/modules/cjs/helpers.js:22:18) 391 | 2019-09-01 20:51:52: at Object. (C:\xampp\htdocs\404blog\backend\koa2-api\controller\userController.js:1:88) 392 | 2019-09-01 20:51:52: at Module._compile (internal/modules/cjs/loader.js:689:30) 393 | 2019-09-01 20:51:52: at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10) 394 | 2019-09-01 20:51:52: at Module.load (internal/modules/cjs/loader.js:599:32) 395 | 2019-09-01 20:51:52: errno: 'ECONNREFUSED', 396 | 2019-09-01 20:51:52: code: 'ECONNREFUSED', 397 | 2019-09-01 20:51:52: syscall: 'connect', 398 | 2019-09-01 20:51:52: address: '127.0.0.1', 399 | 2019-09-01 20:51:52: port: 3306, 400 | 2019-09-01 20:51:52: fatal: true } 401 | -------------------------------------------------------------------------------- /backend/koa2-api/logs/out.log: -------------------------------------------------------------------------------- 1 | 2019-09-01 20:51:00: production 2 | 2019-09-01 20:51:04: production 3 | 2019-09-01 20:51:07: production 4 | 2019-09-01 20:51:10: production 5 | 2019-09-01 20:51:14: production 6 | 2019-09-01 20:51:17: production 7 | 2019-09-01 20:51:20: production 8 | 2019-09-01 20:51:23: production 9 | 2019-09-01 20:51:27: production 10 | 2019-09-01 20:51:30: production 11 | 2019-09-01 20:51:33: production 12 | 2019-09-01 20:51:37: production 13 | 2019-09-01 20:51:40: production 14 | 2019-09-01 20:51:43: production 15 | 2019-09-01 20:51:46: production 16 | 2019-09-01 20:51:50: production 17 | 2019-09-01 20:51:53: production 18 | -------------------------------------------------------------------------------- /backend/koa2-api/middleware/loginCheck.js: -------------------------------------------------------------------------------- 1 | const { ErrorModel } = require('../model/resModel') 2 | 3 | module.exports = async (ctx, next) => { 4 | if (ctx.session.username) { 5 | await next() 6 | return 7 | } 8 | ctx.body( 9 | new ErrorModel('未登录') 10 | ) 11 | } -------------------------------------------------------------------------------- /backend/koa2-api/middleware/readme.md: -------------------------------------------------------------------------------- 1 | # 这个文件夹放置自己定义的中间件 -------------------------------------------------------------------------------- /backend/koa2-api/model/resModel.js: -------------------------------------------------------------------------------- 1 | class BaseModel { 2 | constructor(data, message) { 3 | if (typeof data === 'string') { 4 | this.message = data 5 | data = null 6 | message = null 7 | } 8 | if (data) { 9 | this.data = data 10 | } 11 | if (message) { 12 | this.message = message 13 | } 14 | } 15 | } 16 | 17 | class SuccessModel extends BaseModel { 18 | constructor(data, message) { 19 | super(data, message) 20 | this.errno = 0 21 | } 22 | } 23 | 24 | class ErrorModel extends BaseModel { 25 | constructor(data, message) { 26 | super(data, message) 27 | this.errno = -1 28 | } 29 | } 30 | 31 | module.exports = { 32 | SuccessModel, 33 | ErrorModel 34 | } -------------------------------------------------------------------------------- /backend/koa2-api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "koa2-api", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "node bin/www", 7 | "dev": "cross-env NODE_ENV=dev ./node_modules/.bin/nodemon bin/www", 8 | "prd": "cross-env NODE_ENV=production pm2 start pm2.config.json", 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "dependencies": { 12 | "cross-env": "^5.2.0", 13 | "crypto": "^1.0.1", 14 | "debug": "^4.1.1", 15 | "koa": "^2.7.0", 16 | "koa-bodyparser": "^4.2.1", 17 | "koa-convert": "^1.2.0", 18 | "koa-generic-session": "^2.0.1", 19 | "koa-json": "^2.0.2", 20 | "koa-logger": "^3.2.0", 21 | "koa-onerror": "^4.1.0", 22 | "koa-redis": "^4.0.0", 23 | "koa-router": "^7.4.0", 24 | "koa-static": "^5.0.0", 25 | "koa-views": "^6.2.0", 26 | "mysql": "^2.17.1", 27 | "pug": "^2.0.3", 28 | "redis": "^2.8.0", 29 | "xss": "^1.0.6" 30 | }, 31 | "devDependencies": { 32 | "nodemon": "^1.19.1" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /backend/koa2-api/pm2.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "apps": { 3 | "name": "koa2-api", 4 | "script": "bin/www", 5 | "watch": true, 6 | "ignore_watch": [ 7 | "node_modules", 8 | "logs" 9 | ], 10 | "error_file": "logs/err.log", 11 | "out_file": "logs/out.log", 12 | "log_date_format": "YYYY-MM-DD HH:mm:ss" 13 | } 14 | } -------------------------------------------------------------------------------- /backend/koa2-api/public/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 50px; 3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; 4 | } 5 | 6 | a { 7 | color: #00B7FF; 8 | } 9 | -------------------------------------------------------------------------------- /backend/koa2-api/routes/blog.js: -------------------------------------------------------------------------------- 1 | const router = require('koa-router')() 2 | 3 | router.prefix('/api/blog') 4 | 5 | // 登录验证中间件 6 | const loginCheck = require('../middleware/loginCheck') 7 | const { 8 | getList, 9 | getBlogDetail, 10 | getEditArticle, 11 | editArticle, 12 | getMyArticle, 13 | addArticle, 14 | delArticle, 15 | addview 16 | } = require('../controller/blogController') 17 | const { 18 | SuccessModel, 19 | ErrorModel 20 | } = require('../model/resModel') 21 | 22 | // blog列表 / 搜索 23 | router.get('/list', async (ctx, next) => { 24 | // res.render('index', { title: 'Express' }); 25 | let author = ctx.request.body.author || '' 26 | let keyword = ctx.request.body.keyword || '' 27 | const listData = await getList(author, keyword) 28 | ctx.body = new SuccessModel(listData) 29 | }); 30 | 31 | // 得到自己的博客 32 | router.post('/getMyArticle',loginCheck, async (ctx, next) =>{ 33 | let username = ctx.session.username 34 | const listData = await getMyArticle(username) 35 | if (listData) { 36 | ctx.body = new SuccessModel(listData) 37 | } else { 38 | ctx.body = new ErrorModel("获取博客失败") 39 | } 40 | 41 | }) 42 | 43 | // blog详情 44 | router.post('/detail', async (ctx, next) => { 45 | // res.render('index', { title: 'Express' }); 46 | let blogId = ctx.request.body.blogId || '' 47 | console.log('-------------------------',blogId) 48 | const resulistData = await getBlogDetail(blogId) 49 | console.log('-------------------',resulistData); 50 | ctx.body = new SuccessModel(resulistData) 51 | }); 52 | 53 | 54 | // 添加 55 | router.post('/addArticle',loginCheck, async (ctx, next) => { 56 | let { title, content, link, author_id, markdown,type } = getBlogDetail.body 57 | const result = await addArticle(title, content, link, author_id,markdown,type) 58 | ctx.body = new SuccessModel(result) 59 | }) 60 | 61 | // 得到需要编辑的博客的详情 62 | router.post('/getEditArticle', loginCheck,async (ctx, next) => { 63 | let blogId = ctx.request.body.blogId || '' 64 | const result = await getEditArticle(blogId) 65 | ctx.body = new SuccessModel(result) 66 | }) 67 | 68 | // 编辑博客 (更新) 69 | router.post('/editArticle', loginCheck,async (ctx, next) => { 70 | let blogId = ctx.request.body.blogId || '' 71 | 72 | let { 73 | title, 74 | detail, 75 | type, 76 | link, 77 | markdown 78 | } = ctx.request.body 79 | console.log('type', type) 80 | const result = await editArticle(blogId, title, detail, type, link, markdown) 81 | ctx.body = new SuccessModel(result) 82 | // return result.then(val => { 83 | // if (val) { 84 | // res.json( 85 | // new SuccessModel('更新博客成功') 86 | // ) 87 | // } else { 88 | // res.json( 89 | // new ErrorModel('编辑博客失败') 90 | // ) 91 | // } 92 | // }) 93 | }) 94 | 95 | // 删除 96 | router.post('/delArticle', loginCheck, async (ctx, next) => { 97 | let blogId = ctx.request.body.blogId 98 | const result = await delArticle(blogId) 99 | ctx.body = new SuccessModel(result) 100 | // return result.then(val => { 101 | // if (val) { 102 | // res.json( 103 | // new SuccessModel('删除博客成功') 104 | // ) 105 | // } else { 106 | // res.json( 107 | // new ErrorModel('删除博客失败') 108 | // ) 109 | // } 110 | // }) 111 | }) 112 | 113 | 114 | // 添加浏览人数 115 | router.post('/addview',async (ctx,next) => { 116 | let blogId = ctx.request.body.blogId 117 | const result = await addview(blogId) 118 | ctx.body = new SuccessModel(result) 119 | // return result.then(val => { 120 | // if (val) { 121 | // res.json( 122 | // new SuccessModel() 123 | // ) 124 | // } else { 125 | // res.json( 126 | // new ErrorModel() 127 | // ) 128 | // } 129 | // }).catch(err => { 130 | // console.log(err) 131 | // }) 132 | }) 133 | 134 | 135 | 136 | module.exports = router; -------------------------------------------------------------------------------- /backend/koa2-api/routes/index.js: -------------------------------------------------------------------------------- 1 | const router = require('koa-router')() 2 | 3 | router.get('/', async (ctx, next) => { 4 | await ctx.render('index', { 5 | title: 'Hello Koa 2!' 6 | }) 7 | }) 8 | 9 | router.get('/string', async (ctx, next) => { 10 | ctx.body = 'koa2 string' 11 | }) 12 | 13 | router.get('/json', async (ctx, next) => { 14 | ctx.body = { 15 | title: 'koa2 json' 16 | } 17 | }) 18 | 19 | module.exports = router 20 | -------------------------------------------------------------------------------- /backend/koa2-api/routes/user.js: -------------------------------------------------------------------------------- 1 | const router = require('koa-router')() 2 | 3 | const { 4 | login, 5 | userInfo 6 | } = require('../controller/userController') 7 | const { 8 | SuccessModel, 9 | ErrorModel 10 | } = require('../model/resModel') 11 | 12 | router.prefix('/api/user') 13 | 14 | 15 | // 登录 16 | router.post('/login', async (ctx, next) => { 17 | const { 18 | username, 19 | password 20 | } = ctx.request.body 21 | // console.log(ctx.request) 22 | const result = await login(username, password) 23 | if (result.username) { 24 | // 设置 session 25 | // ctx.session = {} 26 | ctx.session.username = result.username; // 登录成功,设置 session 27 | ctx.session.realname = result.realname 28 | console.log('ctx:', ctx.session) 29 | // ctx.session.realname = data.realname 30 | ctx.body = new SuccessModel('登录成功') 31 | } else { 32 | ctx.body = new ErrorModel('登录失败') 33 | } 34 | 35 | }); 36 | 37 | // 退出 38 | router.get('/exit', async (ctx, next) => { 39 | let result = delete ctx.session.username 40 | // console.log('exit:', result) 41 | if (result) { 42 | ctx.body = new SuccessModel('退出成功') 43 | 44 | } else { 45 | 46 | ctx.body = new SuccessModel('退出失败') 47 | 48 | } 49 | }); 50 | 51 | // 登录验证 52 | router.get('/loginCheck', async (ctx, next) => { 53 | if (ctx.session.username) { 54 | const result = await userInfo(ctx.session.username) 55 | if(result){ 56 | result.isLogin = true 57 | ctx.body = new SuccessModel(result) 58 | } 59 | } else { 60 | ctx.body = new ErrorModel('未登录') 61 | } 62 | 63 | }) 64 | 65 | 66 | 67 | // 测试session 68 | router.get('/session-test', async (ctx, next) => { 69 | if (ctx.session.viewCount == null) { 70 | ctx.session.viewCount = 0 71 | } 72 | ctx.session.viewCount++ 73 | ctx.body = { 74 | errno: 0, 75 | viewCount: ctx.session.viewCount 76 | } 77 | }) 78 | 79 | module.exports = router -------------------------------------------------------------------------------- /backend/koa2-api/utils/cryps.js: -------------------------------------------------------------------------------- 1 | const crypto = require('crypto') 2 | 3 | // 密匙 4 | const SECRET_KEY = '?WJiol_8776#' 5 | 6 | // 加密函数 7 | function md5(content) { 8 | let md5 = crypto.createHash('md5') 9 | return md5.update(content).digest('hex') 10 | } 11 | 12 | // 生成加密后的密码 13 | function genPassword(password) { 14 | const str = `password=${password}&key=${SECRET_KEY}` 15 | return md5(str) 16 | } 17 | 18 | // console.log(genPassword(123)) 19 | module.exports = { 20 | genPassword 21 | } -------------------------------------------------------------------------------- /backend/koa2-api/utils/data.js: -------------------------------------------------------------------------------- 1 | // 将时间戳转换为时间格式 2 | function timestampToTime(timestamp) { 3 | var date = new Date(timestamp); 4 | var Y = date.getFullYear() + '-'; 5 | var M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-'; 6 | var D = (date.getDate() < 10 ? '0' + date.getDate() : date.getDate()) + ' '; 7 | var h = (date.getHours() < 10 ? '0' + date.getHours() : date.getHours()) + ':'; 8 | var m = (date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes()) + ':'; 9 | var s = (date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds()); 10 | return Y + M + D + h + m + s; 11 | } 12 | 13 | module.exports = { 14 | timestampToTime 15 | } -------------------------------------------------------------------------------- /backend/koa2-api/views/error.pug: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | h1= message 5 | h2= error.status 6 | pre #{error.stack} 7 | -------------------------------------------------------------------------------- /backend/koa2-api/views/index.pug: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | h1= title 5 | p Welcome to #{title} 6 | -------------------------------------------------------------------------------- /backend/koa2-api/views/layout.pug: -------------------------------------------------------------------------------- 1 | doctype html 2 | html 3 | head 4 | title= title 5 | link(rel='stylesheet', href='/stylesheets/style.css') 6 | body 7 | block content 8 | -------------------------------------------------------------------------------- /backend/node/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | /dist/ 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | /test/unit/coverage/ 8 | /test/e2e/reports/ 9 | selenium-debug.log 10 | 11 | # Editor directories and files 12 | .idea 13 | .vscode 14 | *.suo 15 | *.ntvs* 16 | *.njsproj 17 | *.sln 18 | -------------------------------------------------------------------------------- /backend/node/app.js: -------------------------------------------------------------------------------- 1 | var createError = require('http-errors'); 2 | var express = require('express'); 3 | var path = require('path'); 4 | var cookieParser = require('cookie-parser'); 5 | var logger = require('morgan'); 6 | const session = require('express-session') 7 | const RedisStore = require('connect-redis')(session) 8 | 9 | 10 | // var indexRouter = require('./routes/index'); 11 | const userRouter = require('./routes/user'); 12 | const blogRouter = require('./routes/blog'); 13 | 14 | var app = express(); 15 | 16 | // view engine setup 17 | // app.set('views', path.join(__dirname, 'views')); 18 | // app.set('view engine', 'jade'); 19 | 20 | app.use(logger('dev')); 21 | app.use(express.json()); 22 | app.use(express.urlencoded({ extended: false })); 23 | app.use(cookieParser()); 24 | // app.use(express.static(path.join(__dirname, 'public'))); 25 | 26 | // 链接redis 27 | const redisClient = require('./db/redis') 28 | const sessionStore = new RedisStore({ 29 | client: redisClient 30 | }) 31 | // 使用 session 中间件 32 | app.use(session({ 33 | secret: 'XHiol#23123_', 34 | cookie: { 35 | // path: '/', // 默认配置 36 | // httpOnly: true, // 默认配置 37 | maxAge: 24 * 60 * 60 * 1000 38 | }, 39 | // session 存储到redis 40 | store: sessionStore 41 | })) 42 | 43 | // app.use('/', indexRouter); 44 | app.use('/api/user', userRouter); 45 | app.use('/api/blog', blogRouter); 46 | 47 | // catch 404 and forward to error handler 48 | // app.use(function(req, res, next) { 49 | // next(createError(404)); 50 | // }); 51 | 52 | // error handler 53 | app.use(function(err, req, res, next) { 54 | // set locals, only providing error in development 55 | res.locals.message = err.message; 56 | res.locals.error = req.app.get('env') === 'dev' ? err : {}; 57 | 58 | // render the error page 59 | res.status(err.status || 500); 60 | res.render('error'); 61 | }); 62 | 63 | 64 | module.exports = app; 65 | -------------------------------------------------------------------------------- /backend/node/bin/www: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var app = require('../app'); 8 | var debug = require('debug')('node:server'); 9 | var http = require('http'); 10 | 11 | /** 12 | * Get port from environment and store in Express. 13 | */ 14 | 15 | var port = normalizePort(process.env.PORT || '8000'); 16 | app.set('port', port); 17 | 18 | /** 19 | * Create HTTP server. 20 | */ 21 | 22 | var server = http.createServer(app); 23 | 24 | /** 25 | * Listen on provided port, on all network interfaces. 26 | */ 27 | 28 | server.listen(port); 29 | server.on('error', onError); 30 | server.on('listening', onListening); 31 | 32 | /** 33 | * Normalize a port into a number, string, or false. 34 | */ 35 | 36 | function normalizePort(val) { 37 | var port = parseInt(val, 10); 38 | 39 | if (isNaN(port)) { 40 | // named pipe 41 | return val; 42 | } 43 | 44 | if (port >= 0) { 45 | // port number 46 | return port; 47 | } 48 | 49 | return false; 50 | } 51 | 52 | /** 53 | * Event listener for HTTP server "error" event. 54 | */ 55 | 56 | function onError(error) { 57 | if (error.syscall !== 'listen') { 58 | throw error; 59 | } 60 | 61 | var bind = typeof port === 'string' 62 | ? 'Pipe ' + port 63 | : 'Port ' + port; 64 | 65 | // handle specific listen errors with friendly messages 66 | switch (error.code) { 67 | case 'EACCES': 68 | console.error(bind + ' requires elevated privileges'); 69 | process.exit(1); 70 | break; 71 | case 'EADDRINUSE': 72 | console.error(bind + ' is already in use'); 73 | process.exit(1); 74 | break; 75 | default: 76 | throw error; 77 | } 78 | } 79 | 80 | /** 81 | * Event listener for HTTP server "listening" event. 82 | */ 83 | 84 | function onListening() { 85 | var addr = server.address(); 86 | var bind = typeof addr === 'string' 87 | ? 'pipe ' + addr 88 | : 'port ' + addr.port; 89 | debug('Listening on ' + bind); 90 | } 91 | -------------------------------------------------------------------------------- /backend/node/config/db.js: -------------------------------------------------------------------------------- 1 | const env = process.env.NODE_ENV // 环境参数 2 | console.log(env) 3 | 4 | // 配置 5 | let MYSQL_CONF 6 | let REDIS_CONF 7 | 8 | if (env === 'dev') { 9 | // mysql 10 | MYSQL_CONF = { 11 | host: 'localhost', 12 | user: 'xingheng', 13 | password: '123456', 14 | port: '3306', 15 | database: 'db_myblog' 16 | } 17 | 18 | // redis 19 | REDIS_CONF = { 20 | port: 6379, 21 | host: '127.0.0.1' 22 | } 23 | } 24 | 25 | if (env === 'production') { 26 | // mysql 27 | MYSQL_CONF = { 28 | host: 'localhost', 29 | user: 'xingheng', 30 | password: '123456', 31 | port: '3306', 32 | database: 'db_myblog' 33 | } 34 | 35 | // redis 36 | REDIS_CONF = { 37 | port: 6379, 38 | host: '127.0.0.1' 39 | } 40 | } 41 | 42 | module.exports = { 43 | MYSQL_CONF, 44 | REDIS_CONF 45 | } -------------------------------------------------------------------------------- /backend/node/controller/blogController.js: -------------------------------------------------------------------------------- 1 | const { 2 | exec, 3 | escape 4 | } = require('./../db/mysql') 5 | const { timestampToTime } = require('../utils/data') 6 | // 得到博客推荐列表 7 | const getList = (author, keyword) => { 8 | let sql = 9 | `select * , tb_blogs.id as articleid 10 | from tb_blogs, tb_users 11 | where tb_blogs.author_id=tb_users.id 12 | and tb_blogs.show=1 13 | ` 14 | if (author) { 15 | author = escape(author) 16 | sql += `and author=${author} ` 17 | } 18 | if (keyword) { 19 | keyword = escape(keyword) 20 | sql += `and title like %${keyword}% ` 21 | } 22 | sql += `order by tb_blogs.createtime desc;` 23 | console.log(sql) 24 | // 返回 promise 25 | return exec(sql) 26 | } 27 | 28 | // 得到自己的博客 29 | const getMyArticle = (username) => { 30 | let sql = 31 | ` 32 | select *, tb_blogs.id as articleid 33 | from tb_blogs, tb_users 34 | where tb_blogs.author_id=tb_users.id 35 | and tb_users.username= '${username}' 36 | and tb_blogs.show=1 37 | order by tb_blogs.createtime desc; 38 | ` 39 | // console.log(sql) 40 | return exec(sql).then(rows => { 41 | return rows || {} 42 | }) 43 | 44 | } 45 | // 得到博客详情 46 | const getBlogDetail = (blogId) => { 47 | let sql = 48 | `select * 49 | from tb_blogs, tb_users 50 | where tb_blogs.author_id=tb_users.id 51 | and tb_blogs.id='${blogId}' 52 | ` 53 | // console.log(sql) 54 | // 返回 promise 55 | return exec(sql) 56 | } 57 | // 得到需要编辑的博客的详情 58 | const getEditArticle = (blogId) => { 59 | let sql = 60 | ` 61 | select * from tb_blogs where id = ${blogId} 62 | ` 63 | // console.log(sql) 64 | return exec(sql).then(rows => { 65 | return rows[0] || {} 66 | }) 67 | 68 | } 69 | // 编辑博客 70 | const editArticle = (blogId, title, detail, type, link, markdown) => { 71 | detail = escape(detail) 72 | markdown = escape(markdown) 73 | 74 | title = escape(title) 75 | // console.log(typeof(type),type) 76 | if (type === 0 + '') { 77 | link = '' 78 | } 79 | link = escape(link) 80 | let sql = 81 | ` 82 | update tb_blogs 83 | set 84 | title = ${title}, 85 | content =${detail}, 86 | type = ${type}, 87 | link = ${link}, 88 | markdown =${markdown} 89 | where id = ${blogId} 90 | ` 91 | console.log(sql) 92 | return exec(sql).then(updateData => { 93 | // console.log('updateData is ', updateData) 94 | if (updateData.affectedRows > 0) { 95 | return true 96 | } 97 | return false 98 | }) 99 | } 100 | 101 | // 添加博客 102 | const addArticle = (title, content, link, author_id,markdown,type) => { 103 | content = escape(content) 104 | markdown = escape(markdown) 105 | title = escape(title) 106 | link = escape(link) 107 | let createtime = timestampToTime(Date.now()) 108 | let sql = 109 | ` 110 | insert into tb_blogs (title, content, link, createtime, author_id, markdown, type) 111 | values (${title}, ${content}, ${link}, '${createtime}', '${author_id}', ${markdown}, '${type}'); 112 | ` 113 | console.log(sql) 114 | return exec(sql).then(insertData => { 115 | // console.log('insertData is ', insertData) 116 | return { 117 | id: insertData.insertId 118 | } 119 | }) 120 | } 121 | 122 | // 删除博客 123 | const delArticle = (blogId) => { 124 | // id 就是要删除博客的 id 125 | // const sql = `delete from tb_blogs where id='${id}' and author='${author}';` 126 | // return exec(sql).then(delData => { 127 | // // console.log('delData is ', delData) 128 | // if (delData.affectedRows > 0) { 129 | // return true 130 | // } 131 | // return false 132 | // }) 133 | 134 | // 软删除 135 | let sql = 136 | ` 137 | update tb_blogs 138 | set 139 | tb_blogs.show = 0 140 | where id = ${blogId} 141 | ` 142 | console.log(sql) 143 | return exec(sql).then(updateData => { 144 | // console.log('updateData is ', updateData) 145 | if (updateData.affectedRows > 0) { 146 | return true 147 | } 148 | return false 149 | }) 150 | } 151 | 152 | // 浏览人数添加 153 | const addview = (blogId) => { 154 | let sql = 155 | ` 156 | update tb_blogs 157 | set 158 | tb_blogs.preview = tb_blogs.preview + 1 159 | where id = ${blogId} 160 | ` 161 | console.log(sql) 162 | return exec(sql).then(updateData => { 163 | // console.log('updateData is ', updateData) 164 | if (updateData.affectedRows > 0) { 165 | return true 166 | } 167 | return false 168 | }) 169 | } 170 | 171 | module.exports = { 172 | getList, 173 | getBlogDetail, 174 | getEditArticle, 175 | editArticle, 176 | getMyArticle, 177 | addArticle, 178 | delArticle, 179 | addview 180 | } -------------------------------------------------------------------------------- /backend/node/controller/userController.js: -------------------------------------------------------------------------------- 1 | const { exec, escape } = require('../db/mysql') 2 | const { genPassword } = require('../utils/cryps') 3 | 4 | // 登录 5 | const login = (username, password) => { 6 | username = escape(username) 7 | 8 | // 生成加密密码 9 | password = genPassword(password) 10 | password = escape(password) 11 | 12 | const sql = 13 | ` 14 | select * from tb_users where username=${username} and password=${password} 15 | ` 16 | // console.log('sql is', sql) 17 | return exec(sql).then(rows => { 18 | return rows[0] || {} 19 | }) 20 | } 21 | 22 | 23 | 24 | // 用户信息 25 | const userInfo = (username) => { 26 | username = escape(username) 27 | const sql = 28 | ` 29 | select tb_users.id ,username from tb_users where username=${username} 30 | ` 31 | // console.log('sql is', sql) 32 | return exec(sql).then(rows => { 33 | return rows[0] || {} 34 | }) 35 | } 36 | 37 | module.exports = { 38 | login, 39 | userInfo 40 | } -------------------------------------------------------------------------------- /backend/node/db/mysql.js: -------------------------------------------------------------------------------- 1 | const mysql = require('mysql') 2 | const { MYSQL_CONF } = require('../config/db') 3 | 4 | // 创建链接对象 5 | const con = mysql.createConnection(MYSQL_CONF) 6 | 7 | // 开始链接 8 | con.connect() 9 | 10 | // 统一执行 sql 的函数 11 | function exec(sql) { 12 | const promise = new Promise((resolve, reject) => { 13 | con.query(sql, (err, result) => { 14 | if (err) { 15 | reject(err) 16 | return 17 | } 18 | resolve(result) 19 | }) 20 | }) 21 | return promise 22 | } 23 | 24 | module.exports = { 25 | exec, 26 | escape: mysql.escape 27 | } -------------------------------------------------------------------------------- /backend/node/db/redis.js: -------------------------------------------------------------------------------- 1 | const redis = require('redis') 2 | const { REDIS_CONF } = require('../config/db.js') 3 | 4 | // 创建客户端 5 | const redisClient = redis.createClient(REDIS_CONF.port, REDIS_CONF.host) 6 | redisClient.on('error', err => { 7 | console.error(err) 8 | }) 9 | 10 | module.exports = redisClient -------------------------------------------------------------------------------- /backend/node/middleware/loginCheck.js: -------------------------------------------------------------------------------- 1 | const { ErrorModel } = require('../model/resModel') 2 | 3 | module.exports = (req, res, next) => { 4 | if (req.session.username) { 5 | next() 6 | return 7 | } 8 | res.json( 9 | new ErrorModel('未登录') 10 | ) 11 | } -------------------------------------------------------------------------------- /backend/node/middleware/readme.md: -------------------------------------------------------------------------------- 1 | # 这个文件夹放置自己定义的中间件 -------------------------------------------------------------------------------- /backend/node/model/resModel.js: -------------------------------------------------------------------------------- 1 | class BaseModel { 2 | constructor(data, message) { 3 | if (typeof data === 'string') { 4 | this.message = data 5 | data = null 6 | message = null 7 | } 8 | if (data) { 9 | this.data = data 10 | } 11 | if (message) { 12 | this.message = message 13 | } 14 | } 15 | } 16 | 17 | class SuccessModel extends BaseModel { 18 | constructor(data, message) { 19 | super(data, message) 20 | this.errno = 0 21 | } 22 | } 23 | 24 | class ErrorModel extends BaseModel { 25 | constructor(data, message) { 26 | super(data, message) 27 | this.errno = -1 28 | } 29 | } 30 | 31 | module.exports = { 32 | SuccessModel, 33 | ErrorModel 34 | } -------------------------------------------------------------------------------- /backend/node/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "node ./bin/www", 7 | "dev": "cross-env NODE_ENV=dev nodemon ./bin/www", 8 | "prd": "cross-env NODE_ENV=production pm2 start ./bin/www" 9 | }, 10 | "dependencies": { 11 | "connect-redis": "^3.4.1", 12 | "cookie-parser": "~1.4.4", 13 | "crypto": "^1.0.1", 14 | "debug": "~2.6.9", 15 | "express": "~4.16.1", 16 | "express-session": "^1.16.2", 17 | "http-errors": "~1.6.3", 18 | "jade": "~1.11.0", 19 | "morgan": "~1.9.1", 20 | "mysql": "^2.17.1", 21 | "redis": "^2.8.0" 22 | }, 23 | "devDependencies": { 24 | "cross-env": "^5.2.0", 25 | "nodemon": "^1.19.1" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /backend/node/routes/blog.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | // 登录验证中间件 4 | const loginCheck = require('../middleware/loginCheck') 5 | const { 6 | getList, 7 | getBlogDetail, 8 | getEditArticle, 9 | editArticle, 10 | getMyArticle, 11 | addArticle, 12 | delArticle, 13 | addview 14 | } = require('../controller/blogController') 15 | const { 16 | SuccessModel, 17 | ErrorModel 18 | } = require('../model/resModel') 19 | 20 | // blog列表 / 搜索 21 | router.get('/list', (req, res, next) => { 22 | // res.render('index', { title: 'Express' }); 23 | let author = req.body.author || '' 24 | let keyword = req.body.keyword || '' 25 | const result = getList(author, keyword) 26 | return result.then(listData => { 27 | res.json( 28 | new SuccessModel(listData) 29 | ) 30 | }) 31 | }); 32 | 33 | // 得到自己的博客 34 | router.post('/getMyArticle',loginCheck, (req, res, next) =>{ 35 | let username = req.session.username 36 | const result = getMyArticle(username) 37 | return result.then(listData => { 38 | res.json( 39 | new SuccessModel(listData) 40 | ) 41 | }) 42 | }) 43 | 44 | // blog详情 45 | router.post('/detail', (req, res, next) => { 46 | // res.render('index', { title: 'Express' }); 47 | let blogId = req.body.blogId || '' 48 | const result = getBlogDetail(blogId) 49 | return result.then(listData => { 50 | res.json( 51 | new SuccessModel(listData[0]) 52 | ) 53 | }) 54 | }); 55 | 56 | 57 | // 添加 58 | router.post('/addArticle',loginCheck, (req, res, next) => { 59 | let { title, content, link, author_id, markdown,type } = req.body 60 | const result = addArticle(title, content, link, author_id,markdown,type) 61 | return result.then(listData=> { 62 | res.json( 63 | new SuccessModel(listData,'添加文章成功') 64 | ) 65 | }).catch(err=>{ 66 | res.json( 67 | new ErrorModel(err,'添加文章失败') 68 | ) 69 | }) 70 | }) 71 | 72 | // 得到需要编辑的博客的详情 73 | router.post('/getEditArticle', loginCheck, (req, res, next) => { 74 | let blogId = req.body.blogId || '' 75 | const result = getEditArticle(blogId) 76 | return result.then(listData => { 77 | res.json( 78 | new SuccessModel(listData) 79 | ) 80 | }) 81 | }) 82 | 83 | // 编辑博客 (更新) 84 | router.post('/editArticle', loginCheck, (req, res, next) => { 85 | let blogId = req.body.blogId || '' 86 | 87 | let { 88 | title, 89 | detail, 90 | type, 91 | link, 92 | markdown 93 | } = req.body 94 | console.log('type', type) 95 | const result = editArticle(blogId, title, detail, type, link, markdown) 96 | return result.then(val => { 97 | if (val) { 98 | res.json( 99 | new SuccessModel('更新博客成功') 100 | ) 101 | } else { 102 | res.json( 103 | new ErrorModel('编辑博客失败') 104 | ) 105 | } 106 | }) 107 | }) 108 | 109 | // 删除 110 | router.post('/delArticle', loginCheck, (req, res, next) => { 111 | let blogId = req.body.blogId 112 | const result = delArticle(blogId) 113 | return result.then(val => { 114 | if (val) { 115 | res.json( 116 | new SuccessModel('删除博客成功') 117 | ) 118 | } else { 119 | res.json( 120 | new ErrorModel('删除博客失败') 121 | ) 122 | } 123 | }) 124 | }) 125 | 126 | 127 | // 添加浏览人数 128 | router.post('/addview', (req,res,next) => { 129 | let blogId = req.body.blogId 130 | const result = addview(blogId) 131 | return result.then(val => { 132 | if (val) { 133 | res.json( 134 | new SuccessModel() 135 | ) 136 | } else { 137 | res.json( 138 | new ErrorModel() 139 | ) 140 | } 141 | }).catch(err => { 142 | console.log(err) 143 | }) 144 | }) 145 | 146 | 147 | 148 | module.exports = router; -------------------------------------------------------------------------------- /backend/node/routes/user.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | const { 4 | login, 5 | userInfo 6 | } = require('../controller/userController') 7 | const { 8 | SuccessModel, 9 | ErrorModel 10 | } = require('../model/resModel') 11 | 12 | // 登录 13 | router.post('/login', function (req, res, next) { 14 | const { 15 | username, 16 | password 17 | } = req.body 18 | const result = login(username, password) 19 | return result.then(data => { 20 | if (data.username) { 21 | // 设置 session 22 | // req.session = {} 23 | req.session.username = data.username; // 登录成功,设置 session 24 | req.session.realname = data.realname 25 | console.log(req.session) 26 | // req.session.realname = data.realname 27 | res.json( 28 | new SuccessModel('登录成功') 29 | ) 30 | return 31 | } 32 | res.json( 33 | new ErrorModel('登录失败') 34 | ) 35 | }) 36 | }); 37 | 38 | // 退出 39 | router.get('/exit', function (req, res, next) { 40 | let result = delete req.session.username 41 | console.log('exit:', result) 42 | if (result) { 43 | res.json( 44 | new SuccessModel('退出成功') 45 | ); 46 | } else { 47 | res.json( 48 | new SuccessModel('退出失败') 49 | ); 50 | } 51 | }); 52 | 53 | // // 测试session 54 | // router.get('/session-test', (req, res, next) => { 55 | // let session = req.session 56 | // console.log('session is :', session) 57 | // }) 58 | 59 | // 登录验证 60 | router.get('/loginCheck', (req, res, next) => { 61 | if (req.session.username) { 62 | const result = userInfo(req.session.username) 63 | return result.then(data => { 64 | data.isLogin = true 65 | res.json( 66 | new SuccessModel(data) 67 | ) 68 | }) 69 | } 70 | res.json( 71 | new ErrorModel('未登录') 72 | ) 73 | }) 74 | 75 | 76 | 77 | module.exports = router; -------------------------------------------------------------------------------- /backend/node/utils/cryps.js: -------------------------------------------------------------------------------- 1 | const crypto = require('crypto') 2 | 3 | // 密匙 4 | const SECRET_KEY = '?WJiol_8776#' 5 | 6 | // 加密函数 7 | function md5(content) { 8 | let md5 = crypto.createHash('md5') 9 | return md5.update(content).digest('hex') 10 | } 11 | 12 | // 生成加密后的密码 13 | function genPassword(password) { 14 | const str = `password=${password}&key=${SECRET_KEY}` 15 | return md5(str) 16 | } 17 | 18 | // console.log(genPassword(123)) 19 | module.exports = { 20 | genPassword 21 | } -------------------------------------------------------------------------------- /backend/node/utils/data.js: -------------------------------------------------------------------------------- 1 | // 将时间戳转换为时间格式 2 | function timestampToTime(timestamp) { 3 | var date = new Date(timestamp); 4 | var Y = date.getFullYear() + '-'; 5 | var M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-'; 6 | var D = (date.getDate() < 10 ? '0' + date.getDate() : date.getDate()) + ' '; 7 | var h = (date.getHours() < 10 ? '0' + date.getHours() : date.getHours()) + ':'; 8 | var m = (date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes()) + ':'; 9 | var s = (date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds()); 10 | return Y + M + D + h + m + s; 11 | } 12 | 13 | module.exports = { 14 | timestampToTime 15 | } -------------------------------------------------------------------------------- /config.md: -------------------------------------------------------------------------------- 1 | # 此处主要是 -------------------------------------------------------------------------------- /frontend/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["env", { 4 | "modules": false, 5 | "targets": { 6 | "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] 7 | } 8 | }], 9 | "stage-2" 10 | ], 11 | "plugins": ["transform-vue-jsx", "transform-runtime"], 12 | "env": { 13 | "test": { 14 | "presets": ["env", "stage-2"], 15 | "plugins": ["transform-vue-jsx", "transform-es2015-modules-commonjs", "dynamic-import-node"] 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /frontend/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /frontend/.eslintignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | /config/ 3 | /dist/ 4 | /*.js 5 | /test/unit/coverage/ 6 | /src/ 7 | -------------------------------------------------------------------------------- /frontend/.eslintrc.js: -------------------------------------------------------------------------------- 1 | // https://eslint.org/docs/user-guide/configuring 2 | 3 | module.exports = { 4 | root: true, 5 | parserOptions: { 6 | parser: 'babel-eslint' 7 | }, 8 | env: { 9 | browser: true, 10 | }, 11 | extends: [ 12 | // https://github.com/vuejs/eslint-plugin-vue#priority-a-essential-error-prevention 13 | // consider switching to `plugin:vue/strongly-recommended` or `plugin:vue/recommended` for stricter rules. 14 | 'plugin:vue/essential', 15 | // https://github.com/standard/standard/blob/master/docs/RULES-en.md 16 | 'standard' 17 | ], 18 | // required to lint *.vue files 19 | plugins: [ 20 | 'vue' 21 | ], 22 | // add your custom rules here 23 | rules: { 24 | // allow async-await 25 | 'generator-star-spacing': 'off', 26 | // allow debugger during development 27 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off' 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /node_modules/ 3 | /dist/ 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | /test/unit/coverage/ 8 | /test/e2e/reports/ 9 | selenium-debug.log 10 | 11 | # Editor directories and files 12 | .idea 13 | .vscode 14 | *.suo 15 | *.ntvs* 16 | *.njsproj 17 | *.sln 18 | -------------------------------------------------------------------------------- /frontend/.postcssrc.js: -------------------------------------------------------------------------------- 1 | // https://github.com/michael-ciniawsky/postcss-load-config 2 | 3 | module.exports = { 4 | "plugins": { 5 | "postcss-import": {}, 6 | "postcss-url": {}, 7 | // to edit target browsers: use "browserslist" field in package.json 8 | "autoprefixer": {} 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /frontend/README.md: -------------------------------------------------------------------------------- 1 | # notfound 2 | 3 | > 一个简单的个人博客项目 4 | 5 | ## 项目结构 6 | ``` 7 | # 前端 8 | - vue 9 | # element-ui 10 | # 后端 (暂未确定) 11 | - node 12 | # thinkphp框架 13 | or 14 | - php (不希望) 15 | # thinkphp框架 16 | # 数据库 17 | - mysql 18 | ``` 19 | 20 | 21 | ## Build Setup 22 | 23 | ``` bash 24 | # install dependencies 25 | npm install 26 | 27 | # serve with hot reload at localhost:8080 28 | npm run dev 29 | 30 | # build for production with minification 31 | npm run build 32 | 33 | # build for production and view the bundle analyzer report 34 | npm run build --report 35 | 36 | # run unit tests 37 | npm run unit 38 | 39 | # run e2e tests 40 | npm run e2e 41 | 42 | # run all tests 43 | npm test 44 | ``` 45 | 46 | For a detailed explanation on how things work, check out the [guide](http://vuejs-templates.github.io/webpack/) and [docs for vue-loader](http://vuejs.github.io/vue-loader). 47 | -------------------------------------------------------------------------------- /frontend/build/build.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | require('./check-versions')() 3 | 4 | process.env.NODE_ENV = 'production' 5 | 6 | const ora = require('ora') 7 | const rm = require('rimraf') 8 | const path = require('path') 9 | const chalk = require('chalk') 10 | const webpack = require('webpack') 11 | const config = require('../config') 12 | const webpackConfig = require('./webpack.prod.conf') 13 | 14 | const spinner = ora('building for production...') 15 | spinner.start() 16 | 17 | rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => { 18 | if (err) throw err 19 | webpack(webpackConfig, (err, stats) => { 20 | spinner.stop() 21 | if (err) throw err 22 | process.stdout.write(stats.toString({ 23 | colors: true, 24 | modules: false, 25 | children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build. 26 | chunks: false, 27 | chunkModules: false 28 | }) + '\n\n') 29 | 30 | if (stats.hasErrors()) { 31 | console.log(chalk.red(' Build failed with errors.\n')) 32 | process.exit(1) 33 | } 34 | 35 | console.log(chalk.cyan(' Build complete.\n')) 36 | console.log(chalk.yellow( 37 | ' Tip: built files are meant to be served over an HTTP server.\n' + 38 | ' Opening index.html over file:// won\'t work.\n' 39 | )) 40 | }) 41 | }) 42 | -------------------------------------------------------------------------------- /frontend/build/check-versions.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const chalk = require('chalk') 3 | const semver = require('semver') 4 | const packageConfig = require('../package.json') 5 | const shell = require('shelljs') 6 | 7 | function exec (cmd) { 8 | return require('child_process').execSync(cmd).toString().trim() 9 | } 10 | 11 | const versionRequirements = [ 12 | { 13 | name: 'node', 14 | currentVersion: semver.clean(process.version), 15 | versionRequirement: packageConfig.engines.node 16 | } 17 | ] 18 | 19 | if (shell.which('npm')) { 20 | versionRequirements.push({ 21 | name: 'npm', 22 | currentVersion: exec('npm --version'), 23 | versionRequirement: packageConfig.engines.npm 24 | }) 25 | } 26 | 27 | module.exports = function () { 28 | const warnings = [] 29 | 30 | for (let i = 0; i < versionRequirements.length; i++) { 31 | const mod = versionRequirements[i] 32 | 33 | if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) { 34 | warnings.push(mod.name + ': ' + 35 | chalk.red(mod.currentVersion) + ' should be ' + 36 | chalk.green(mod.versionRequirement) 37 | ) 38 | } 39 | } 40 | 41 | if (warnings.length) { 42 | console.log('') 43 | console.log(chalk.yellow('To use this template, you must update following to modules:')) 44 | console.log() 45 | 46 | for (let i = 0; i < warnings.length; i++) { 47 | const warning = warnings[i] 48 | console.log(' ' + warning) 49 | } 50 | 51 | console.log() 52 | process.exit(1) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /frontend/build/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HunterXing/404blog/b34a5cea3e46303f75aded08f35d2240fd586225/frontend/build/logo.png -------------------------------------------------------------------------------- /frontend/build/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const path = require('path') 3 | const config = require('../config') 4 | const ExtractTextPlugin = require('extract-text-webpack-plugin') 5 | const packageConfig = require('../package.json') 6 | 7 | exports.assetsPath = function (_path) { 8 | const assetsSubDirectory = process.env.NODE_ENV === 'production' 9 | ? config.build.assetsSubDirectory 10 | : config.dev.assetsSubDirectory 11 | 12 | return path.posix.join(assetsSubDirectory, _path) 13 | } 14 | 15 | exports.cssLoaders = function (options) { 16 | options = options || {} 17 | 18 | const cssLoader = { 19 | loader: 'css-loader', 20 | options: { 21 | sourceMap: options.sourceMap 22 | } 23 | } 24 | 25 | const postcssLoader = { 26 | loader: 'postcss-loader', 27 | options: { 28 | sourceMap: options.sourceMap 29 | } 30 | } 31 | 32 | // generate loader string to be used with extract text plugin 33 | function generateLoaders (loader, loaderOptions) { 34 | const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader] 35 | 36 | if (loader) { 37 | loaders.push({ 38 | loader: loader + '-loader', 39 | options: Object.assign({}, loaderOptions, { 40 | sourceMap: options.sourceMap 41 | }) 42 | }) 43 | } 44 | 45 | // Extract CSS when that option is specified 46 | // (which is the case during production build) 47 | if (options.extract) { 48 | return ExtractTextPlugin.extract({ 49 | use: loaders, 50 | fallback: 'vue-style-loader' 51 | }) 52 | } else { 53 | return ['vue-style-loader'].concat(loaders) 54 | } 55 | } 56 | 57 | // https://vue-loader.vuejs.org/en/configurations/extract-css.html 58 | return { 59 | css: generateLoaders(), 60 | postcss: generateLoaders(), 61 | less: generateLoaders('less'), 62 | sass: generateLoaders('sass', { indentedSyntax: true }), 63 | scss: generateLoaders('sass'), 64 | stylus: generateLoaders('stylus'), 65 | styl: generateLoaders('stylus') 66 | } 67 | } 68 | 69 | // Generate loaders for standalone style files (outside of .vue) 70 | exports.styleLoaders = function (options) { 71 | const output = [] 72 | const loaders = exports.cssLoaders(options) 73 | 74 | for (const extension in loaders) { 75 | const loader = loaders[extension] 76 | output.push({ 77 | test: new RegExp('\\.' + extension + '$'), 78 | use: loader 79 | }) 80 | } 81 | 82 | return output 83 | } 84 | 85 | exports.createNotifierCallback = () => { 86 | const notifier = require('node-notifier') 87 | 88 | return (severity, errors) => { 89 | if (severity !== 'error') return 90 | 91 | const error = errors[0] 92 | const filename = error.file && error.file.split('!').pop() 93 | 94 | notifier.notify({ 95 | title: packageConfig.name, 96 | message: severity + ': ' + error.name, 97 | subtitle: filename || '', 98 | icon: path.join(__dirname, 'logo.png') 99 | }) 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /frontend/build/vue-loader.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const utils = require('./utils') 3 | const config = require('../config') 4 | const isProduction = process.env.NODE_ENV === 'production' 5 | const sourceMapEnabled = isProduction 6 | ? config.build.productionSourceMap 7 | : config.dev.cssSourceMap 8 | 9 | module.exports = { 10 | loaders: utils.cssLoaders({ 11 | sourceMap: sourceMapEnabled, 12 | extract: isProduction 13 | }), 14 | cssSourceMap: sourceMapEnabled, 15 | cacheBusting: config.dev.cacheBusting, 16 | transformToRequire: { 17 | video: ['src', 'poster'], 18 | source: 'src', 19 | img: 'src', 20 | image: 'xlink:href' 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /frontend/build/webpack.base.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const path = require('path') 3 | const utils = require('./utils') 4 | const config = require('../config') 5 | const vueLoaderConfig = require('./vue-loader.conf') 6 | 7 | function resolve (dir) { 8 | return path.join(__dirname, '..', dir) 9 | } 10 | 11 | const createLintingRule = () => ({ 12 | test: /\.(js|vue)$/, 13 | loader: 'eslint-loader', 14 | enforce: 'pre', 15 | include: [resolve('src'), resolve('test')], 16 | options: { 17 | formatter: require('eslint-friendly-formatter'), 18 | emitWarning: !config.dev.showEslintErrorsInOverlay 19 | } 20 | }) 21 | 22 | module.exports = { 23 | context: path.resolve(__dirname, '../'), 24 | entry: { 25 | app: './src/main.js' 26 | }, 27 | output: { 28 | path: config.build.assetsRoot, 29 | filename: '[name].js', 30 | publicPath: process.env.NODE_ENV === 'production' 31 | ? config.build.assetsPublicPath 32 | : config.dev.assetsPublicPath 33 | }, 34 | resolve: { 35 | extensions: ['.js', '.vue', '.json'], 36 | alias: { 37 | 'vue$': 'vue/dist/vue.esm.js', 38 | '@': resolve('src'), 39 | 'common': resolve('src/common'), 40 | 'style': resolve('src/assets/style') 41 | } 42 | }, 43 | module: { 44 | rules: [ 45 | ...(config.dev.useEslint ? [createLintingRule()] : []), 46 | { 47 | test: /\.vue$/, 48 | loader: 'vue-loader', 49 | options: vueLoaderConfig 50 | }, 51 | { 52 | test: /\.js$/, 53 | loader: 'babel-loader', 54 | include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')] 55 | }, 56 | { 57 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, 58 | loader: 'url-loader', 59 | options: { 60 | limit: 10000, 61 | name: utils.assetsPath('img/[name].[hash:7].[ext]') 62 | } 63 | }, 64 | { 65 | test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, 66 | loader: 'url-loader', 67 | options: { 68 | limit: 10000, 69 | name: utils.assetsPath('media/[name].[hash:7].[ext]') 70 | } 71 | }, 72 | { 73 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, 74 | loader: 'url-loader', 75 | options: { 76 | limit: 10000, 77 | name: utils.assetsPath('fonts/[name].[hash:7].[ext]') 78 | } 79 | } 80 | ] 81 | }, 82 | node: { 83 | // prevent webpack from injecting useless setImmediate polyfill because Vue 84 | // source contains it (although only uses it if it's native). 85 | setImmediate: false, 86 | // prevent webpack from injecting mocks to Node native modules 87 | // that does not make sense for the client 88 | dgram: 'empty', 89 | fs: 'empty', 90 | net: 'empty', 91 | tls: 'empty', 92 | child_process: 'empty' 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /frontend/build/webpack.dev.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const utils = require('./utils') 3 | const webpack = require('webpack') 4 | const config = require('../config') 5 | const merge = require('webpack-merge') 6 | const path = require('path') 7 | const baseWebpackConfig = require('./webpack.base.conf') 8 | const CopyWebpackPlugin = require('copy-webpack-plugin') 9 | const HtmlWebpackPlugin = require('html-webpack-plugin') 10 | const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') 11 | const portfinder = require('portfinder') 12 | 13 | const HOST = process.env.HOST 14 | const PORT = process.env.PORT && Number(process.env.PORT) 15 | 16 | const devWebpackConfig = merge(baseWebpackConfig, { 17 | module: { 18 | rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true }) 19 | }, 20 | // cheap-module-eval-source-map is faster for development 21 | devtool: config.dev.devtool, 22 | 23 | // these devServer options should be customized in /config/index.js 24 | devServer: { 25 | clientLogLevel: 'warning', 26 | historyApiFallback: { 27 | rewrites: [ 28 | { from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') }, 29 | ], 30 | }, 31 | hot: true, 32 | contentBase: false, // since we use CopyWebpackPlugin. 33 | compress: true, 34 | host: HOST || config.dev.host, 35 | port: PORT || config.dev.port, 36 | open: config.dev.autoOpenBrowser, 37 | overlay: config.dev.errorOverlay 38 | ? { warnings: false, errors: true } 39 | : false, 40 | publicPath: config.dev.assetsPublicPath, 41 | proxy: config.dev.proxyTable, 42 | quiet: true, // necessary for FriendlyErrorsPlugin 43 | watchOptions: { 44 | poll: config.dev.poll, 45 | } 46 | }, 47 | plugins: [ 48 | new webpack.DefinePlugin({ 49 | 'process.env': require('../config/dev.env') 50 | }), 51 | new webpack.HotModuleReplacementPlugin(), 52 | new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update. 53 | new webpack.NoEmitOnErrorsPlugin(), 54 | // https://github.com/ampedandwired/html-webpack-plugin 55 | new HtmlWebpackPlugin({ 56 | filename: 'index.html', 57 | template: 'index.html', 58 | favicon: path.resolve('./favicon.ico'), 59 | inject: true 60 | }), 61 | // copy custom static assets 62 | new CopyWebpackPlugin([ 63 | { 64 | from: path.resolve(__dirname, '../static'), 65 | to: config.dev.assetsSubDirectory, 66 | ignore: ['.*'] 67 | } 68 | ]) 69 | ] 70 | }) 71 | 72 | module.exports = new Promise((resolve, reject) => { 73 | portfinder.basePort = process.env.PORT || config.dev.port 74 | portfinder.getPort((err, port) => { 75 | if (err) { 76 | reject(err) 77 | } else { 78 | // publish the new Port, necessary for e2e tests 79 | process.env.PORT = port 80 | // add port to devServer config 81 | devWebpackConfig.devServer.port = port 82 | 83 | // Add FriendlyErrorsPlugin 84 | devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({ 85 | compilationSuccessInfo: { 86 | messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`], 87 | }, 88 | onErrors: config.dev.notifyOnErrors 89 | ? utils.createNotifierCallback() 90 | : undefined 91 | })) 92 | 93 | resolve(devWebpackConfig) 94 | } 95 | }) 96 | }) 97 | -------------------------------------------------------------------------------- /frontend/build/webpack.prod.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const path = require('path') 3 | const utils = require('./utils') 4 | const webpack = require('webpack') 5 | const config = require('../config') 6 | const merge = require('webpack-merge') 7 | const baseWebpackConfig = require('./webpack.base.conf') 8 | const CopyWebpackPlugin = require('copy-webpack-plugin') 9 | const HtmlWebpackPlugin = require('html-webpack-plugin') 10 | const ExtractTextPlugin = require('extract-text-webpack-plugin') 11 | const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin') 12 | const UglifyJsPlugin = require('uglifyjs-webpack-plugin') 13 | 14 | const env = process.env.NODE_ENV === 'testing' ? 15 | require('../config/test.env') : 16 | require('../config/prod.env') 17 | 18 | const webpackConfig = merge(baseWebpackConfig, { 19 | module: { 20 | rules: utils.styleLoaders({ 21 | sourceMap: config.build.productionSourceMap, 22 | extract: true, 23 | usePostCSS: true 24 | }) 25 | }, 26 | devtool: config.build.productionSourceMap ? config.build.devtool : false, 27 | output: { 28 | path: config.build.assetsRoot, 29 | filename: utils.assetsPath('js/[name].[chunkhash].js'), 30 | chunkFilename: utils.assetsPath('js/[id].[chunkhash].js') 31 | }, 32 | plugins: [ 33 | // http://vuejs.github.io/vue-loader/en/workflow/production.html 34 | new webpack.DefinePlugin({ 35 | 'process.env': env 36 | }), 37 | new UglifyJsPlugin({ 38 | uglifyOptions: { 39 | compress: { 40 | warnings: false 41 | } 42 | }, 43 | sourceMap: config.build.productionSourceMap, 44 | parallel: true 45 | }), 46 | // extract css into its own file 47 | new ExtractTextPlugin({ 48 | filename: utils.assetsPath('css/[name].[contenthash].css'), 49 | // Setting the following option to `false` will not extract CSS from codesplit chunks. 50 | // Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack. 51 | // It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`, 52 | // increasing file size: https://github.com/vuejs-templates/webpack/issues/1110 53 | allChunks: true, 54 | }), 55 | // Compress extracted CSS. We are using this plugin so that possible 56 | // duplicated CSS from different components can be deduped. 57 | new OptimizeCSSPlugin({ 58 | cssProcessorOptions: { 59 | safe: true, 60 | map: { 61 | inline: false 62 | }, 63 | // 添加对autoprefixer的配置 64 | autoprefixer: { 65 | remove: false 66 | } 67 | } 68 | }), 69 | // generate dist index.html with correct asset hash for caching. 70 | // you can customize output by editing /index.html 71 | // see https://github.com/ampedandwired/html-webpack-plugin 72 | new HtmlWebpackPlugin({ 73 | filename: process.env.NODE_ENV === 'testing' ? 74 | 'index.html' : config.build.index, 75 | template: 'index.html', 76 | favicon: path.resolve('./favicon.ico'), 77 | inject: true, 78 | minify: { 79 | removeComments: true, 80 | collapseWhitespace: true, 81 | removeAttributeQuotes: true 82 | // more options: 83 | // https://github.com/kangax/html-minifier#options-quick-reference 84 | }, 85 | // necessary to consistently work with multiple chunks via CommonsChunkPlugin 86 | chunksSortMode: 'dependency' 87 | }), 88 | // keep module.id stable when vendor modules does not change 89 | new webpack.HashedModuleIdsPlugin(), 90 | // enable scope hoisting 91 | new webpack.optimize.ModuleConcatenationPlugin(), 92 | // split vendor js into its own file 93 | new webpack.optimize.CommonsChunkPlugin({ 94 | name: 'vendor', 95 | minChunks(module) { 96 | // any required modules inside node_modules are extracted to vendor 97 | return ( 98 | module.resource && 99 | /\.js$/.test(module.resource) && 100 | module.resource.indexOf( 101 | path.join(__dirname, '../node_modules') 102 | ) === 0 103 | ) 104 | } 105 | }), 106 | // extract webpack runtime and module manifest to its own file in order to 107 | // prevent vendor hash from being updated whenever app bundle is updated 108 | new webpack.optimize.CommonsChunkPlugin({ 109 | name: 'manifest', 110 | minChunks: Infinity 111 | }), 112 | // This instance extracts shared chunks from code splitted chunks and bundles them 113 | // in a separate chunk, similar to the vendor chunk 114 | // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk 115 | new webpack.optimize.CommonsChunkPlugin({ 116 | name: 'app', 117 | async: 'vendor-async', 118 | children: true, 119 | minChunks: 3 120 | }), 121 | 122 | // copy custom static assets 123 | new CopyWebpackPlugin([{ 124 | from: path.resolve(__dirname, '../static'), 125 | to: config.build.assetsSubDirectory, 126 | ignore: ['.*'] 127 | }]) 128 | ] 129 | }) 130 | 131 | if (config.build.productionGzip) { 132 | const CompressionWebpackPlugin = require('compression-webpack-plugin') 133 | 134 | webpackConfig.plugins.push( 135 | new CompressionWebpackPlugin({ 136 | asset: '[path].gz[query]', 137 | algorithm: 'gzip', 138 | test: new RegExp( 139 | '\\.(' + 140 | config.build.productionGzipExtensions.join('|') + 141 | ')$' 142 | ), 143 | threshold: 10240, 144 | minRatio: 0.8 145 | }) 146 | ) 147 | } 148 | 149 | if (config.build.bundleAnalyzerReport) { 150 | const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin 151 | webpackConfig.plugins.push(new BundleAnalyzerPlugin()) 152 | } 153 | 154 | module.exports = webpackConfig 155 | -------------------------------------------------------------------------------- /frontend/config/dev.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const merge = require('webpack-merge') 3 | const prodEnv = require('./prod.env') 4 | 5 | module.exports = merge(prodEnv, { 6 | NODE_ENV: '"development"' 7 | }) 8 | -------------------------------------------------------------------------------- /frontend/config/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | // Template version: 1.3.1 3 | // see http://vuejs-templates.github.io/webpack for documentation. 4 | 5 | const path = require('path') 6 | 7 | module.exports = { 8 | dev: { 9 | 10 | // Paths 11 | assetsSubDirectory: 'static', 12 | assetsPublicPath: '/', 13 | proxyTable: { 14 | // // php 后台API 15 | // '/phpApi': { 16 | // target: 'http://212.64.25.152', 17 | // // true允许跨域 18 | // changeOrigin: true 19 | // // pathRewrite: { 20 | // // // 需要rewrite重写的, 如果在服务器端做了处理则可以不要这段 21 | // // '^/phpApi': '/php' 22 | // // } 23 | // }, 24 | // node后台API 25 | // '/api': { 26 | // target: 'http://localhost:8000', 27 | // // true允许跨域 28 | // changeOrigin: true 29 | // // pathRewrite: { 30 | // // // rewrite重写的 31 | // // '^/nodeApi': '' 32 | // // } 33 | // } 34 | // // 服务器根目录下的接口 35 | // // '/interface': { 36 | // // target: 'http://localhost/', 37 | // // changeOrigin: true, 38 | // // pathRewrite: { 39 | // // '^/interface': '/api' 40 | // // } 41 | // // }, 42 | // // '/api': { 43 | // // target: 'http://localhost:8080', 44 | // // pathRewrite: { 45 | // // '^/api': 'static/mock/' 46 | // // } 47 | // // } 48 | }, 49 | 50 | // Various Dev Server settings 51 | host: '0.0.0.0', // can be overwritten by process.env.HOST 52 | port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined 53 | autoOpenBrowser: false, 54 | errorOverlay: true, 55 | notifyOnErrors: true, 56 | poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions- 57 | 58 | // Use Eslint Loader? 59 | // If true, your code will be linted during bundling and 60 | // linting errors and warnings will be shown in the console. 61 | useEslint: true, 62 | // If true, eslint errors and warnings will also be shown in the error overlay 63 | // in the browser. 64 | showEslintErrorsInOverlay: false, 65 | 66 | /** 67 | * Source Maps那匹马 68 | */ 69 | 70 | // https://webpack.js.org/configuration/devtool/#development 71 | devtool: 'cheap-module-eval-source-map', 72 | 73 | // If you have problems debugging vue-files in devtools, 74 | // set this to false - it *may* help 75 | // https://vue-loader.vuejs.org/en/options.html#cachebusting 76 | cacheBusting: true, 77 | 78 | cssSourceMap: true 79 | }, 80 | 81 | build: { 82 | // Template for index.html 83 | index: path.resolve(__dirname, '../dist/index.html'), 84 | 85 | // Paths 86 | assetsRoot: path.resolve(__dirname, '../dist'), 87 | assetsSubDirectory: 'static', 88 | assetsPublicPath: '/', 89 | 90 | /** 91 | * Source Maps 92 | */ 93 | 94 | productionSourceMap: true, 95 | // https://webpack.js.org/configuration/devtool/#production 96 | devtool: '#source-map', 97 | 98 | // Gzip off by default as many popular static hosts such as 99 | // Surge or Netlify already gzip all static assets for you. 100 | // Before setting to `true`, make sure to: 101 | // npm install --save-dev compression-webpack-plugin 102 | productionGzip: false, 103 | productionGzipExtensions: ['js', 'css'], 104 | 105 | // Run the build command with an extra argument to 106 | // View the bundle analyzer report after build finishes: 107 | // `npm run build --report` 108 | // Set to `true` or `false` to always turn it on or off 109 | bundleAnalyzerReport: process.env.npm_config_report 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /frontend/config/prod.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | module.exports = { 3 | NODE_ENV: '"production"' 4 | } 5 | -------------------------------------------------------------------------------- /frontend/config/test.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const merge = require('webpack-merge') 3 | const devEnv = require('./dev.env') 4 | 5 | module.exports = merge(devEnv, { 6 | NODE_ENV: '"testing"' 7 | }) 8 | -------------------------------------------------------------------------------- /frontend/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HunterXing/404blog/b34a5cea3e46303f75aded08f35d2240fd586225/frontend/favicon.ico -------------------------------------------------------------------------------- /frontend/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | notfound 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "notfound", 3 | "version": "1.0.0", 4 | "description": "A Vue.js project", 5 | "author": "xingheng <15555306200@163.com>", 6 | "private": true, 7 | "scripts": { 8 | "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js", 9 | "start": "npm run dev", 10 | "unit": "jest --config test/unit/jest.conf.js --coverage", 11 | "e2e": "node test/e2e/runner.js", 12 | "test": "npm run unit && npm run e2e", 13 | "lint": "eslint --ext .js,.vue src test/unit test/e2e/specs", 14 | "build": "node build/build.js" 15 | }, 16 | "dependencies": { 17 | "ajv": "^5.0.0", 18 | "axios": "^0.18.0", 19 | "element-ui": "^2.10.0", 20 | "mavon-editor": "^2.7.4", 21 | "node-sass": "^4.12.0", 22 | "qs": "^6.7.0", 23 | "sass-loader": "^7.1.0", 24 | "stylus": "^0.54.5", 25 | "stylus-loader": "^3.0.2", 26 | "vue": "^2.5.2", 27 | "vue-lazyload": "^1.2.6", 28 | "vue-router": "^3.0.1", 29 | "vuex": "^3.1.1" 30 | }, 31 | "devDependencies": { 32 | "autoprefixer": "^7.1.2", 33 | "babel-core": "^6.22.1", 34 | "babel-eslint": "^8.2.1", 35 | "babel-helper-vue-jsx-merge-props": "^2.0.3", 36 | "babel-jest": "^21.0.2", 37 | "babel-loader": "^7.1.1", 38 | "babel-plugin-dynamic-import-node": "^1.2.0", 39 | "babel-plugin-syntax-jsx": "^6.18.0", 40 | "babel-plugin-transform-es2015-modules-commonjs": "^6.26.0", 41 | "babel-plugin-transform-runtime": "^6.22.0", 42 | "babel-plugin-transform-vue-jsx": "^3.5.0", 43 | "babel-preset-env": "^1.3.2", 44 | "babel-preset-stage-2": "^6.22.0", 45 | "babel-register": "^6.22.0", 46 | "chalk": "^2.0.1", 47 | "chromedriver": "^2.27.2", 48 | "copy-webpack-plugin": "^4.0.1", 49 | "cross-spawn": "^5.0.1", 50 | "css-loader": "^0.28.0", 51 | "es6-promise": "^4.2.6", 52 | "eslint": "^4.15.0", 53 | "eslint-config-standard": "^10.2.1", 54 | "eslint-friendly-formatter": "^3.0.0", 55 | "eslint-loader": "^1.7.1", 56 | "eslint-plugin-import": "^2.7.0", 57 | "eslint-plugin-node": "^5.2.0", 58 | "eslint-plugin-promise": "^3.4.0", 59 | "eslint-plugin-standard": "^3.0.1", 60 | "eslint-plugin-vue": "^4.0.0", 61 | "extract-text-webpack-plugin": "^3.0.0", 62 | "file-loader": "^1.1.4", 63 | "friendly-errors-webpack-plugin": "^1.6.1", 64 | "html-webpack-plugin": "^2.30.1", 65 | "jest": "^22.0.4", 66 | "jest-serializer-vue": "^0.3.0", 67 | "nightwatch": "^0.9.12", 68 | "node-notifier": "^5.1.2", 69 | "optimize-css-assets-webpack-plugin": "^3.2.0", 70 | "ora": "^1.2.0", 71 | "portfinder": "^1.0.13", 72 | "postcss-import": "^11.0.0", 73 | "postcss-loader": "^2.0.8", 74 | "postcss-url": "^7.2.1", 75 | "rimraf": "^2.6.0", 76 | "selenium-server": "^3.0.1", 77 | "semver": "^5.3.0", 78 | "shelljs": "^0.7.6", 79 | "uglifyjs-webpack-plugin": "^1.1.1", 80 | "url-loader": "^0.5.8", 81 | "vue-jest": "^1.0.2", 82 | "vue-loader": "^13.3.0", 83 | "vue-style-loader": "^3.0.1", 84 | "vue-template-compiler": "^2.5.2", 85 | "webpack": "^3.6.0", 86 | "webpack-bundle-analyzer": "^2.9.0", 87 | "webpack-dev-server": "^2.9.1", 88 | "webpack-merge": "^4.1.0" 89 | }, 90 | "engines": { 91 | "node": ">= 6.0.0", 92 | "npm": ">= 3.0.0" 93 | }, 94 | "browserslist": [ 95 | "> 1%", 96 | "last 2 versions", 97 | "not ie <= 8" 98 | ] 99 | } 100 | -------------------------------------------------------------------------------- /frontend/src/assets/images/BlogLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HunterXing/404blog/b34a5cea3e46303f75aded08f35d2240fd586225/frontend/src/assets/images/BlogLogo.png -------------------------------------------------------------------------------- /frontend/src/assets/images/aritcle-pic1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HunterXing/404blog/b34a5cea3e46303f75aded08f35d2240fd586225/frontend/src/assets/images/aritcle-pic1.jpg -------------------------------------------------------------------------------- /frontend/src/assets/images/aritcle-pic2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HunterXing/404blog/b34a5cea3e46303f75aded08f35d2240fd586225/frontend/src/assets/images/aritcle-pic2.jpg -------------------------------------------------------------------------------- /frontend/src/assets/images/aritcle-pic3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HunterXing/404blog/b34a5cea3e46303f75aded08f35d2240fd586225/frontend/src/assets/images/aritcle-pic3.jpg -------------------------------------------------------------------------------- /frontend/src/assets/images/bg-summer-day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HunterXing/404blog/b34a5cea3e46303f75aded08f35d2240fd586225/frontend/src/assets/images/bg-summer-day.png -------------------------------------------------------------------------------- /frontend/src/assets/images/noData.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HunterXing/404blog/b34a5cea3e46303f75aded08f35d2240fd586225/frontend/src/assets/images/noData.png -------------------------------------------------------------------------------- /frontend/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HunterXing/404blog/b34a5cea3e46303f75aded08f35d2240fd586225/frontend/src/assets/logo.png -------------------------------------------------------------------------------- /frontend/src/assets/readme.md: -------------------------------------------------------------------------------- 1 | ### 这里是资源文件 2 | -------------------------------------------------------------------------------- /frontend/src/assets/style/iconfont.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'iconfont'; /* project id 1186029 */ 3 | src: url('//at.alicdn.com/t/font_1186029_8ceryprb1ad.eot'); 4 | src: url('//at.alicdn.com/t/font_1186029_8ceryprb1ad.eot?#iefix') format('embedded-opentype'), 5 | url('//at.alicdn.com/t/font_1186029_8ceryprb1ad.woff2') format('woff2'), 6 | url('//at.alicdn.com/t/font_1186029_8ceryprb1ad.woff') format('woff'), 7 | url('//at.alicdn.com/t/font_1186029_8ceryprb1ad.ttf') format('truetype'), 8 | url('//at.alicdn.com/t/font_1186029_8ceryprb1ad.svg#iconfont') format('svg'); 9 | } 10 | .iconfont { 11 | font-family: "iconfont" !important; 12 | font-size: 16px; 13 | font-style: normal; 14 | -webkit-font-smoothing: antialiased; 15 | -moz-osx-font-smoothing: grayscale; 16 | } 17 | 18 | .icon-pinglun:before { 19 | content: "\e625"; 20 | } 21 | 22 | .icon-nocheck:before { 23 | content: "\e641"; 24 | } 25 | 26 | .icon-bijiben:before { 27 | content: "\e6ba"; 28 | } 29 | 30 | .icon-youhuiquan01:before { 31 | content: "\e637"; 32 | } 33 | 34 | .icon-jiantouarrow486:before { 35 | content: "\e6aa"; 36 | } 37 | 38 | .icon-zhuye:before { 39 | content: "\e61b"; 40 | } 41 | 42 | .icon-qianjin:before { 43 | content: "\e74e"; 44 | } 45 | 46 | .icon-iconfontdongtai:before { 47 | content: "\e62d"; 48 | } 49 | 50 | .icon-shouye:before { 51 | content: "\e651"; 52 | } 53 | 54 | .icon-bijinotes23:before { 55 | content: "\e60f"; 56 | } 57 | 58 | .icon-xiazai6:before { 59 | content: "\e611"; 60 | } 61 | 62 | .icon-wode:before { 63 | content: "\e645"; 64 | } 65 | 66 | .icon-leibie:before { 67 | content: "\e65c"; 68 | } 69 | 70 | .icon-bijinotes231:before { 71 | content: "\e610"; 72 | } 73 | 74 | .icon-bianjibijishishouxie:before { 75 | content: "\e69b"; 76 | } 77 | 78 | .icon-weibo:before { 79 | content: "\e630"; 80 | } 81 | 82 | .icon-jiantouzuo:before { 83 | content: "\e613"; 84 | } 85 | 86 | .icon-pengyouquan:before { 87 | content: "\e63f"; 88 | } 89 | 90 | .icon-sousuo1:before { 91 | content: "\e647"; 92 | } 93 | 94 | .icon-jiantou:before { 95 | content: "\e622"; 96 | } 97 | 98 | .icon-cuowu:before { 99 | content: "\e676"; 100 | } 101 | 102 | .icon-cuowu1:before { 103 | content: "\e631"; 104 | } 105 | 106 | .icon-linedesign-03:before { 107 | content: "\e66e"; 108 | } 109 | 110 | .icon-xingbienv:before { 111 | content: "\e606"; 112 | } 113 | 114 | .icon-xingbienan:before { 115 | content: "\e607"; 116 | } 117 | 118 | .icon-jibiji:before { 119 | content: "\e60a"; 120 | } 121 | 122 | .icon-gouwuche1:before { 123 | content: "\e64f"; 124 | } 125 | 126 | .icon-weixin:before { 127 | content: "\e73b"; 128 | } 129 | 130 | .icon-QQ:before { 131 | content: "\e73e"; 132 | } 133 | 134 | .icon-shezhi:before { 135 | content: "\e60b"; 136 | } 137 | 138 | .icon-weibiaoti--:before { 139 | content: "\e601"; 140 | } 141 | 142 | .icon-liebiao:before { 143 | content: "\e655"; 144 | } 145 | 146 | .icon-qianbi:before { 147 | content: "\ea09"; 148 | } 149 | 150 | .icon-close:before { 151 | content: "\e65a"; 152 | } 153 | 154 | .icon-icon-test:before { 155 | content: "\e666"; 156 | } 157 | 158 | .icon-tianjiahaoyou:before { 159 | content: "\e638"; 160 | } 161 | 162 | .icon-lishi:before { 163 | content: "\e608"; 164 | } 165 | 166 | .icon-fanhui1:before { 167 | content: "\e626"; 168 | } 169 | 170 | .icon-sousuo2:before { 171 | content: "\e632"; 172 | } 173 | 174 | .icon-yincangmima:before { 175 | content: "\e612"; 176 | } 177 | 178 | .icon-gengduodiandiandian:before { 179 | content: "\e61d"; 180 | } 181 | 182 | .icon-gouwuche:before { 183 | content: "\e609"; 184 | } 185 | 186 | .icon-biji:before { 187 | content: "\e672"; 188 | } 189 | 190 | .icon-jiahao:before { 191 | content: "\e61f"; 192 | } 193 | 194 | .icon-fabu:before { 195 | content: "\e615"; 196 | } 197 | 198 | .icon-jianhao:before { 199 | content: "\e620"; 200 | } 201 | 202 | .icon-xitongxiaoxi:before { 203 | content: "\e652"; 204 | } 205 | 206 | .icon-tubiaolunkuo-:before { 207 | content: "\e618"; 208 | } 209 | 210 | .icon-qiabao:before { 211 | content: "\e61e"; 212 | } 213 | 214 | .icon-weibiaoti-_huabanfuben:before { 215 | content: "\e61a"; 216 | } 217 | 218 | .icon-qian:before { 219 | content: "\e639"; 220 | } 221 | 222 | .icon-fenxiang1:before { 223 | content: "\e6b7"; 224 | } 225 | 226 | .icon-cha:before { 227 | content: "\e619"; 228 | } 229 | 230 | .icon-xuexizhongxin:before { 231 | content: "\e63d"; 232 | } 233 | 234 | .icon-fenxiang2:before { 235 | content: "\e614"; 236 | } 237 | 238 | .icon-ziyuan:before { 239 | content: "\e635"; 240 | } 241 | 242 | .icon-guolvqi:before { 243 | content: "\e70e"; 244 | } 245 | 246 | .icon-bofang:before { 247 | content: "\ead8"; 248 | } 249 | 250 | .icon-liebiao1:before { 251 | content: "\eaf8"; 252 | } 253 | 254 | .icon-faxian:before { 255 | content: "\e63b"; 256 | } 257 | 258 | .icon-shoucang:before { 259 | content: "\e687"; 260 | } 261 | 262 | .icon-xiaoxi1:before { 263 | content: "\e61c"; 264 | } 265 | 266 | .icon-sousuo:before { 267 | content: "\e60e"; 268 | } 269 | 270 | .icon-faxian_:before { 271 | content: "\e6e1"; 272 | } 273 | 274 | .icon-tubiaozhizuo-:before { 275 | content: "\e60c"; 276 | } 277 | 278 | .icon-tu_pian:before { 279 | content: "\e624"; 280 | } 281 | 282 | .icon-faxian1:before { 283 | content: "\e629"; 284 | } 285 | 286 | .icon-fanhui:before { 287 | content: "\e60d"; 288 | } 289 | 290 | .icon-jiahao2:before { 291 | content: "\e6ab"; 292 | } 293 | 294 | .icon-good_active-copy:before { 295 | content: "\e600"; 296 | } 297 | 298 | .icon-lishijilu:before { 299 | content: "\e605"; 300 | } 301 | 302 | .icon-lishijilu1:before { 303 | content: "\e718"; 304 | } 305 | 306 | .icon-dingdan:before { 307 | content: "\e604"; 308 | } 309 | 310 | .icon-wode1:before { 311 | content: "\e602"; 312 | } 313 | 314 | .icon-jibiji1:before { 315 | content: "\e681"; 316 | } 317 | 318 | .icon-xinxi:before { 319 | content: "\e633"; 320 | } 321 | 322 | .icon-fenxiang:before { 323 | content: "\e6a4"; 324 | } 325 | 326 | -------------------------------------------------------------------------------- /frontend/src/assets/style/main.scss: -------------------------------------------------------------------------------- 1 | @import './pagecss/myfooter.scss'; 2 | -------------------------------------------------------------------------------- /frontend/src/assets/style/markdown.styl: -------------------------------------------------------------------------------- 1 | 2 | .markdown-body { 3 | -ms-text-size-adjust: 100%; 4 | -webkit-text-size-adjust: 100%; 5 | line-height: 1.5; 6 | color: #24292e; 7 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; 8 | font-size: 16px; 9 | line-height: 1.5; 10 | word-wrap: break-word; 11 | } 12 | 13 | .markdown-body .pl-c { 14 | color: #6a737d; 15 | } 16 | 17 | .markdown-body .pl-c1, 18 | .markdown-body .pl-s .pl-v { 19 | color: #005cc5; 20 | } 21 | 22 | .markdown-body .pl-e, 23 | .markdown-body .pl-en { 24 | color: #6f42c1; 25 | } 26 | 27 | .markdown-body .pl-smi, 28 | .markdown-body .pl-s .pl-s1 { 29 | color: #24292e; 30 | } 31 | 32 | .markdown-body .pl-ent { 33 | color: #22863a; 34 | } 35 | 36 | .markdown-body .pl-k { 37 | color: #d73a49; 38 | } 39 | 40 | .markdown-body .pl-s, 41 | .markdown-body .pl-pds, 42 | .markdown-body .pl-s .pl-pse .pl-s1, 43 | .markdown-body .pl-sr, 44 | .markdown-body .pl-sr .pl-cce, 45 | .markdown-body .pl-sr .pl-sre, 46 | .markdown-body .pl-sr .pl-sra { 47 | color: #032f62; 48 | } 49 | 50 | .markdown-body .pl-v, 51 | .markdown-body .pl-smw { 52 | color: #e36209; 53 | } 54 | 55 | .markdown-body .pl-bu { 56 | color: #b31d28; 57 | } 58 | 59 | .markdown-body .pl-ii { 60 | color: #fafbfc; 61 | background-color: #b31d28; 62 | } 63 | 64 | .markdown-body .pl-c2 { 65 | color: #fafbfc; 66 | background-color: #d73a49; 67 | } 68 | 69 | .markdown-body .pl-c2::before { 70 | content: "^M"; 71 | } 72 | 73 | .markdown-body .pl-sr .pl-cce { 74 | font-weight: bold; 75 | color: #22863a; 76 | } 77 | 78 | .markdown-body .pl-ml { 79 | color: #735c0f; 80 | } 81 | 82 | .markdown-body .pl-mh, 83 | .markdown-body .pl-mh .pl-en, 84 | .markdown-body .pl-ms { 85 | font-weight: bold; 86 | color: #005cc5; 87 | } 88 | 89 | .markdown-body .pl-mi { 90 | font-style: italic; 91 | color: #24292e; 92 | } 93 | 94 | .markdown-body .pl-mb { 95 | font-weight: bold; 96 | color: #24292e; 97 | } 98 | 99 | .markdown-body .pl-md { 100 | color: #b31d28; 101 | background-color: #ffeef0; 102 | } 103 | 104 | .markdown-body .pl-mi1 { 105 | color: #22863a; 106 | background-color: #f0fff4; 107 | } 108 | 109 | .markdown-body .pl-mc { 110 | color: #e36209; 111 | background-color: #ffebda; 112 | } 113 | 114 | .markdown-body .pl-mi2 { 115 | color: #f6f8fa; 116 | background-color: #005cc5; 117 | } 118 | 119 | .markdown-body .pl-mdr { 120 | font-weight: bold; 121 | color: #6f42c1; 122 | } 123 | 124 | .markdown-body .pl-ba { 125 | color: #586069; 126 | } 127 | 128 | .markdown-body .pl-sg { 129 | color: #959da5; 130 | } 131 | 132 | .markdown-body .pl-corl { 133 | text-decoration: underline; 134 | color: #032f62; 135 | } 136 | 137 | .markdown-body .octicon { 138 | display: inline-block; 139 | vertical-align: text-top; 140 | fill: currentColor; 141 | } 142 | 143 | .markdown-body a { 144 | background-color: transparent; 145 | -webkit-text-decoration-skip: objects; 146 | } 147 | 148 | .markdown-body a:active, 149 | .markdown-body a:hover { 150 | outline-width: 0; 151 | } 152 | 153 | .markdown-body strong { 154 | font-weight: inherit; 155 | } 156 | 157 | .markdown-body strong { 158 | font-weight: bolder; 159 | } 160 | 161 | .markdown-body h1 { 162 | font-size: 2em; 163 | margin: 0.67em 0; 164 | } 165 | 166 | .markdown-body img { 167 | border-style: none; 168 | } 169 | 170 | .markdown-body svg:not(:root) { 171 | overflow: hidden; 172 | } 173 | 174 | .markdown-body code, 175 | .markdown-body kbd, 176 | .markdown-body pre { 177 | font-family: monospace, monospace; 178 | font-size: 1em; 179 | } 180 | 181 | .markdown-body hr { 182 | box-sizing: content-box; 183 | height: 0; 184 | overflow: visible; 185 | } 186 | 187 | .markdown-body input { 188 | font: inherit; 189 | margin: 0; 190 | } 191 | 192 | .markdown-body input { 193 | overflow: visible; 194 | } 195 | 196 | .markdown-body [type="checkbox"] { 197 | box-sizing: border-box; 198 | padding: 0; 199 | } 200 | 201 | .markdown-body * { 202 | box-sizing: border-box; 203 | } 204 | 205 | .markdown-body input { 206 | font-family: inherit; 207 | font-size: inherit; 208 | line-height: inherit; 209 | } 210 | 211 | .markdown-body a { 212 | color: #0366d6; 213 | text-decoration: none; 214 | } 215 | 216 | .markdown-body a:hover { 217 | text-decoration: underline; 218 | } 219 | 220 | .markdown-body strong { 221 | font-weight: 600; 222 | } 223 | 224 | .markdown-body hr { 225 | height: 0; 226 | margin: 15px 0; 227 | overflow: hidden; 228 | background: transparent; 229 | border: 0; 230 | border-bottom: 1px solid #dfe2e5; 231 | } 232 | 233 | .markdown-body hr::before { 234 | display: table; 235 | content: ""; 236 | } 237 | 238 | .markdown-body hr::after { 239 | display: table; 240 | clear: both; 241 | content: ""; 242 | } 243 | 244 | .markdown-body table { 245 | border-spacing: 0; 246 | border-collapse: collapse; 247 | } 248 | 249 | .markdown-body td, 250 | .markdown-body th { 251 | padding: 0; 252 | } 253 | 254 | .markdown-body h1, 255 | .markdown-body h2, 256 | .markdown-body h3, 257 | .markdown-body h4, 258 | .markdown-body h5, 259 | .markdown-body h6 { 260 | margin-top: 0; 261 | margin-bottom: 0; 262 | } 263 | 264 | .markdown-body h1 { 265 | font-size: 32px; 266 | font-weight: 600; 267 | } 268 | 269 | .markdown-body h2 { 270 | font-size: 24px; 271 | font-weight: 600; 272 | } 273 | 274 | .markdown-body h3 { 275 | font-size: 20px; 276 | font-weight: 600; 277 | } 278 | 279 | .markdown-body h4 { 280 | font-size: 16px; 281 | font-weight: 600; 282 | } 283 | 284 | .markdown-body h5 { 285 | font-size: 14px; 286 | font-weight: 600; 287 | } 288 | 289 | .markdown-body h6 { 290 | font-size: 12px; 291 | font-weight: 600; 292 | } 293 | 294 | .markdown-body p { 295 | margin-top: 0; 296 | margin-bottom: 10px; 297 | } 298 | 299 | .markdown-body blockquote { 300 | margin: 0; 301 | } 302 | 303 | .markdown-body ul, 304 | .markdown-body ol { 305 | padding-left: 0; 306 | margin-top: 0; 307 | margin-bottom: 0; 308 | } 309 | 310 | .markdown-body ol ol, 311 | .markdown-body ul ol { 312 | list-style-type: lower-roman; 313 | } 314 | 315 | .markdown-body ul ul ol, 316 | .markdown-body ul ol ol, 317 | .markdown-body ol ul ol, 318 | .markdown-body ol ol ol { 319 | list-style-type: lower-alpha; 320 | } 321 | 322 | .markdown-body dd { 323 | margin-left: 0; 324 | } 325 | 326 | .markdown-body code { 327 | font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace; 328 | font-size: 12px; 329 | } 330 | 331 | .markdown-body pre { 332 | margin-top: 0; 333 | margin-bottom: 0; 334 | font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace; 335 | font-size: 12px; 336 | } 337 | 338 | .markdown-body .octicon { 339 | vertical-align: text-bottom; 340 | } 341 | 342 | .markdown-body .pl-0 { 343 | padding-left: 0 !important; 344 | } 345 | 346 | .markdown-body .pl-1 { 347 | padding-left: 4px !important; 348 | } 349 | 350 | .markdown-body .pl-2 { 351 | padding-left: 8px !important; 352 | } 353 | 354 | .markdown-body .pl-3 { 355 | padding-left: 16px !important; 356 | } 357 | 358 | .markdown-body .pl-4 { 359 | padding-left: 24px !important; 360 | } 361 | 362 | .markdown-body .pl-5 { 363 | padding-left: 32px !important; 364 | } 365 | 366 | .markdown-body .pl-6 { 367 | padding-left: 40px !important; 368 | } 369 | 370 | .markdown-body::before { 371 | display: table; 372 | content: ""; 373 | } 374 | 375 | .markdown-body::after { 376 | display: table; 377 | clear: both; 378 | content: ""; 379 | } 380 | 381 | .markdown-body>*:first-child { 382 | margin-top: 0 !important; 383 | } 384 | 385 | .markdown-body>*:last-child { 386 | margin-bottom: 0 !important; 387 | } 388 | 389 | .markdown-body a:not([href]) { 390 | color: inherit; 391 | text-decoration: none; 392 | } 393 | 394 | .markdown-body .anchor { 395 | float: left; 396 | padding-right: 4px; 397 | margin-left: -20px; 398 | line-height: 1; 399 | } 400 | 401 | .markdown-body .anchor:focus { 402 | outline: none; 403 | } 404 | 405 | .markdown-body p, 406 | .markdown-body blockquote, 407 | .markdown-body ul, 408 | .markdown-body ol, 409 | .markdown-body dl, 410 | .markdown-body table, 411 | .markdown-body pre { 412 | margin-top: 0; 413 | margin-bottom: 16px; 414 | } 415 | 416 | .markdown-body hr { 417 | height: 0.25em; 418 | padding: 0; 419 | margin: 24px 0; 420 | background-color: #e1e4e8; 421 | border: 0; 422 | } 423 | 424 | .markdown-body blockquote { 425 | padding: 0 1em; 426 | color: #6a737d; 427 | border-left: 0.25em solid #5cb85c; 428 | } 429 | 430 | .markdown-body blockquote>:first-child { 431 | margin-top: 0; 432 | } 433 | 434 | .markdown-body blockquote>:last-child { 435 | margin-bottom: 0; 436 | } 437 | 438 | .markdown-body kbd { 439 | display: inline-block; 440 | padding: 3px 5px; 441 | font-size: 11px; 442 | line-height: 10px; 443 | color: #444d56; 444 | vertical-align: middle; 445 | background-color: #fafbfc; 446 | border: solid 1px #c6cbd1; 447 | border-bottom-color: #959da5; 448 | border-radius: 3px; 449 | box-shadow: inset 0 -1px 0 #959da5; 450 | } 451 | 452 | .markdown-body h1, 453 | .markdown-body h2, 454 | .markdown-body h3, 455 | .markdown-body h4, 456 | .markdown-body h5, 457 | .markdown-body h6 { 458 | margin-top: 24px; 459 | margin-bottom: 16px; 460 | font-weight: 600; 461 | line-height: 1.25; 462 | } 463 | 464 | .markdown-body h1 .octicon-link, 465 | .markdown-body h2 .octicon-link, 466 | .markdown-body h3 .octicon-link, 467 | .markdown-body h4 .octicon-link, 468 | .markdown-body h5 .octicon-link, 469 | .markdown-body h6 .octicon-link { 470 | color: #1b1f23; 471 | vertical-align: middle; 472 | visibility: hidden; 473 | } 474 | 475 | .markdown-body h1:hover .anchor, 476 | .markdown-body h2:hover .anchor, 477 | .markdown-body h3:hover .anchor, 478 | .markdown-body h4:hover .anchor, 479 | .markdown-body h5:hover .anchor, 480 | .markdown-body h6:hover .anchor { 481 | text-decoration: none; 482 | } 483 | 484 | .markdown-body h1:hover .anchor .octicon-link, 485 | .markdown-body h2:hover .anchor .octicon-link, 486 | .markdown-body h3:hover .anchor .octicon-link, 487 | .markdown-body h4:hover .anchor .octicon-link, 488 | .markdown-body h5:hover .anchor .octicon-link, 489 | .markdown-body h6:hover .anchor .octicon-link { 490 | visibility: visible; 491 | } 492 | 493 | .markdown-body h1 { 494 | padding-bottom: 0.3em; 495 | font-size: 2em; 496 | border-bottom: 1px solid #eaecef; 497 | } 498 | 499 | .markdown-body h2 { 500 | padding-bottom: 0.3em; 501 | font-size: 1.5em; 502 | border-bottom: 1px solid #eaecef; 503 | } 504 | 505 | .markdown-body h3 { 506 | font-size: 1.25em; 507 | } 508 | 509 | .markdown-body h4 { 510 | font-size: 1em; 511 | } 512 | 513 | .markdown-body h5 { 514 | font-size: 0.875em; 515 | } 516 | 517 | .markdown-body h6 { 518 | font-size: 0.85em; 519 | color: #6a737d; 520 | } 521 | 522 | .markdown-body ul, 523 | .markdown-body ol { 524 | padding-left: 2em; 525 | } 526 | 527 | .markdown-body ul ul, 528 | .markdown-body ul ol, 529 | .markdown-body ol ol, 530 | .markdown-body ol ul { 531 | margin-top: 0; 532 | margin-bottom: 0; 533 | } 534 | 535 | .markdown-body li>p { 536 | margin-top: 16px; 537 | } 538 | 539 | .markdown-body li+li { 540 | margin-top: 0.25em; 541 | } 542 | 543 | .markdown-body dl { 544 | padding: 0; 545 | } 546 | 547 | .markdown-body dl dt { 548 | padding: 0; 549 | margin-top: 16px; 550 | font-size: 1em; 551 | font-style: italic; 552 | font-weight: 600; 553 | } 554 | 555 | .markdown-body dl dd { 556 | padding: 0 16px; 557 | margin-bottom: 16px; 558 | } 559 | 560 | .markdown-body table { 561 | display: block; 562 | width: 100%; 563 | overflow: auto; 564 | } 565 | 566 | .markdown-body table th { 567 | font-weight: 600; 568 | } 569 | 570 | .markdown-body table th, 571 | .markdown-body table td { 572 | padding: 6px 13px; 573 | border: 1px solid #dfe2e5; 574 | } 575 | 576 | .markdown-body table tr { 577 | background-color: #fff; 578 | border-top: 1px solid #c6cbd1; 579 | } 580 | 581 | .markdown-body table tr:nth-child(2n) { 582 | background-color: #f6f8fa; 583 | } 584 | 585 | .markdown-body img { 586 | max-width: 100%; 587 | box-sizing: content-box; 588 | background-color: #fff; 589 | } 590 | 591 | .markdown-body img[align=right] { 592 | padding-left: 20px; 593 | } 594 | 595 | .markdown-body img[align=left] { 596 | padding-right: 20px; 597 | } 598 | 599 | .markdown-body code { 600 | padding: 0; 601 | padding-top: 0.2em; 602 | padding-bottom: 0.2em; 603 | margin: 0; 604 | font-size: 85%; 605 | background-color: rgba(27,31,35,0.05); 606 | border-radius: 3px; 607 | } 608 | 609 | .markdown-body code::before, 610 | .markdown-body code::after { 611 | letter-spacing: -0.2em; 612 | content: "\00a0"; 613 | } 614 | 615 | .markdown-body pre { 616 | word-wrap: normal; 617 | } 618 | 619 | .markdown-body pre>code { 620 | padding: 0; 621 | margin: 0; 622 | font-size: 100%; 623 | word-break: normal; 624 | white-space: pre; 625 | background: transparent; 626 | border: 0; 627 | } 628 | 629 | .markdown-body .highlight { 630 | margin-bottom: 16px; 631 | } 632 | 633 | .markdown-body .highlight pre { 634 | margin-bottom: 0; 635 | word-break: normal; 636 | } 637 | 638 | .markdown-body .highlight pre, 639 | .markdown-body pre { 640 | padding: 16px; 641 | overflow: auto; 642 | font-size: 85%; 643 | line-height: 1.45; 644 | background-color: #f6f8fa; 645 | border-radius: 3px; 646 | } 647 | 648 | .markdown-body pre code { 649 | display: inline; 650 | max-width: auto; 651 | padding: 0; 652 | margin: 0; 653 | overflow: visible; 654 | line-height: inherit; 655 | word-wrap: normal; 656 | background-color: transparent; 657 | border: 0; 658 | } 659 | 660 | .markdown-body pre code::before, 661 | .markdown-body pre code::after { 662 | content: normal; 663 | } 664 | 665 | .markdown-body .full-commit .btn-outline:not(:disabled):hover { 666 | color: #005cc5; 667 | border-color: #005cc5; 668 | } 669 | 670 | .markdown-body kbd { 671 | display: inline-block; 672 | padding: 3px 5px; 673 | font: 11px "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace; 674 | line-height: 10px; 675 | color: #444d56; 676 | vertical-align: middle; 677 | background-color: #fafbfc; 678 | border: solid 1px #d1d5da; 679 | border-bottom-color: #c6cbd1; 680 | border-radius: 3px; 681 | box-shadow: inset 0 -1px 0 #c6cbd1; 682 | } 683 | 684 | .markdown-body :checked+.radio-label { 685 | position: relative; 686 | z-index: 1; 687 | border-color: #0366d6; 688 | } 689 | 690 | .markdown-body .task-list-item { 691 | list-style-type: none; 692 | } 693 | 694 | .markdown-body .task-list-item+.task-list-item { 695 | margin-top: 3px; 696 | } 697 | 698 | .markdown-body .task-list-item input { 699 | margin: 0 0.2em 0.25em -1.6em; 700 | vertical-align: middle; 701 | } 702 | 703 | .markdown-body hr { 704 | border-bottom-color: #eee; 705 | } 706 | 707 | .hljs-comment { 708 | color: green; 709 | } 710 | .hljs-keyword { 711 | color: red; 712 | } 713 | .hljs-string { 714 | color: #084b71; 715 | } 716 | .lang-js { 717 | color: #de1144; 718 | } 719 | img { 720 | display inline-block 721 | text-align: center 722 | } 723 | -------------------------------------------------------------------------------- /frontend/src/assets/style/pagecss/myfooter.scss: -------------------------------------------------------------------------------- 1 | .m-footer { 2 | .content { 3 | text-align: center; 4 | font-size: 12px; 5 | color: rgb(24, 19, 19); 6 | .time { 7 | margin-top: 10px; 8 | color: #f00; 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /frontend/src/assets/style/pagecss/myheader.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HunterXing/404blog/b34a5cea3e46303f75aded08f35d2240fd586225/frontend/src/assets/style/pagecss/myheader.scss -------------------------------------------------------------------------------- /frontend/src/assets/style/reset.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0 3 | } 4 | .fl { 5 | float: left; 6 | } 7 | .fr { 8 | float: right; 9 | } 10 | .clearfix{ 11 | zoom: 1; 12 | } 13 | .clearfix:after{ 14 | display: block; 15 | content: ''; 16 | clear: both; 17 | } 18 | -------------------------------------------------------------------------------- /frontend/src/common/Article/ArticleCard.vue: -------------------------------------------------------------------------------- 1 | 106 | 107 | 165 | 166 | 274 | -------------------------------------------------------------------------------- /frontend/src/common/Article/SideBar.vue: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HunterXing/404blog/b34a5cea3e46303f75aded08f35d2240fd586225/frontend/src/common/Article/SideBar.vue -------------------------------------------------------------------------------- /frontend/src/common/footer/FooterBar.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 29 | 61 | 62 | -------------------------------------------------------------------------------- /frontend/src/common/footer/MyFooter.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 39 | -------------------------------------------------------------------------------- /frontend/src/common/goTop/GoTop.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 26 | 27 | 42 | -------------------------------------------------------------------------------- /frontend/src/common/header/MyHeader.vue: -------------------------------------------------------------------------------- 1 | 90 | 91 | 166 | 167 | 332 | -------------------------------------------------------------------------------- /frontend/src/common/readme.md: -------------------------------------------------------------------------------- 1 | ### 这里是一些公共组件 2 | -------------------------------------------------------------------------------- /frontend/src/main.js: -------------------------------------------------------------------------------- 1 | // The Vue build version to load with the `import` command 2 | // (runtime-only or standalone) has been set in webpack.base.conf with an alias. 3 | import Vue from 'vue' 4 | import Vuex from 'vuex' 5 | // import App from './App' 6 | import Index from './views/blog/index/Index' 7 | import router from './router' 8 | import ElementUI from 'element-ui' 9 | import 'element-ui/lib/theme-chalk/index.css' 10 | import 'style/reset.css' 11 | import 'style/iconfont.css' 12 | import 'style/main.scss' 13 | import VueLazyload from 'vue-lazyload' 14 | import Axios from 'axios' 15 | // 使得ie兼容promise 16 | import promise from 'es6-promise' 17 | import './assets/style/markdown.styl' 18 | import store from './store/index' 19 | promise.polyfill() 20 | 21 | Vue.config.productionTip = false 22 | Vue.use(ElementUI) 23 | Vue.use(VueLazyload) 24 | Vue.use(store) 25 | Vue.use(Vuex) 26 | /* eslint-disable no-new */ 27 | new Vue({ 28 | el: '#app', 29 | router, 30 | store, 31 | components: { 32 | Index 33 | }, 34 | template: '' 35 | }) 36 | // 在Vue的原型上添加axios方法 37 | Vue.prototype.axios = Axios 38 | 39 | // 路由跳转时改变title 40 | router.beforeEach((to, from, next) => { 41 | /* 路由发生变化修改页面title */ 42 | if (to.meta.title) { 43 | document.title = to.meta.title 44 | } 45 | let getFlag = localStorage.isLogin 46 | 47 | if (getFlag) { 48 | next() 49 | } else { 50 | if (to.meta.isLogin) { 51 | next({ 52 | path: '/recommend' 53 | }) 54 | alert('请先登录') 55 | } else { 56 | next() 57 | } 58 | } 59 | }) 60 | -------------------------------------------------------------------------------- /frontend/src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Router from 'vue-router' 3 | 4 | Vue.use(Router) 5 | 6 | export default new Router({ 7 | mode: 'history', 8 | routes: [ 9 | { 10 | path: '/', 11 | name: 'Index', 12 | component: () => import('@/views/blog/index/Index.vue'), 13 | redirect: { name: 'Recommend' }, 14 | meta: { 15 | title: '博客', 16 | isLogin: false 17 | }, 18 | children: [{ 19 | path: 'home', 20 | name: 'Home', 21 | component: () => import('@/views/blog/home/Home.vue'), 22 | meta: { 23 | title: '关于作者', 24 | isLogin: false 25 | } 26 | }, { 27 | path: 'article', 28 | name: 'Article', 29 | component: () => import('@/views/blog/article/Article.vue'), 30 | meta: { 31 | title: '我的文章', 32 | isLogin: true 33 | } 34 | }, { 35 | path: 'recommend', 36 | name: 'Recommend', 37 | component: () => import('@/views/blog/recommend/Recommend.vue'), 38 | meta: { 39 | title: '推荐内容', 40 | isLogin: false 41 | } 42 | }] 43 | }, 44 | { 45 | path: '/write/:blogId', 46 | name: 'Write', 47 | component: () => import('@/views/blog/write/Write.vue'), 48 | meta: { 49 | title: '写文章', 50 | isLogin: true 51 | } 52 | }, 53 | { 54 | path: '/article/detail/:blogId', 55 | name: 'ArticleDetail', 56 | component: () => import('@/views/blog/article/ArticleDetail.vue'), 57 | meta: { 58 | title: '文章详情', 59 | isLogin: false 60 | } 61 | }, 62 | { 63 | path: '/login', 64 | name: 'login', 65 | component: () => import('@/views/blog/phoneLogin/Login.vue'), 66 | meta: { 67 | title: '登录', 68 | isLogin: false 69 | } 70 | } 71 | ], 72 | // 路由激活状态 绑定一个class样式 73 | linkActiveClass: 'linkActive' 74 | }) 75 | -------------------------------------------------------------------------------- /frontend/src/store/index.js: -------------------------------------------------------------------------------- 1 | // this.$store.state.count 2 | import Vue from 'vue' 3 | import Vuex from 'vuex' 4 | import axios from 'axios' 5 | Vue.use(Vuex) 6 | 7 | export default new Vuex.Store({ 8 | // 共享数据较多时,可以把state actions mutations 分开,模块化 9 | state: { 10 | // 默认的用户状态 11 | auth: { 12 | isLogin: false 13 | } 14 | }, 15 | // 响应动作 16 | actions: { 17 | loginCheck (ctx) { 18 | let auth = {} 19 | // 后台请求 检查用户是否登录 20 | axios.get('/api/user/loginCheck') 21 | .then(res => { 22 | if (res.data.errno === 0) { 23 | auth = res.data.data 24 | console.log(auth) 25 | ctx.commit('loginCheck', auth) 26 | } else { 27 | auth.isLogin = false 28 | ctx.commit('loginCheck', auth) 29 | } 30 | }) 31 | } 32 | }, 33 | // 变化 34 | mutations: { 35 | loginCheck (state, auth) { 36 | state.auth = auth 37 | console.log(state.auth) 38 | localStorage.isLogin = state.auth.isLogin 39 | // debugger 40 | } 41 | } 42 | }) 43 | -------------------------------------------------------------------------------- /frontend/src/views/blog/article/Article.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 48 | 49 | 68 | -------------------------------------------------------------------------------- /frontend/src/views/blog/article/ArticleDetail.vue: -------------------------------------------------------------------------------- 1 | 31 | 32 | 213 | 214 | 285 | -------------------------------------------------------------------------------- /frontend/src/views/blog/home/Home.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 24 | 25 | 34 | -------------------------------------------------------------------------------- /frontend/src/views/blog/index/Index.vue: -------------------------------------------------------------------------------- 1 | 91 | 92 | 227 | 228 | 274 | -------------------------------------------------------------------------------- /frontend/src/views/blog/phoneLogin/Login.vue: -------------------------------------------------------------------------------- 1 | 47 | 48 | 146 | 147 | 216 | -------------------------------------------------------------------------------- /frontend/src/views/blog/recommend/Recommend.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 43 | 44 | 50 | -------------------------------------------------------------------------------- /frontend/src/views/blog/write/Write.vue: -------------------------------------------------------------------------------- 1 | 48 | 49 | 273 | 274 | 327 | -------------------------------------------------------------------------------- /frontend/src/views/blog/write/components/MyHeader.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 44 | 45 | 70 | -------------------------------------------------------------------------------- /frontend/static/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HunterXing/404blog/b34a5cea3e46303f75aded08f35d2240fd586225/frontend/static/.gitkeep -------------------------------------------------------------------------------- /frontend/test/e2e/custom-assertions/elementCount.js: -------------------------------------------------------------------------------- 1 | // A custom Nightwatch assertion. 2 | // The assertion name is the filename. 3 | // Example usage: 4 | // 5 | // browser.assert.elementCount(selector, count) 6 | // 7 | // For more information on custom assertions see: 8 | // http://nightwatchjs.org/guide#writing-custom-assertions 9 | 10 | exports.assertion = function (selector, count) { 11 | this.message = 'Testing if element <' + selector + '> has count: ' + count 12 | this.expected = count 13 | this.pass = function (val) { 14 | return val === this.expected 15 | } 16 | this.value = function (res) { 17 | return res.value 18 | } 19 | this.command = function (cb) { 20 | var self = this 21 | return this.api.execute(function (selector) { 22 | return document.querySelectorAll(selector).length 23 | }, [selector], function (res) { 24 | cb.call(self, res) 25 | }) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /frontend/test/e2e/nightwatch.conf.js: -------------------------------------------------------------------------------- 1 | require('babel-register') 2 | var config = require('../../config') 3 | 4 | // http://nightwatchjs.org/gettingstarted#settings-file 5 | module.exports = { 6 | src_folders: ['test/e2e/specs'], 7 | output_folder: 'test/e2e/reports', 8 | custom_assertions_path: ['test/e2e/custom-assertions'], 9 | 10 | selenium: { 11 | start_process: true, 12 | server_path: require('selenium-server').path, 13 | host: '127.0.0.1', 14 | port: 4444, 15 | cli_args: { 16 | 'webdriver.chrome.driver': require('chromedriver').path 17 | } 18 | }, 19 | 20 | test_settings: { 21 | default: { 22 | selenium_port: 4444, 23 | selenium_host: 'localhost', 24 | silent: true, 25 | globals: { 26 | devServerURL: 'http://localhost:' + (process.env.PORT || config.dev.port) 27 | } 28 | }, 29 | 30 | chrome: { 31 | desiredCapabilities: { 32 | browserName: 'chrome', 33 | javascriptEnabled: true, 34 | acceptSslCerts: true 35 | } 36 | }, 37 | 38 | firefox: { 39 | desiredCapabilities: { 40 | browserName: 'firefox', 41 | javascriptEnabled: true, 42 | acceptSslCerts: true 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /frontend/test/e2e/runner.js: -------------------------------------------------------------------------------- 1 | // 1. start the dev server using production config 2 | process.env.NODE_ENV = 'testing' 3 | 4 | const webpack = require('webpack') 5 | const DevServer = require('webpack-dev-server') 6 | 7 | const webpackConfig = require('../../build/webpack.prod.conf') 8 | const devConfigPromise = require('../../build/webpack.dev.conf') 9 | 10 | let server 11 | 12 | devConfigPromise.then(devConfig => { 13 | const devServerOptions = devConfig.devServer 14 | const compiler = webpack(webpackConfig) 15 | server = new DevServer(compiler, devServerOptions) 16 | const port = devServerOptions.port 17 | const host = devServerOptions.host 18 | return server.listen(port, host) 19 | }) 20 | .then(() => { 21 | // 2. run the nightwatch test suite against it 22 | // to run in additional browsers: 23 | // 1. add an entry in test/e2e/nightwatch.conf.js under "test_settings" 24 | // 2. add it to the --env flag below 25 | // or override the environment flag, for example: `npm run e2e -- --env chrome,firefox` 26 | // For more information on Nightwatch's config file, see 27 | // http://nightwatchjs.org/guide#settings-file 28 | let opts = process.argv.slice(2) 29 | if (opts.indexOf('--config') === -1) { 30 | opts = opts.concat(['--config', 'test/e2e/nightwatch.conf.js']) 31 | } 32 | if (opts.indexOf('--env') === -1) { 33 | opts = opts.concat(['--env', 'chrome']) 34 | } 35 | 36 | const spawn = require('cross-spawn') 37 | const runner = spawn('./node_modules/.bin/nightwatch', opts, { stdio: 'inherit' }) 38 | 39 | runner.on('exit', function (code) { 40 | server.close() 41 | process.exit(code) 42 | }) 43 | 44 | runner.on('error', function (err) { 45 | server.close() 46 | throw err 47 | }) 48 | }) 49 | -------------------------------------------------------------------------------- /frontend/test/e2e/specs/test.js: -------------------------------------------------------------------------------- 1 | // For authoring Nightwatch tests, see 2 | // http://nightwatchjs.org/guide#usage 3 | 4 | module.exports = { 5 | 'default e2e tests': function (browser) { 6 | // automatically uses dev Server port from /config.index.js 7 | // default: http://localhost:8080 8 | // see nightwatch.conf.js 9 | const devServer = browser.globals.devServerURL 10 | 11 | browser 12 | .url(devServer) 13 | .waitForElementVisible('#app', 5000) 14 | .assert.elementPresent('.hello') 15 | .assert.containsText('h1', 'Welcome to Your Vue.js App') 16 | .assert.elementCount('img', 1) 17 | .end() 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /frontend/test/unit/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "jest": true 4 | }, 5 | "globals": { 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /frontend/test/unit/jest.conf.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | 3 | module.exports = { 4 | rootDir: path.resolve(__dirname, '../../'), 5 | moduleFileExtensions: [ 6 | 'js', 7 | 'json', 8 | 'vue' 9 | ], 10 | moduleNameMapper: { 11 | '^@/(.*)$': '/src/$1' 12 | }, 13 | transform: { 14 | '^.+\\.js$': '/node_modules/babel-jest', 15 | '.*\\.(vue)$': '/node_modules/vue-jest' 16 | }, 17 | testPathIgnorePatterns: [ 18 | '/test/e2e' 19 | ], 20 | snapshotSerializers: ['/node_modules/jest-serializer-vue'], 21 | setupFiles: ['/test/unit/setup'], 22 | mapCoverage: true, 23 | coverageDirectory: '/test/unit/coverage', 24 | collectCoverageFrom: [ 25 | 'src/**/*.{js,vue}', 26 | '!src/main.js', 27 | '!src/router/index.js', 28 | '!**/node_modules/**' 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /frontend/test/unit/setup.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | Vue.config.productionTip = false 4 | -------------------------------------------------------------------------------- /frontend/test/unit/specs/HelloWorld.spec.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import HelloWorld from '@/components/HelloWorld' 3 | 4 | describe('HelloWorld.vue', () => { 5 | it('should render correct contents', () => { 6 | const Constructor = Vue.extend(HelloWorld) 7 | const vm = new Constructor().$mount() 8 | expect(vm.$el.querySelector('.hello h1').textContent) 9 | .toEqual('Welcome to Your Vue.js App') 10 | }) 11 | }) 12 | --------------------------------------------------------------------------------