React与Koa一起打造一个仿稀土掘金全栈个人博客(技术篇)
发布日期:2021-07-01 02:18:55 浏览次数:3 分类:技术文章

本文共 37007 字,大约阅读时间需要 123 分钟。

在这里插入图片描述

前言

我的个人博客样式布局是仿的 ,个人博客线上网址为 ,也可以百度搜索 。为了浏览体验,可以用PC浏览器浏览。

本篇文章将分为前台角度与后台角度来分析我是怎么开发的。

前台角度

主要资源

  • react.js
  • ant Design
  • for-editor
  • axios
  • craco-less
  • immutable
  • react-loadable
  • react-redux
  • react-router-dom
  • react-transition-group
  • redux
  • redux-immutable
  • redux-thunk
  • styled-components

模块页面

  1. 首页

  2. 登录注册

  3. 文章详情

  4. 文章评论

  5. 圈子

  6. 写圈子

  7. 搜索页

  8. 权限页

  9. 写文章

项目配置

项目目录

在这里插入图片描述

前台搭建项目步骤

一、使用稳定依赖管理工具

推荐你使用淘宝源

npm config set registry https://registry.npm.taobao.org

还有就是搭配依赖管理工具

二、使用官方React脚手架
create-react-app my-project
三、精简项目文件夹

使用脚手架搭建的初始文件夹是这样的。

在这里插入图片描述
那么我们需要精简一下。注意原来的App.js我改成App.jsx。因为 React 使用 JSX 来替代常规的 JavaScript,所以用JSX比较好。
在这里插入图片描述
下面我们将要编辑几个文件:
src/index.js

// index.jsimport React from 'react';import ReactDOM from 'react-dom';import App from './App.jsx';ReactDOM.render(  
, document.getElementById('root'));

public/index.html

      
前端历劫之路

App.jsx文件内的内容什么意思现在可以先不用去关心,可以先放这。

src/App.jsx

// App.jsximport React from 'react';import {
Provider } from 'react-redux';import store from './store/';import Router from './router';import {
BrowserRouter} from 'react-router-dom';import {
Main} from './styled/'import {
CSSTransition, TransitionGroup } from 'react-transition-group';import {
GlobalStyle } from '../src/styled/index';import HeaderArea from './components/layout/Header';import './App.less';const Body = () => {
return (
)}const App = () => {
return (
)};export default App;
四、创建文件夹

src目录下分别创建以下几个文件夹

在这里插入图片描述

五、安装依赖

dependencies:

  • antd
  • axios
  • for-editor
  • immutable
  • react-loadable
  • react-redux
  • react-router-dom
  • react-transition-group
  • redux
  • redux-immutable
  • redux-thunk
  • styled-components
六、配置自定义主题

按照 配置主题 的要求,自定义主题需要用到类似 less-loader 提供的 less 变量覆盖功能。我们可以引入 craco-less 来帮助加载 less 样式和修改变量。

  1. 首先在src目录下创建一个App.less文件,编辑内容如下:
@import '~antd/dist/antd.less';
  1. 然后在App.jsx内引入App.less文件(上面已经编辑过App.jsx文件的这里不用管)
  2. 然后安装 craco-less 并创建修改 craco.config.js(存放在项目根目录下) 文件如下:
// craco.config.jsconst CracoLessPlugin = require('craco-less');const theme = require ('./theme');module.exports = {
plugins: [ {
plugin: CracoLessPlugin, options: {
lessLoaderOptions: {
modifyVars: theme.theme, javascriptEnabled: true, }, }, } ],};
// theme.jsconst theme = {
'@primary-color': '#FFB90F', // 全局主色 '@link-color': '#1890ff', // 链接色 '@success-color': '#52c41a', // 成功色 '@warning-color': '#faad14', // 警告色 '@error-color': '#f5222d', // 错误色 '@font-size-base': '14px', // 主字号 '@heading-color': 'rgba(0, 0, 0, 0.85)', // 标题色 '@text-color': 'rgba(0, 0, 0, 0.65)', // 主文本色 '@text-color-secondary': 'rgba(0, 0, 0, 0.45)', // 次文本色 '@disabled-color': 'rgba(0, 0, 0, 0.25)', // 失效色 '@border-radius-base': '4px', // 组件/浮层圆角 '@border-color-base': '#d9d9d9', // 边框色 '@box-shadow-base': '0 2px 8px rgba(0, 0, 0, 0.15)' // 浮层阴影}exports.theme = theme
七、路由懒加载

