react hooks + koa2 + mysql 的我的博客

前言

主要是本身重構了以前本身寫的項目,添加了一些功能,好比 github 受權等等...php

博客改成可配置性的,也就是說若是你想使用這個博客,你只須要動動手改改相應的我的信息,數據庫等等,你就能夠打包上線成爲你的專屬博客...html

相應的項目信息也在下面有所介紹、若是你想改改博客風格或者學習的話、也會有所幫助。前端

react hooks + koa + mysql

一個及其簡潔的我的博客系統、即插即用,若是你想使用這個博客、動動手改改配置便可使用!!node

  • 先後臺分離式開發(項目中也包含博客的後臺管理系統),爲了方便記錄後端開發過程,筆者將後端也一塊兒放在同個項目文件夾中。
  • 博客樣式幾乎藉助於 antd 這個優秀的 UI 框架,主打簡約風格,是筆者借鑑了 antd 官方的風格所設計。
  • 具有了代碼高亮、權限管理、第三方 github 登陸、評論與通知、以及郵件通知功能的我的博客...
  • 具有文件導入導出功能,假如你以前用 hexo 博客, 那麼你能夠直接經過導入 md 文件遷移你的文章。

MIT Licence
LICENSE
996.icu

實現功能

  • 前臺:主頁 + 列表頁 + 搜索頁 + 分類頁 + 標籤頁
  • 後臺:文章管理 + 用戶管理
  • 響應式、文章錨點導航、回到頂部、markdown 代碼高亮
  • 用戶:站內用戶、github 第三方受權登陸的用戶
  • 用戶能夠評論與回覆、以及郵件通知回覆的狀態
  • md 文件導入導出功能!能夠直接上傳 md 文件生成文章

技術棧

  • 前端 (基於 create-react-app eject 後的配置)mysql

    • react v16.9.0 hooks + redux + react-router4
    • marked highlight.js
    • webpack 打包優化
    • axios 封裝
  • 後端 (自構建後臺項目)react

    • koa2 + koa-router
    • sequelize + mysql
    • jwt + bcrypt
    • nodemailer
    • koa-send archiver

博客預覽

pc 端

移動端

項目結構

目錄結構

.
│
├─config                // 構建配置
├─public                // html 入口
├─scripts               // 項目腳本
└─server                // 後端
    ├─config            // 項目配置 github、email、database、token-secret 等等
    ├─controllers       // 業務控制層
    ├─middlewares       // 中間件
    ├─models            // 數據庫模型
    ├─router            // 路由
    ├─utils             // 工具包
    ├─  app.js          // 後端主入口文件
    ├─  initData.js     // 初始化基礎數據腳本
    └─...
│
└─src                   // 前端項目源碼
   ├─assets             // 靜態文件
   ├─components         // 公用組件
   ├─layout             // 佈局組件
   ├─redux              // redux 目錄
   ├─routes             // 路由
   ├─styles             // 樣式
   ├─utils              // 工具包
   ├─views              // 視圖層
   ├─  App.jsx          // 後端主入口文件
   ├─  config.js        // 項目配置 github 我的主頁、我的介紹等等
   ├─  index.js         // 主入口文件
   └─...

複製代碼

數據庫模型

role === 1: 博主用戶 role === 2: 普通用戶webpack

權限管理 server/middlewares/authHandler.jsios

const { checkToken } = require('../utils/token')

/** * role === 1 須要權限的路由 * @required 'all': get post put delete 均須要權限。 */
const verifyList1 = [
  { regexp: /\/article\/output/, required: 'get', verifyTokenBy: 'url' }, // 導出文章 verifyTokenBy 從哪裏驗證 token
  { regexp: /\/article/, required: 'post, put, delete' }, // 普通用戶 禁止修改或者刪除、添加文章
  { regexp: /\/discuss/, required: 'delete, post' }, // 普通用戶 禁止刪除評論
  { regexp: /\/user/, required: 'get, put, delete' } // 普通用戶 禁止獲取用戶、修改用戶、以及刪除用戶
]

// role === 2 須要權限的路由
const verifyList2 = [
  { regexp: /\/discuss/, required: 'post' } // 未登陸用戶 禁止評論
]

/** * 檢查路由是否須要權限,返回一個權限列表 * * @return {Array} 返回 roleList */
function checkAuth(method, url) {
  function _verify(list, role) {
    const target = list.find(v => {
      return v.regexp.test(url) && (v.required === 'all' || v.required.toUpperCase().includes(method))
    })

    return target
  }

  const roleList = []
  const result1 = _verify(verifyList1)
  const result2 = _verify(verifyList2)

  result1 && roleList.push({ role: 1, verifyTokenBy: result1.verifyTokenBy || 'headers' })
  result2 && roleList.push({ role: 2, verifyTokenBy: result1.verifyTokenBy || 'headers' })

  return roleList
}

// auth example token eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoyLCJpZCI6MSwiaWF0IjoxNTY3MDcyOTE4LCJleHAiOjE1Njk2NjQ5MTh9.-V71bEfuUczUt_TgK0AWUJTbAZhDAN5wAv8RjmxfDKI
module.exports = async (ctx, next) => {
  const roleList = checkAuth(ctx.method, ctx.url)
  // 該路由須要驗證
  if (roleList.length > 0) {
    if (checkToken(ctx, roleList)) {
      await next()
    } else {
      ctx.status = 401
      ctx.client(401)
    }
  } else {
    await next()
  }
}
複製代碼

關於使用這個項目須要的配置

前端 src/config.js

import React from 'react'
import MyInfo from '@/views/web/about/MyInfo'

