{"id":13677522,"url":"https://github.com/alvin0216/react-blog","last_synced_at":"2025-04-12T23:39:14.547Z","repository":{"id":39494138,"uuid":"166946831","full_name":"alvin0216/react-blog","owner":"alvin0216","description":" react hooks + koa2 + sequelize + mysql 构建的个人博客。具备评论、通知、上传文章等等功能","archived":false,"fork":false,"pushed_at":"2023-03-01T08:37:03.000Z","size":6119,"stargazers_count":721,"open_issues_count":62,"forks_count":193,"subscribers_count":12,"default_branch":"master","last_synced_at":"2025-04-12T23:39:06.314Z","etag":null,"topics":["bycrypt","create-react-app","jwt","koa2","mysql","react","react-hooks","react-router-v4","redux","sequelize"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/alvin0216.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2019-01-22T07:18:28.000Z","updated_at":"2025-04-10T02:49:00.000Z","dependencies_parsed_at":"2024-01-14T14:31:23.929Z","dependency_job_id":"a07f748e-116d-44fc-86a0-d424bf8fafdb","html_url":"https://github.com/alvin0216/react-blog","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alvin0216%2Freact-blog","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alvin0216%2Freact-blog/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alvin0216%2Freact-blog/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alvin0216%2Freact-blog/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/alvin0216","download_url":"https://codeload.github.com/alvin0216/react-blog/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248647255,"owners_count":21139081,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bycrypt","create-react-app","jwt","koa2","mysql","react","react-hooks","react-router-v4","redux","sequelize"],"created_at":"2024-08-02T13:00:43.437Z","updated_at":"2025-04-12T23:39:14.511Z","avatar_url":"https://github.com/alvin0216.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"## 声明\n\n本项目已不再维护，项目已经升级到 ssr 版本，沿用并改进了 UI。新项目地址 [remix-ssr-blog](https://github.com/alvin0216/remix-ssr-blog)。欢迎继续关注！\n\n## react hooks + koa + mysql\n\n\u003e 一个及其简洁的个人博客系统、即插即用，如果你想使用这个博客、动动手改改配置即可使用！！\n\n- 前后台分离式开发（项目中也包含博客的后台管理系统），为了方便记录后端开发过程，笔者将后端也一起放在同个项目文件夹中。\n- 博客样式几乎借助于 `antd` 这个优秀的 UI 框架，主打简约风格，是笔者借鉴了 `antd` 官方的风格所设计。\n- 具备了代码高亮、权限管理、第三方 `github` 登录、评论与通知、以及邮件通知功能的个人博客...\n- 具备文件导入导出功能，假如你之前用 `hexo` 博客, 那么你可以直接通过导入 `md` 文件迁移你的文章。\n\n* 我的博客地址: [郭大大的博客](https://blog.alvin.run)\n\n声明：博客仅做展示使用，（之前被比特币攻击了），所需数据已重置。\n\n\u003c!-- * 测试博客地址: [测试使用的郭大大的博客](https://test.alvin.run)) `admin/admin` 为博主账号 小伙伴可以使用看看博客的完整功能！ --\u003e\n\n[![MIT Licence](https://badges.frapsoft.com/os/mit/mit.svg?v=103)](https://opensource.org/licenses/mit-license.php)\n[![LICENSE](https://img.shields.io/badge/license-Anti%20996-blue.svg)](https://github.com/996icu/996.ICU/blob/master/LICENSE)\n[![996.icu](https://img.shields.io/badge/link-996.icu-red.svg)](https://996.icu)\n\n### 实现功能\n\n- [x] 前台：主页 + 列表页 + 搜索页 + 分类页 + 标签页\n- [x] 后台：文章管理 + 用户管理\n- [x] 响应式、文章锚点导航、回到顶部、`markdown` 代码高亮\n- [x] 用户：站内用户、`github` 第三方授权登录的用户\n- [x] 用户可以评论与回复、以及**邮件通知**回复的状态\n- [x] `md` 文件导入导出功能！可以直接上传 `md` 文件生成文章\n\n### 技术栈\n\n- 前端 （基于 `create-react-app eject` 后的配置）\n\n  - react v16.9.0 `hooks` + `redux` + `react-router4`\n  - `marked highlight.js`\n  - `webpack` 打包优化\n  - `axios` 封装\n\n- 后端 （自构建后台项目）\n  - `koa2` + `koa-router`\n  - `sequelize` + `mysql`\n  - `jwt` + `bcrypt`\n  - `nodemailer`\n  - `koa-send` `archiver`\n\n## 博客预览\n\n### pc 端\n\n![](https://user-gold-cdn.xitu.io/2019/10/14/16dc944b4cdc4409?w=1908\u0026h=1056\u0026f=png\u0026s=385734)\n\n### 移动端\n\n![](https://user-gold-cdn.xitu.io/2019/9/20/16d4df6fb00c0abf?w=370\u0026h=789\u0026f=png\u0026s=144230)\n\n## 项目结构\n\n### 目录结构\n\n```js\n.\n│\n├─config                // 构建配置\n├─public                // html 入口\n├─scripts               // 项目脚本\n└─server                // 后端\n    ├─config            // 项目配置 github、email、database、token-secret 等等\n    ├─controllers       // 业务控制层\n    ├─middlewares       // 中间件\n    ├─models            // 数据库模型\n    ├─router            // 路由\n    ├─utils             // 工具包\n    ├─  app.js          // 后端主入口文件\n    ├─  initData.js     // 初始化基础数据脚本\n    └─...\n│\n└─src                   // 前端项目源码\n   ├─assets             // 静态文件\n   ├─components         // 公用组件\n   ├─layout             // 布局组件\n   ├─redux              // redux 目录\n   ├─routes             // 路由\n   ├─styles             // 样式\n   ├─utils              // 工具包\n   ├─views              // 视图层\n   ├─  App.jsx          // 后端主入口文件\n   ├─  config.js        // 项目配置 github 个人主页、个人介绍等等\n   ├─  index.js         // 主入口文件\n   └─...\n\n```\n\n### 数据库模型\n\n![](https://user-gold-cdn.xitu.io/2019/9/20/16d4e0f97411e6cb?w=660\u0026h=655\u0026f=png\u0026s=340072)\n\nrole === 1: 博主用户\nrole === 2: 普通用户\n\n权限管理 `server/middlewares/authHandler.js`\n\n```js\nconst { checkToken } = require('../utils/token')\n\n/**\n * role === 1 需要权限的路由\n * @required 'all': get post put delete 均需要权限。\n */\nconst verifyList1 = [\n  { regexp: /\\/article\\/output/, required: 'get', verifyTokenBy: 'url' }, // 导出文章 verifyTokenBy 从哪里验证 token\n  { regexp: /\\/article/, required: 'post, put, delete' }, // 普通用户 禁止修改或者删除、添加文章\n  { regexp: /\\/discuss/, required: 'delete, post' }, // 普通用户 禁止删除评论\n  { regexp: /\\/user/, required: 'get, put, delete' }, // 普通用户 禁止获取用户、修改用户、以及删除用户\n]\n\n// role === 2 需要权限的路由\nconst verifyList2 = [\n  { regexp: /\\/discuss/, required: 'post' }, // 未登录用户 禁止评论\n]\n\n/**\n * 检查路由是否需要权限，返回一个权限列表\n *\n * @return {Array} 返回 roleList\n */\nfunction checkAuth(method, url) {\n  function _verify(list, role) {\n    const target = list.find((v) =\u003e {\n      return v.regexp.test(url) \u0026\u0026 (v.required === 'all' || v.required.toUpperCase().includes(method))\n    })\n\n    return target\n  }\n\n  const roleList = []\n  const result1 = _verify(verifyList1)\n  const result2 = _verify(verifyList2)\n\n  result1 \u0026\u0026 roleList.push({ role: 1, verifyTokenBy: result1.verifyTokenBy || 'headers' })\n  result2 \u0026\u0026 roleList.push({ role: 2, verifyTokenBy: result1.verifyTokenBy || 'headers' })\n\n  return roleList\n}\n\n// auth example token eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoyLCJpZCI6MSwiaWF0IjoxNTY3MDcyOTE4LCJleHAiOjE1Njk2NjQ5MTh9.-V71bEfuUczUt_TgK0AWUJTbAZhDAN5wAv8RjmxfDKI\nmodule.exports = async (ctx, next) =\u003e {\n  const roleList = checkAuth(ctx.method, ctx.url)\n  //  该路由需要验证\n  if (roleList.length \u003e 0) {\n    if (checkToken(ctx, roleList)) {\n      await next()\n    } else {\n      ctx.status = 401\n      ctx.client(401)\n    }\n  } else {\n    await next()\n  }\n}\n```\n\n## 关于使用这个项目需要的配置\n\n### 前端 `src/config.js`\n\n```js\nimport React from 'react'\nimport MyInfo from '@/views/web/about/MyInfo'\n\n// API_BASE_URL\nexport const API_BASE_URL = 'http://127.0.0.1:6060'\n\n// project config\nexport const HEADER_BLOG_NAME = '郭大大的博客' // header title 显示的名字\n\n// === sidebar\nexport const SIDEBAR = {\n  avatar: require('@/assets/images/avatar.jpeg'), // 侧边栏头像\n  title: '郭大大', // 标题\n  subTitle: '前端打杂人员，略微代码洁癖', // 子标题\n  // 个人主页\n  homepages: {\n    github: 'https://github.com/gershonv',\n    juejin: 'https://juejin.im/user/5acac6c4f265da2378408f92',\n  },\n}\n\n// === discuss avatar\nexport const DISCUSS_AVATAR = SIDEBAR.avatar // 评论框博主头像\n\n// github\nexport const GITHUB = {\n  enable: true, // github 第三方授权开关\n  client_id: '', // Setting \u003e Developer setting \u003e OAuth applications =\u003e client_id\n  url: 'https://github.com/login/oauth/authorize', // 跳转的登录的地址\n}\n\nexport const ABOUT = {\n  avatar: SIDEBAR.avatar,\n  describe: SIDEBAR.subTitle,\n  discuss: true, // 关于页面是否开启讨论\n  renderMyInfo: \u003cMyInfo /\u003e, // 我的介绍 自定义组件 =\u003e src/views/web/about/MyInfo.jsx\n}\n```\n\n### 后端 `server/config.js`\n\n```js\nconst devMode = process.env.NODE_ENV === 'development'\n\nconst config = {\n  PORT: 6060, // 启动端口\n  ADMIN_GITHUB_LOGIN_NAME: 'gershonv', // 博主的 github 登录的账户名 user\n  GITHUB: {\n    client_id: 'c6a96a84105bb0be1fe5',\n    client_secret: '',\n    access_token_url: 'https://github.com/login/oauth/access_token',\n    fetch_user_url: 'https://api.github.com/user', // 用于 oauth2\n    fetch_user: 'https://api.github.com/users/', // fetch user url https://api.github.com/users/gershonv\n  },\n  EMAIL_NOTICE: {\n    // 邮件通知服务\n    // detail: https://nodemailer.com/\n    enable: true, // 开关\n    transporterConfig: {\n      host: 'smtp.163.com',\n      port: 465,\n      secure: true, // true for 465, false for other ports\n      auth: {\n        user: 'guodadablog@163.com', // generated ethereal user\n        pass: '123456', // generated ethereal password 授权码 而非 密码\n      },\n    },\n    subject: '郭大大的博客 - 您的评论获得新的回复！', // 主题\n    text: '您的评论获得新的回复！',\n    WEB_HOST: 'http://127.0.0.1:3000', // email callback url\n  },\n  TOKEN: {\n    secret: 'guo-test', // secret is very important!\n    expiresIn: '720h', // token 有效期\n  },\n  DATABASE: {\n    database: 'test',\n    user: 'root',\n    password: '123456',\n    options: {\n      host: 'localhost', // 连接的 host 地址\n      dialect: 'mysql', // 连接到 mysql\n      pool: {\n        max: 5,\n        min: 0,\n        acquire: 30000,\n        idle: 10000,\n      },\n      define: {\n        timestamps: false, // 默认不加时间戳\n        freezeTableName: true, // 表名默认不加 s\n      },\n      timezone: '+08:00',\n    },\n  },\n}\n\n// 部署的环境变量设置\nif (!devMode) {\n  console.log('env production....')\n\n  // ==== 配置数据库\n  config.DATABASE = {\n    ...config.DATABASE,\n    database: '', // 数据库名\n    user: '', // 账号\n    password: '', // 密码\n  }\n\n  // 配置 github 授权\n  config.GITHUB.client_id = ''\n  config.GITHUB.client_secret = ''\n\n  // ==== 配置 token 密钥\n  config.TOKEN.secret = ''\n\n  // ==== 配置邮箱\n\n  // config.EMAIL_NOTICE.enable = true\n  config.EMAIL_NOTICE.transporterConfig.auth = {\n    user: 'guodadablog@163.com', // generated ethereal user\n    pass: '123456XXX', // generated ethereal password 授权码 而非 密码\n  }\n  config.EMAIL_NOTICE.WEB_HOST = 'https://guodada.fun'\n}\n\nmodule.exports = config\n```\n\n关于 `github` 第三方授权和 `email` 授权，可以参考\n\n- [GitHub 第三方登录](https://www.jianshu.com/p/78d186aeb526)\n- [GitHub 第三方授权 demo](https://github.com/gershonv/oAuth2-github.git)\n- [nodemailer](https://nodemailer.com/)\n\n## 使用这个项目\n\n```bash\ngit clone https://github.com/gershonv/react-blog.git\n\n## 安装依赖以及开启开发模式\ncd react-blog\nyarn\nyarn dev\n\n## 安装依赖以及开启开发模式 注意必须先配置好数据库、个人github账户登录名，配置文件在 server/config/index.js\n## 笔者采用的数据库字符集为 utf8mb4 排序规则 utf8mb4_general_ci\ncd server\nyarn\nyarn dev\n\n\n## 打包前端\ncd react-blog\nyarn build\n\n## 后端笔者则是采用pm2\ncd server\npm2 start app.js\n```\n\n### 导入功能说明\n\n导入 `md` 文件是按照 hexo 生成的前缀去解析的， 比如\n\n```bash\n---\ntitle: ES6 - Class\ndate: 2018-07-16 22:19:09\ncategories: Javascript\ntags:\n  - Javascript\n  - ES6\n---\n```\n\n对应会解析为\n\n- 标题：`ES6 - Class`\n- 创建日期：`2018-07-16 22:19:09`\n- 分类：`Javascript`\n- 标签：`Javascript` `ES6`\n\n如果导入标题一样的文件，可以确认是否覆盖原来的文章！\n\nPS : 觉得不错的伙伴可以给个 star ~~~ 或者 fork 下来看看哦。如果有什么建议，也可以提 issue 哦\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falvin0216%2Freact-blog","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falvin0216%2Freact-blog","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falvin0216%2Freact-blog/lists"}