在router文件夹下创建index.js和routes.js。

routes.js

// routes.js// 路由配置import React from 'react';import {
Route } from 'react-router-dom';import {
Home,About,Details,Write,Circle,Noauth,Search} from './routes'const APPRouter = () =>(
);export default APPRouter;

index.js

// index.js// 页面组件import loadable from '../util/loadable';export const Home = loadable(()=> import('../views/Home/'));export const About = loadable(()=> import('../views/About/'));export const Details = loadable(()=> import('../views/Details'));export const Write = loadable(()=> import('../views/Write'));export const Circle = loadable(()=> import('../views/Circle'));export const Noauth = loadable(()=>import('../components/modules/Noauth'))export const Search = loadable(()=>import('../views/Search'))

在util文件夹下创建一个loadable.js。

loadable.js

// loadable.js// 懒加载组件import React from 'react';import Loadable from 'react-loadable';import styled from 'styled-components';import {
Spin } from 'antd';const loadingComponent =()=>{
return (
)};export default (loader,loading = loadingComponent)=>{
return Loadable({
loader, loading });};const Loading = styled.div` text-align: center; margin:50vh 0;`;
八、全局样式与样式组件

这里我们使用styled-components这个依赖写样式组件,因为在react.js中存在组件样式污染的缘故。

在styled创建一个index.js。

index.js

// index.js// 全局样式import styled,{
createGlobalStyle} from 'styled-components';export const Content = styled.div` border-radius: 2px; width: 100%; padding:20px; margin:20px 0; border:1px solid #f4f4f4; background:#fff; box-sizing:border-box;`export const Main = styled.div` position: relative; margin: 100px auto 20px; width: 100%; max-width: 960px;`;export const GlobalStyle = createGlobalStyle`html, body, div, span, applet, object, iframe,h1, h2, h3, h4, h5, h6, p, blockquote, pre,a, abbr, acronym, address, big, cite, code,del, dfn, em, img, ins, kbd, q, s, samp,small, strike, strong, sub, sup, tt, var,b, u, i, center,dl, dt, dd, ol, ul, li,fieldset, form, label, legend,table, caption, tbody, tfoot, thead, tr, th, td,article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary,time, mark, audio, video{ margin: 0; padding: 0; border: 0; font-size: 100%; font: inherit; font-weight: normal; vertical-align: baseline;}article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section{ display: block;}ol, ul, li{ list-style: none;}blockquote, q{ quotes: none;}blockquote:before, blockquote:after,q:before, q:after{ content: ''; content: none;}table{ border-collapse: collapse; border-spacing: 0;}a{ color: #7e8c8d; text-decoration: none; -webkit-backface-visibility: hidden;}::-webkit-scrollbar{ width: 5px; height: 5px;}::-webkit-scrollbar-track-piece{ background-color: rgba(0, 0, 0, 0.2); -webkit-border-radius: 6px;}::-webkit-scrollbar-thumb:vertical{ height: 5px; background-color: rgba(125, 125, 125, 0.7); -webkit-border-radius: 6px;}::-webkit-scrollbar-thumb:horizontal{ width: 5px; background-color: rgba(125, 125, 125, 0.7); -webkit-border-radius: 6px;}html, body{ width: 100% !important; background:#E8E8E8; font-size: 12px; font-family: Avenir,-apple-system,BlinkMacSystemFont,segoe ui,Roboto,helvetica neue,Arial,noto sans,sans-serif,apple color emoji,segoe ui emoji,segoe ui symbol,noto color emoji,sans-serif;}body{ line-height: 1; -webkit-text-size-adjust: none; -webkit-tap-highlight-color: rgba(0, 0, 0, 0);}html{ overflow-y: scroll;}.clearfix:before,.clearfix:after{ content: " "; display: inline-block; height: 0; clear: both; visibility: hidden;}.clearfix{ *zoom: 1;}.ovf{ overflow:hidden;}.dn{ display: none;}/*自定义全局*/p{ margin:10px;}.fade-enter { opacity: 0;}.fade-enter-active { opacity: 1; transition: all .5s;}.fade-exit { opacity: 1; transition: all .5s;}.fade-exit-active { opacity: 0;}.hide{ opacity: 0; height: 0px; transform: translatey(-100px); }::-webkit-scrollbar { width:5px; height:5px;}::-webkit-scrollbar-track { width: 5px; background-color:#fff; -webkit-border-radius: 10px; -moz-border-radius: 10px; border-radius:10px;}::-webkit-scrollbar-thumb { background-clip:padding-box; min-height:28px; -webkit-border-radius: 10px; -moz-border-radius: 10px; border-radius:10px;}::-webkit-scrollbar-thumb:hover { background-color:#FFB90F;}`;
九、封装axios请求