// API_BASE_URL
export const API_BASE_URL = 'http://127.0.0.1:6060'

// project config
export const HEADER_BLOG_NAME = '郭大大的博客' // header title 顯示的名字

// === sidebar
export const SIDEBAR = {
  avatar: require('@/assets/images/avatar.jpeg'), // 側邊欄頭像
  title: '郭大大', // 標題
  subTitle: '前端打雜人員,略微代碼潔癖', // 子標題
  // 我的主頁
  homepages: {
    github: 'https://github.com/gershonv',
    juejin: 'https://juejin.im/user/5acac6c4f265da2378408f92'
  }
}

// === discuss avatar
export const DISCUSS_AVATAR = SIDEBAR.avatar // 評論框博主頭像

// github
export const GITHUB = {
  enable: true, // github 第三方受權開關
  client_id: '', // Setting > Developer setting > OAuth applications => client_id
  url: 'https://github.com/login/oauth/authorize', // 跳轉的登陸的地址
  loadingType: 1
}

export const ABOUT = {
  avatar: SIDEBAR.avatar,
  describe: SIDEBAR.subTitle,
  discuss: true, // 關於頁面是否開啓討論
  renderMyInfo: <MyInfo /> // 個人介紹 自定義組件 => src/views/web/about/MyInfo.jsx } 複製代碼

後端 server/config.js

const devMode = process.env.NODE_ENV === 'development'

const config = {
  PORT: 6060, // 啓動端口
  ADMIN_GITHUB_LOGIN_NAME: 'gershonv', // 博主的 github 登陸的帳戶名 user
  GITHUB: {
    client_id: 'c6a96a84105bb0be1fe5',
    client_secret: '',
    access_token_url: 'https://github.com/login/oauth/access_token',
    fetch_user_url: 'https://api.github.com/user', // 用於 oauth2
    fetch_user: 'https://api.github.com/users/' // fetch user url https://api.github.com/users/gershonv
  },
  EMAIL_NOTICE: {
    // 郵件通知服務
    // detail: https://nodemailer.com/
    enable: true, // 開關
    transporterConfig: {
      host: 'smtp.163.com',
      port: 465,
      secure: true, // true for 465, false for other ports
      auth: {
        user: 'guodadablog@163.com', // generated ethereal user
        pass: '123456' // generated ethereal password 受權碼 而非 密碼
      }
    },
    subject: '郭大大的博客 - 您的評論得到新的回覆!', // 主題
    text: '您的評論得到新的回覆!',
    WEB_HOST: 'http://127.0.0.1:3000' // email callback url
  },
  TOKEN: {
    secret: 'guo-test', // secret is very important!
    expiresIn: '720h' // token 有效期
  },
  DATABASE: {
    database: 'test',
    user: 'root',
    password: '123456',
    options: {
      host: 'localhost', // 鏈接的 host 地址
      dialect: 'mysql', // 鏈接到 mysql
      pool: {
        max: 5,
        min: 0,
        acquire: 30000,
        idle: 10000
      },
      define: {
        timestamps: false, // 默認不加時間戳
        freezeTableName: true // 表名默認不加 s
      },
      timezone: '+08:00'
    }
  }
}

// 部署的環境變量設置
if (!devMode) {
  console.log('env production....')

  // ==== 配置數據庫
  config.DATABASE = {
    ...config.DATABASE,
    database: '', // 數據庫名
    user: '', // 帳號
    password: '' // 密碼
  }

  // 配置 github 受權
  config.GITHUB.client_id = ''
  config.GITHUB.client_secret = ''

  // ==== 配置 token 密鑰
  config.TOKEN.secret = ''

  // ==== 配置郵箱

  // config.EMAIL_NOTICE.enable = true
  config.EMAIL_NOTICE.transporterConfig.auth = {
    user: 'guodadablog@163.com', // generated ethereal user
    pass: '123456XXX' // generated ethereal password 受權碼 而非 密碼
  }
  config.EMAIL_NOTICE.WEB_HOST = 'https://guodada.fun'
}

module.exports = config
複製代碼

關於 github 第三方受權和 email 受權,能夠參考git

使用這個項目

git clone https://github.com/gershonv/react-blog.git

## 安裝依賴以及開啓開發模式
cd react-blog
yarn
yarn dev

## 安裝依賴以及開啓開發模式 注意必須先配置好數據庫、我的github帳戶登陸名,配置文件在 server/config/index.js
## 筆者採用的數據庫字符集爲 utf8mb4 排序規則 utf8mb4_general_ci
cd server
yarn
yarn dev


## 打包前端
cd react-blog
yarn build

## 後端筆者則是採用pm2
cd server
pm2 start app.js
複製代碼

導入功能說明

導入 md 文件是按照 hexo 生成的前綴去解析的, 好比github

---
title: ES6 - Class
date: 2018-07-16 22:19:09
categories: Javascript
tags:
  - Javascript
  - ES6
---
複製代碼

對應會解析爲

  • 標題:ES6 - Class
  • 建立日期:2018-07-16 22:19:09
  • 分類:Javascript
  • 標籤:Javascript ES6

若是導入標題同樣的文件,能夠確認是否覆蓋原來的文章!

項目地址

因爲太多小夥伴私聊 QQ,故本人建了個羣方便交流技術答疑源碼,無商業用途,感興趣的夥伴能夠加羣 855655742。請勿廣告,謝謝。

PS : 整個項目都是筆者我的設計、編碼所做,若是對您有幫助,但願不吝 star、多多支持

相關文章
相關標籤/搜索