在request文件夹下创建api.js和http.js。

api.js

存放api接口。

// api.js// 接口地址import {
get,post} from './http';const url= 'https://www.maomin.club/myblog/'; // api// post格式export const reg = g => post(`${
url}register`, g); // 注册export const log = g => post(`${
url}login`, g); // 登录export const write = g => post(`${
url}write`, g); // 写文章export const circle = g => post(`${
url}circle`, g); // 发圈子export const getCircle = g => post(`${
url}getCircle`, g); // 获取圈子export const uploadImg = g => post(`${
url}uploadImg`, g); // 写文章上传图片export const getListapi = g => post(`${
url}getList`, g); // 获取文章列表export const getDetails = g => post(`${
url}getDetails`, g); // 获取文章详情export const comment = g => post(`${
url}comment`, g); // 发送评论export const getComment = g => post(`${
url}getComment`, g); // 获取评论export const getinfo = g => post(`${
url}getinfo`, g) // 获取用户信息// get格式export const alllist = g =>get(`${
url}getAllList`,g);//获取所有文章列表

http.js

请求配置。

// http.js// axios配置import axios from 'axios';import {
message} from 'antd';// 请求拦截器axios.interceptors.request.use( config => {
if (localStorage.getItem('Authorization')) {
config.headers.Authorization = localStorage.getItem('Authorization'); //查看是否存在token return config; } else if (config.isUpload) {
config.headers = {
'Content-Type': 'multipart/form-data' } // 根据参数是否启用form-data方式 return config; } else {
config.headers = {
'Content-Type': 'application/json;charset=utf-8' } return config; } }, error => {
return Promise.error(error) })// 响应拦截器axios.interceptors.response.use( // 服务码是200的情况 response => {
if (response.status === 200) {
switch (response.data.resultCode) {
// token过期 case 2: message.error('登录过期,请重新登录'); localStorage.removeItem('Authorization'); setTimeout(() => {
window.location.href="/"; }, 1000); break; case 3: message.error('未登录'); break; case 4: message.error('请输入正确的账号或者密码'); break; default: break; } return Promise.resolve(response); } else {
return Promise.reject(response) } }, // 服务器状态码不是200的情况 error => {
if (error.response.status) {
switch (error.response.status) {
// 404请求不存在 case 404: alert('网络请求不存在'); break; // 其他错误,直接抛出错误提示 default: alert('error.response.data.message'); } return Promise.reject(error.response) } })/** * get方法,对应get请求 * @param {String} url [请求的url地址] * @param {Object} params [请求时携带的参数] */export function get(url, params, config = {
add: ''}) {
return new Promise((resolve, reject) => {
axios.get(url, {
params: params }, config).then(res => {
resolve(res.data) }).catch(err => {
reject(err.data) }) })}/** * post方法,对应post请求 * @param {String} url [请求的url地址] * @param {Object} params [请求时携带的参数] */export function post(url, params, config = {
isUpload: false}) {
return new Promise((resolve, reject) => {
axios.post(url, params, config) .then(res => {
resolve(res.data) }) .catch(err => {
reject(err.data) }) })}
十、状态管理总配置

在store文件夹创建一个index.js和reducer.js。因为每个页面模块都有一个状态,所以我们在这个项目里采用分模块。然后我们现在的需要做的是统一管理它们每一个模块。

index.js

// index.js// 全局store配置import {
createStore,applyMiddleware,compose} from 'redux';import thunk from 'redux-thunk';import reducer from './reducer';// redux-devtools 配置const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
}) : compose;const enhancer = composeEnhancers( // 使用中间件 thunk applyMiddleware(thunk));const store = createStore(reducer,enhancer);export default store;

reducer.js

// reducer.js// 分模块Reducerimport {
combineReducers } from 'redux-immutable';import {
reducer as homeReducer } from '../views/Home/store/';import {
reducer as layoutReducer } from '../components/layout/store';import {
reducer as aboutReducer } from '../views/About/store';import {
reducer as detailsReducer } from '../views/Details/store';const reducer = combineReducers({
home: homeReducer, layout:layoutReducer, about:aboutReducer, details:detailsReducer});export default reducer;
十一、页面模块与组件模块

因页面过多,这里只展示首页模块,其他逻辑思想大差不差,如果想详细了解的可以加我微信。

在views文件夹创建一个Home文件夹。依次创建如下图所示文件:
在这里插入图片描述
index.jsx
页面组件。

// index.jsximport React, {
useEffect, Fragment } from 'react';import {
Link } from 'react-router-dom';import {
connect } from 'react-redux';import {
Pagination, Spin } from 'antd';import styled from 'styled-components';import {
LeftView, RightView, Item, ContentBox, InfoBox, Meta, Title, ImgBox, SidebarBlock, ImgBlock, MoreBlock } from './styleJs/style';import {
actionsCreator } from './store/';const mapStateToProps = (state) => {
return {
datalist: state.getIn(['home', 'datalist']), page: state.getIn(['home', 'page']), defaultCurrent: state.getIn(['home', 'defaultCurrent']) }};const mapDispatchToProps = (dispatch) => {
return {
getdata(v) {
dispatch(actionsCreator.getList(v)) }, pageChange(v) {
dispatch(actionsCreator.changePage(v)) } }};const Loading = styled.div` text-align: center; margin:34vh 0;`;const Home = (props) => {
const {
datalist, getdata, page, defaultCurrent, pageChange } = props; const newList = datalist.toJS(); useEffect(() => {
getdata(defaultCurrent); }, [defaultCurrent, getdata]) return (
{
page === 0 ?
:
{
newList.map((item) => {
return (
{ item.tab}
{<div></div> item.title}
")).replace("
}
© {
new Date().getFullYear()}
maomin.club版权所有
公安备案号 37021302000701号
鲁ICP备19020856号-1
)}export default connect(mapStateToProps, mapDispatchToProps)(Home);

styles/style.js

home页面的样式。

// style.jsimport styled, {
keyframes } from 'styled-components';const fadeIn = keyframes` from { opacity:0; } to { opacity:1; }`export const LeftView = styled.div` border-radius: 2px; width: 700px; margin-right: 21.667rem; border:1px solid #f4f4f4; background:#fff; box-sizing:border-box; animation: ${
fadeIn} 1s ease-in;`export const RightView = styled.div` position: absolute; top: 0; right: 0; width:20rem; @media (max-width: 960px){ display: none; }`export const Item = styled.div` border-bottom: 1px solid rgba(178,186,194,.15);`export const ContentBox = styled.div` display: flex; align-items: center; padding: 1.5rem 2rem;`export const InfoBox = styled.div` flex: 1 1 auto; display: flex; flex-direction: column; justify-content: center; min-width: 0;`export const Meta = styled.div` color: #b2bac2;`export const Title = styled.div` margin: 1rem 0 1rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-size: 1.4rem; font-weight: 600; line-height: 1.2; color: #2e3135;`export const ImgBox = styled.div` background-image:url('${
props => props.srci}'); background-repeat: no-repeat; background-size: cover; flex: 0 0 auto; width: 5rem; height: 5rem; background-color:#f4f4f4; margin-left: 2rem; background-color: #fff; border-radius: 2px; background-position: 50%; animation: ${
fadeIn} 1s ease-in;`export const SidebarBlock = styled.div` background-color: #fff; box-shadow: 0 1px 2px 0 rgba(0,0,0,.05); border-radius: 2px; margin-bottom: 1.3rem; font-size: 1.16rem; line-height: 1.29; color: #333;`export const ImgBlock = styled.img` width:100%; animation: ${
fadeIn} 1s ease-in;`export const MoreBlock =styled.div` background-color: transparent; box-shadow: none; a{ display:block; line-height:22px; text-decoration: none; cursor: pointer; color: #909090; } div { line-height:22px; } span{ margin:0 5px; }`

store/actionsCreator.js

react-thunk作用:使我们可以在action中返回函数,而不是只能返回一个对象。然后我们可以在函数中做很多事情,比如发送异步的ajax请求。

// actionsCreator.jsimport {
actionsTypes} from './index';import {
getListapi} from '../../../request/api';import {
fromJS} from 'immutable';const dataList =(data,page) =>{
return {
type:actionsTypes.DATA_LIST, data:fromJS(data), page:fromJS(page) }};const currentPage = (p) =>{
return {
type:actionsTypes.CHANGE_PAGE, current:p } }export const getList = (p) =>{
return (dispatch) =>{
let postData ={
page:p } getListapi(postData).then((res)=>{
const data = res.data; const page = res.page; const action = dataList(data,page); dispatch(action); }).catch((err)=>{
console.log(err); }) }};export const changePage=(page)=>{
return (dispatch) =>{
const action = currentPage(page); dispatch(action); } }

store/actionsTypes.js

// actionsTypes.jsexport const DATA_LIST = 'home/DATA_LIST';export const CHANGE_PAGE = 'home/CHANGE_PAGE';

store/index.js

home页面的store配置。

// index.jsimport reducer from './reducer';import * as actionsTypes from './actionsTypes';import * as actionsCreator from './actionsCreator';export {
reducer, actionsCreator,actionsTypes};

store/reducer.js

由于是不可变的,可以放心的对对象进行任意操作。在 React 开发中,频繁操作state对象或是 store ,配合 immutableJS 快、安全、方便。

// reducer.jsimport {
actionsTypes} from './index';import {
fromJS} from 'immutable';let defaultState = fromJS({
datalist: [], page:0, defaultCurrent:1});export default (state = defaultState, action) => {
switch (action.type) {
case actionsTypes.DATA_LIST: return state.merge({
'datalist':action.data, 'page':action.page }) case actionsTypes.CHANGE_PAGE: return state.set('defaultCurrent',action.current) default: return state; }};

后台角度

主要资源

  • https
  • fs
  • path
  • koa
  • koa-router
  • koa2-cors
  • jsonwebtoken
  • koa-body
  • koa-static
  • koa-sslify
  • mysql
  • node-schedule

源码

后台主要是用了Koa模块,下面的源码是基于https环境。数据库是采用了创建地址池的方法,数据库的连接池负责分配,管理和释放数据库链接的。它允许应用程序重复使用一个现有的数据库的链接。而不是重新创建一个。地址池这里可以优化,这里为了看的更清楚,统一放在了一个文件里。具体详解请看下面的注释。

// app.jsvar https = require("https");//https服务var fs = require("fs");var path = require('path');var Koa = require('koa');var Router = require('koa-router');var cors = require('koa2-cors');var jwt = require('jsonwebtoken');var koaBody = require('koa-body'); //文件保存库var serve = require('koa-static');var enforceHttps = require('koa-sslify').default;var mysql = require('mysql');var schedule = require('node-schedule');var app = new Koa();app.use(enforceHttps());var router = new Router();var secretkey = ''; // token的key// 这是我的https配置文件可忽略var options = {
key: fs.readFileSync('https/2_www.maomin.club.key'), cert: fs.readFileSync('https/1_www.maomin.club_bundle.crt')}// 存文件配置const home = serve(path.join(__dirname) + '/public/');app.use(home);app.use(koaBody({
multipart: true}));// 跨域const allowOrigins = [ "https://www.maomin.club/"];app.use(cors({
origin: function (ctx) {
if (allowOrigins.includes(ctx.header.origin)) {
return ctx.header.origin; } return false; }, exposeHeaders: ['WWW-Authenticate', 'Server-Authorization'], maxAge: 5, credentials: true, withCredentials: true, allowMethods: ['GET', 'POST', 'DELETE'], allowHeaders: ['Content-Type', 'Authorization', 'Accept'],}));// 创建地址池var pool = mysql.createPool({
host: '', // 主机 port: 3306, // 端口 user: '', // 用户 password: '', // 密码 database: '', // 数据库 multipleStatements: true, // 允许每个mysql语句有多条查询 connectionLimit: 100 // 最大连接数})// 数据库操作// 定时置3schedule.scheduleJob('10 0 0 * * *', function () {
console.log('update!') var updateStr = 'UPDATE login SET count = ?'; var modSqlParams = [3]; pool.getConnection(function (err, conn) {
if (err) {
//do something console.log(err); } conn.query(updateStr, modSqlParams, function (err, results) {
if (err) {
//do something throw err; } conn.release(); //释放连接 }) })});// 检查tokenconst checkToken = function (tokenid) {
return new Promise((resolve) => {
if (tokenid) {
//校验tokenid jwt.verify(tokenid, secretkey, function (err, decoded) {
// decoded:指的是tokneid解码后用户信息 if (err) {
//如果tokenid过期则会执行err的代码块 resolve({
success: false, resultCode: 2, message: err }); } else {
resolve("notime"); } }) } else {
resolve({
success: false, resultCode: 3, message: '未登录' }) } })}let json = {
};// 通用查询方法const query = function (sql) {
return new Promise((resolve, reject) => {
pool.getConnection(function (err, conn) {
if (err) {
//do something console.log(err); } conn.query(sql, function (err, results) {
if (err) {
//do something reject(error); } else {
//return data or anything you want do! resolve(results); } conn.release(); //释放连接 }) }) })}// 分页let all = "";const page = function (sql, p) {
return new Promise((resolve, reject) => {
pool.getConnection(function (err, conn) {
if (err) {
//do something console.log(err); } conn.query(sql, function (err, results) {
if (err) {
//do something reject(error); } else {
//return data or anything you want do! var allCount = results[0][0]['COUNT(*)']; all = allCount; var allPage = parseInt(allCount) / p; var pageStr = allPage.toString(); if (pageStr.indexOf('.') > 0) {
allPage = parseInt(pageStr.split('.')[0]) + 1; } var List = results[1]; resolve(List) } conn.release(); //释放连接 }) }) })}// 登录方法const logQuery = function (userStr, token) {
return new Promise((resolve, reject) => {
pool.getConnection(function (err, conn) {
if (err) {
//do something console.log(err); } conn.query(userStr, function (err, results) {
if (err) {
//do something reject(error); } else {
//return data or anything you want do! if (results.length !== 0) {
var dataString = JSON.stringify(results); var data = JSON.parse(dataString); json['message'] = '登录成功'; json['resultCode'] = 200; json['username'] = data[0].username; json['token'] = token; var updateStr = 'UPDATE login SET token = ? WHERE Id = ?'; var modSqlParams = [token, data[0].id]; pool.getConnection(function (err, conn) {
if (err) {
//do something console.log(err); } conn.query(updateStr, modSqlParams, function (err, results) {
if (err) {
//do something throw err; } conn.release(); //释放连接 }) }) resolve(json); } else {
resolve({
success: false, resultCode: 4, message: '请输入正确的账号或密码' }); } } conn.release(); //释放连接 }) }) })}//注册方法const regQuery = function (userStr, name, passwd, token, count) {
return new Promise((resolve, reject) => {
pool.getConnection(function (err, conn) {
if (err) {
//do something console.log(err); } conn.query(userStr, function (err, result) {
if (err) {
//do something reject(error); } else {
//return data or anything you want do! if (result.length > 0) {
json['message'] = '用户已经存在'; json['resultCode'] = 1; } else {
json['message'] = '注册成功'; json['token'] = token; json['username'] = name; json['count'] = count; json['resultCode'] = 200; var insertStr = `insert into login (username, password,token,count) values ("${
name}", "${
passwd}","${
token}","${
count}")`; pool.getConnection(function (err, conn) {
if (err) {
//do something console.log(err); } conn.query(insertStr, function (err, results) {
if (err) {
//do something throw err; } conn.release(); //释放连接 }) }) } resolve(json) } conn.release(); //释放连接 }) }) })}// 评论方法const commentQuery = function (userStr, aid) {
return new Promise((resolve, reject) => {
pool.getConnection(function (err, conn) {
if (err) {
//do something console.log(err); } conn.query(userStr, async function (err) {
if (err) {
//do something reject(error); } else {
//return data or anything you want do! json['message'] = '评论成功'; json['success'] = true; let sql = `select aid,username,com from comment where aid="${
aid}"`; let results = await query(sql); json['data'] = results; resolve(json); } conn.release(); //释放连接 }) }) })}// 发圈子方法const setCount = function (userStr, username, imgsrc, inputValue, td) {
return new Promise((resolve, reject) => {
pool.getConnection(function (err, conn) {
if (err) {
//do something console.log(err); } conn.query(userStr, function (err, results) {
if (err) {
//do something reject(error); } else {
//return data or anything you want do! var dataString = JSON.stringify(results); var data = JSON.parse(dataString); if (data[0].count > 0) {
var newCount = data[0].count - 1; json['message'] = '发表成功'; json['resultCode'] = 200; json['success'] = true; json['count'] = newCount; // 次数减一 var updateStr = 'UPDATE login SET count = ? WHERE username = ?'; var modSqlParams = [newCount, username]; pool.getConnection(function (err, conn) {
if (err) {
//do something console.log(err); } conn.query(updateStr, modSqlParams, function (err) {
if (err) {
//do something throw err; } conn.release(); //释放连接 }) }) // 存入圈子数据库 var insetStr = `insert into circle (username, imgsrc, inputValue, td) values ("${
username}","${
imgsrc}","${
inputValue}","${
td}")` pool.getConnection(function (err, conn) {
if (err) {
//do something console.log(err); } conn.query(insetStr, modSqlParams, function (err) {
if (err) {
//do something throw err; } conn.release(); //释放连接 }) }) resolve(json); } else {
resolve({
success: false, resultCode: 5, message: '操作太频繁,请明天再发哦' }); } } conn.release(); //释放连接 }) }) })}// 用户信息方法const getInfo = function (tokenid) {
return new Promise((resolve) => {
if (tokenid) {
//校验tokenid jwt.verify(tokenid, secretkey, function (err, decoded) {
// decoded:指的是tokneid解码后用户信息 if (err) {
//如果tokenid过期则会执行err的代码块 resolve({
success: false, resultCode: 2, message: err }); } else {
resolve(decoded); } }) } else {
resolve({
success: false, resultCode: 3, message: '未登录' }) } })}// 获取用户信息router.post('/getinfo', async (ctx, next) => {
var tokenid = ctx.request.body.token; let results = await getInfo(tokenid); ctx.body = results;})// 注册router.post('/register', async (ctx, next) => {
let name = ctx.request.body.username; let passwd = ctx.request.body.password; let count = 3; let token = jwt.sign({
username: name }, secretkey, {
expiresIn: 60 * 60 * 12 // 12h }); let userStr = `select * from login where username="${
name}"`; let results = await regQuery(userStr, name, passwd, token, count); ctx.body = results});// 登录router.post('/login', async (ctx, next) => {
let name = ctx.request.body.username; let passwd = ctx.request.body.password; let token = jwt.sign({
username: name }, secretkey, {
expiresIn: 60 * 60 * 12 // 12h }); let userStr = `select username,password,id from login where username="${
name}" and password="${
passwd}"`; let results = await logQuery(userStr, token); ctx.body = results});// 写评论router.post('/comment', async (ctx, next) => {
let aid = ctx.request.body.aid; let username = ctx.request.body.username; let com = ctx.request.body.com; let td = ctx.request.body.td; var tokenid = ctx.request.headers.authorization//获取前端请求头发送过来的tokenid let trueFlase = await checkToken(tokenid); if (trueFlase === "notime") {
let userStr = `insert into comment (aid, username, com, td) values ("${
aid}","${
username}","${
com}","${
td}")` let results = await commentQuery(userStr, aid); ctx.body = results; } else {
ctx.body = trueFlase; }})// 获取评论router.post('/getComment', async (ctx, next) => {
var start = (ctx.request.body.page - 1) * 3; let aid = ctx.request.body.aid; var count = `SELECT * FROM comment WHERE aid="${
aid}"`; let allnum = await query(count); const len = allnum.length; var sql = `SELECT COUNT(*) FROM comment ORDER BY id DESC;SELECT * FROM comment WHERE aid="${
aid}" ORDER BY id DESC limit ${
start},3`; let results = await page(sql, 3); ctx.body = {
data: results, page: len }})// 写文章router.post('/write', async (ctx, next) => {
let title = ctx.request.body.title; let tab = ctx.request.body.tab; let context = ctx.request.body.context; var tokenid = ctx.request.headers.authorization//获取前端请求头发送过来的tokenid let trueFlase = await checkToken(tokenid); if (trueFlase === "notime") {
var userStr = `insert into article (title, tab, context) values ("${
title}","${
tab}","${
context}")` pool.getConnection(function (err, conn) {
if (err) {
//do something console.log(err); } conn.query(userStr, function (err) {
if (err) {
//do something throw err; } conn.release(); //释放连接 }) }) ctx.body = {
success: true, message: '发送成功' } // echo the result back } else {
ctx.body = trueFlase; }});// 写文章上传图片router.post('/uploadImg', async (ctx, next) => {
if (ctx.request.files.file) {
var file = ctx.request.files.file; // 创建可读流 var reader = fs.createReadStream(file.path); // 修改文件的名称 var myDate = new Date(); var newFilename = myDate.getTime() + '.' + file.name.split('.')[1]; var targetPath = path.join(__dirname, './public/images/') + `${
newFilename}`; //创建可写流 var upStream = fs.createWriteStream(targetPath); // 可读流通过管道写入可写流 reader.pipe(upStream); var imgsrc = 'https://www.maomin.club/myblog/images/' + newFilename; ctx.body = {
success: true, imgsrc: imgsrc }; }})// 发圈子router.post('/circle', async (ctx, next) => {
if (ctx.request.files.file) {
var file = ctx.request.files.file; // 创建可读流 var reader = fs.createReadStream(file.path); // 修改文件的名称 var myDate = new Date(); var newFilename = myDate.getTime() + '.' + file.name.split('.')[1]; var targetPath = path.join(__dirname, './public/images/') + `${
newFilename}`; //创建可写流 var upStream = fs.createWriteStream(targetPath); // 可读流通过管道写入可写流 reader.pipe(upStream); var imgsrc = 'https://www.maomin.club/myblog/images/' + newFilename; } else {
var imgsrc = "" } let username = ctx.request.body.username; let inputValue = ctx.request.body.inputValue; let td = ctx.request.body.td; var tokenid = ctx.request.headers.authorization//获取前端请求头发送过来的tokenid let trueFlase = await checkToken(tokenid); if (trueFlase === "notime") {
let userStr = `select count from login where username="${
username}"`; let results = await setCount(userStr, username, imgsrc, inputValue, td); ctx.body = results; } else {
ctx.body = trueFlase; }});// 获取圈子router.post('/getCircle', async (ctx, next) => {
var start = (ctx.request.body.page - 1) * 3; var sql = 'SELECT COUNT(*) FROM circle ORDER BY id DESC; SELECT * FROM circle ORDER BY id DESC limit ' + start + ',3'; let results = await page(sql, 3); ctx.body = {
data: results, page: all }});// 获取文章列表(分页)router.post('/getList', async (ctx, next) => {
var start = (ctx.request.body.page - 1) * 6; var sql = 'SELECT COUNT(*) FROM article ORDER BY id DESC; SELECT * FROM article ORDER BY id DESC limit ' + start + ',6'; let results = await page(sql, 6); ctx.body = {
data: results, page: all }});// 获取文章列表(全部)router.get('/getAllList', async (ctx, next) => {
var sql = "select * from article"; let results = await query(sql); ctx.body = results});// 获取文章详情router.post('/getDetails', async (ctx, next) => {
const id = ctx.request.body.id; var sql = `select * from article where id="${
id}"`; let results = await query(sql); ctx.body = results});//使用路由中间件app .use(router.routes()) .use(router.allowedMethods());https.createServer(options, app.callback()).listen(8410);console.log('服务器运行中')

作者:Vam的金豆之路

主要领域:前端开发

我的微信:maomin9761

微信公众号:前端历劫之路


转载地址:https://maomin.blog.csdn.net/article/details/106782300 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:使用React.js写一个类似单选框与复选框的功能
下一篇:React与Koa一起打造一个功能丰富的全栈个人博客(业务篇)

发表评论

最新留言

做的很好,不错不错
[***.243.131.199]2024年04月15日 15时52分16秒