first commit

This commit is contained in:
编码猿
2024-09-27 02:06:13 +08:00
commit 852d94fbb9
36760 changed files with 3274413 additions and 0 deletions

View File

@@ -0,0 +1,12 @@
const express = require('express')
const router = express.Router()
const http = require('./http')
const utils = require('./utils')
const config = require('./config')
const mysql = require('yn-mysql-utils')
const md5 = require('md5')
let db = new mysql(config.mysql)
module.exports = { express , router, http , utils, config, db, md5}

View File

@@ -0,0 +1,73 @@
var createError = require('http-errors');
var express = require('express');
var loadRouter = require('express-load-router');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
var config = require('./config');
var session = require('express-session')
var app = express();
var classRouter = require('./controller/class');
var IndexRouter = require('./controller/index');
var InfoRouter = require('./controller/info');
var likeActionRouter = require('./controller/user/likeAction');
var loginActionRouter = require('./controller/user/loginAction');
// 后端允许前端跨域
app.all('*', (req, res, next) => {
res.header("Access-Control-Allow-Origin", "*");
res.header('Access-Control-Allow-Methods', 'PUT, GET, POST, DELETE, OPTIONS');
res.header("Access-Control-Allow-Headers", "X-Requested-With");
res.header('Access-Control-Allow-Headers', 'Content-Type');
next();
});
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
// 使用 session 中间件
app.use(session({
secret : 'secret', // 对session id 相关的cookie 进行签名
resave : false,
saveUninitialized: true, // 是否保存未初始化的会话
cookie : {
maxAge : 1000 * 60 * 10, // 设置 session 的有效时间,单位毫秒
},
}));
app.use(`${config.ApiBaseUrl}`, classRouter)
app.use(`${config.ApiBaseUrl}`, IndexRouter)
app.use(`${config.ApiBaseUrl}`, InfoRouter)
app.use(`${config.ApiBaseUrl}`, loginActionRouter)
app.use(`${config.ApiBaseUrl}`, likeActionRouter)
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
res.json({
status: 404,
message: err.message
})
});
module.exports = app;

View File

@@ -0,0 +1,87 @@
#!/usr/bin/env node
/**
* Module dependencies.
*/
var app = require('../app');
var debug = require('debug')('xiaoshuo-backend:server');
var http = require('http');
/**
* Get port from environment and store in Express.
*/
var port = normalizePort(process.env.PORT || '3000');
app.set('port', port);
/**
* Create HTTP server.
*/
var server = http.createServer(app);
/**
* Listen on provided port, on all network interfaces.
*/
server.listen(port);
server.on('error', onError);
server.on('listening', onListening);
/**
* Normalize a port into a number, string, or false.
*/
function normalizePort(val) {
var port = parseInt(val, 10);
if (isNaN(port)) {
// named pipe
return val;
}
if (port >= 0) {
// port number
return port;
}
return false;
}
/**
* Event listener for HTTP server "error" event.
*/
function onError(error) {
if (error.syscall !== 'listen') {
throw error;
}
var bind = typeof port === 'string'
? 'Pipe ' + port
: 'Port ' + port;
// handle specific listen errors with friendly messages
switch (error.code) {
case 'EACCES':
console.error(bind + ' requires elevated privileges');
process.exit(1);
break;
case 'EADDRINUSE':
console.error(bind + ' is already in use');
process.exit(1);
break;
default:
throw error;
}
}
/**
* Event listener for HTTP server "listening" event.
*/
function onListening() {
console.log("后端api接口运行在http://127.0.0.1:3000/")
}

View File

@@ -0,0 +1,66 @@
module.exports = {
ApiBaseUrl: '/api/json/1.0/',
getIpAddres: 'http://2019.ip138.com/ic.asp',
mysql: {
host : '127.0.0.1',
user : 'root',
password : 'lb714500',
database : 'Novel',
connectionLimit : 10
},
TargetUrl: {
baseUrl: 'http://www.xbiquge.la/',
class: {
title: 'fenlei/',
fantasy: {
id: '1_',
url: 'xuanhuanxiaoshuo/'
},
though: {
id: '2_',
url: 'xiuzhenxiaoshuo/'
},
urban: {
id: '3_',
url: 'dushixiaoshuo/'
},
passThrough: {
id: '4_',
url: 'chuanyuexiaoshuo/'
},
game: {
id: '5_',
url: 'wangyouxiaoshuo/'
},
scienceFiction: {
id: '6_',
url: 'kehuanxiaoshuo/'
},
hotlist: {
url: 'paihangbang/'
}
}
},
sql: {
index: {
selectData: 'SELECT * FROM novel_book WHERE book_key = ? AND book_page = ?',
selectSave: 'SELECT * FROM novel_book WHERE book_key = ? AND book_page = ? AND book_hot = ?',
inrset: 'INSERT INTO novel_book(book_title,book_introduce,book_img,book_author,book_time,book_url,book_hot,book_page,book_key) VALUES (?,?,?,?,?,?,?,?,?)',
delete: 'DELETE FROM novel_book WHERE book_key = ? AND book_hot = 1'
},
user: {
getUserInfo: 'SELECT * FROM novel_user WHERE id = ?',
checkUser: 'SELECT * FROM novel_user WHERE user_phone = ?',
login: 'SELECT id,user_phone,user_ip FROM novel_user WHERE user_phone = ? AND user_pwd = ?',
userReg: 'INSERT INTO novel_user(user_phone,user_pwd,user_ip) VALUES (?,?,?)',
checkBook: 'SELECT * FROM novel_collection WHERE collection_user = ? AND collection_bookid = ?',
insertBook: 'INSERT INTO novel_collection(collection_user,collection_bookid) VALUES (?,?)',
selectBook: 'SELECT * FROM novel_collection WHERE collection_user = ?',
deleteBook: 'DELETE FROM novel_collection WHERE collection_user = ? AND collection_bookid = ?'
}
},
apiStatus: {
status: 200,
data: null
}
}

View File

@@ -0,0 +1,70 @@
const { http, utils, db, config,router } = require("../../_");
// http://127.0.0.1:3000/api/json/1.0/class?class=1&page=1
router.get('/class', async function(req,res,next){
// 请求数据 and 返回dom数据
let $ = await utils.AnalyticalData({ class:req.query.class, page: req.query.page } ),
left = [],
father = null;
/**
* 存在 action 爬取每个分类下面的更多小说
*/
if(req.query.hasOwnProperty("action")){
// http://127.0.0.1:3000/api/json/1.0/class?class=2&page=1&action=more
father = $("#newscontent .l ul li")
father.each(function(){
left.push({
title: $(this).find(".s2 a").text(),
url: $(this).find(".s2 a").attr("href")
})
})
let newList = []
left.forEach(async (item) => {
let $ = await utils.AnalyticalData({ siteUrl: item.url } )
newList.push({
title: $('#maininfo #info').find('h1').text(),
imgURL: $('#sidebar #fmimg').find('img').attr('src'),
introduce: $('#maininfo #intro').children().last().text(),
author: ($('#maininfo #info').find('h1+p').text()).substring(7),
url: item.url
})
if(newList.length == 30) {
return res.json(newList)
}
})
/**
* 不存在 action 爬取每个分类小面的推荐小说
*/
} else {
// http://127.0.0.1:3000/api/json/1.0/class?class=2
father = $("#hotcontent .ll .item")
father.each(async function() {
left.push({
title: $(this).find("dl dt a").text(),
imgURL: $(this).find(".image img").attr("src"),
author: $(this).find("dl dt span").text(),
introduce: $(this).find("dl dd").text(),
url: $(this).find(".image a").attr("href")
})
})
return res.json(left)
}
})
module.exports = router

View File

@@ -0,0 +1,8 @@
const { http, utils, db, config,router } = require("../../_");
router.get('/index', async function(req, res, next) {
console.log('index');
})
module.exports = router

View File

@@ -0,0 +1,66 @@
const { http, utils, db, config,router } = require("../../_");
/**
* 小说详情页面
* 地址http://127.0.0.1:3000/api/json/1.0/info?id=28/28039/
*/
router.get('/info', async (req, res, next) => {
let id = req.query.id,
action = req.query.action == undefined ? null: req.query.action,
InfoData = {};
try {
let $ = utils.returnDOM(await http.get({ url: `${config.TargetUrl.baseUrl}${id}` }));
InfoData.title = $("#info").find("h1").text()
InfoData.author = $("#info p").eq(0).text()
InfoData.updateTime = $("#info p").eq(2).text()
InfoData.imgUrl = $("#fmimg img").attr("src")
InfoData.introduce = $("#intro p").eq(1).text()
InfoData.oldChapter = []
let father = $("#list dl dd a");
father.each(function (item) {
InfoData.oldChapter.push({
chapterTitle: $(this).text(),
chapterUrl: $(this).attr("href"),
})
})
if(action == null) {
InfoData.newChapter = {}
InfoData.newChapter.newTitle = $("#info p").eq(3).find("a").text()
InfoData.newChapter.newUrl = $("#info p").eq(3).find("a").attr("href")
utils.returnJson(200,InfoData,res)
} else {
res.json(InfoData)
}
}catch (e) {
console.log(e)
}
})
/**
* 阅读小说接口
* 地址http://127.0.0.1:3000/api/json/1.0/read?id=/28/28039/13633573.html
*/
router.get('/read', async (req, res, next) => {
let id = req.query.id;
try {
let $ = utils.returnDOM(await http.get({ url: `${config.TargetUrl.baseUrl}${id}` }));
utils.returnJson(200,{
title: $(".bookname h1").text(),
content: (unescape($("#content").html().replace(/&#x/g,'%u').replace(/;/g,''))).replace(/%uA0%uA0%uA0%uA0|\n/gi,'  ')
},res)
}catch (e) {
console.log(e)
}
})
module.exports = router

View File

@@ -0,0 +1,83 @@
const { http, utils, db, config,router,md5 } = require("../../_");
/**
* 检查用户是否已经添加过某本小说
*/
router.post('/user/checkbook', async function (req, res, next) {
let userid = req.body.userid,
bookid = req.body.bookid;
let Check = await db.Query({ sql: config.sql.user.checkBook, par: [ userid, bookid ] });
if(Check.length) {
utils.returnJson(200,"已收藏",res);
}else {
utils.returnJson(200,"未收藏",res);
}
});
/**
* 用户添加小说到书架
*/
router.post('/user/addbok', async function (req, res, next) {
let userid = req.body.userid,
bookid = req.body.bookid;
let add = await db.Query({ sql: config.sql.user.insertBook, par: [ userid, bookid ] });
if(add.message.length == 0) {
utils.returnJson(200,"添加成功",res);
}else {
utils.returnJson(404,"添加失败",res);
}
})
/**
* 获取用户书架中收藏的书
* http://127.0.0.1:3000/api/json/1.0/user/getbook?userid=1
*/
router.get('/user/getbook', async function (req, res, next) {
let userid = req.query.userid;
let getBook = await db.Query({ sql: config.sql.user.selectBook, par: [ userid ] }),
infoData = [],
keys = 0;
if(getBook.length ==0) {
utils.returnJson(404,"数据没有数据",res);
}else {
getBook.forEach(async (value , index) => {
infoData.push(await http.get({ url: `http://127.0.0.1:3000/api/json/1.0/info?action=back&id=${value.collection_bookid}` }));
infoData[keys].bookid = value.collection_bookid
keys++;
if(keys == getBook.length) {
utils.returnJson(200,infoData,res);
}
})
}
})
/**
* 获取用户书架中收藏的书
* http://127.0.0.1:3000/api/json/1.0/user/deletebook?uid=1&bid=26/26966/
*/
router.get('/user/deletebook', async function (req, res, next) {
let userid = req.query.uid,
bookid = req.query.bid;
let delbook = await db.Query({ sql: config.sql.user.deleteBook, par: [ userid, bookid ] });
if(delbook.message.length == 0) {
utils.returnJson(200,"删除成功",res);
}else {
utils.returnJson(404,"删除失败",res);
}
})
module.exports = router

View File

@@ -0,0 +1,65 @@
const { http, utils, db, config,router,md5 } = require("../../_");
/**
* 用户登录
*/
router.post('/user/login', async function (req, res, next) {
let UserName = req.body.name,
UserPwd = req.body.pwd,
IP = req.body.ip;
let UserInfo = await db.Query({ sql: config.sql.user.login, par: [ UserName, md5(UserPwd) ] });
if(UserInfo.length) {
req.session.userinfo = UserInfo;
utils.returnJson(200,UserInfo[0],res);
}else {
utils.returnJson(404,'账号和密码不匹配',res)
}
});
/**
* 注册账户
*/
router.post('/user/reg', async (req, res, next) => {
let userName = req.body.name,
UserPwd = req.body.pwd;
if(/^[1][3,4,5,6,7,8,9][0-9]{9}$/.test(userName)) {
if( (await db.Query({ sql: config.sql.user.checkUser, par: [userName] })).length ) {
utils.returnJson(404,'手机号已存在',res)
}else {
let userReg = await db.Query({ sql: config.sql.user.userReg, par: [userName, md5(UserPwd), await utils.GetIp()] })
req.session.userinfo = await db.Query({ sql: config.sql.user.getUserInfo, par: [userReg.insertId] })
utils.returnJson(200,'regSuccess',res)
}
}else {
utils.returnJson(404,'手机号格式不正确',res)
}
});
/**
* 退出登录
*/
router.post('/user/outLogin', async (req, res, next) => {
});
/**
* 检查用户是否登录 前端检查 + 后端session检查
*/
router.get('/user/checkLogin', async (req, res, next) => {
console.log(req.session.userinfo)
// if(req.session.userinfo.length) {
// utils.returnJson(200,'Logined',res)
// }else {
// utils.returnJson(404,'noLogin',res)
// }
});
/**
* 重置密码
*/
router.post('/user/restPwd', async (req, res, next) => {
});
module.exports = router

View File

@@ -0,0 +1,15 @@
# POST http://127.0.0.1:3000/api/json/1.0/user/reg HTTP/1.1
# content-type: application/json
# {
# "name": "15155145354",
# "pwd": "123456"
# }
POST http://127.0.0.1:3000/api/json/1.0/user/login HTTP/1.1
content-type: application/json
{
"name": "15155145354",
"pwd": "123456"
}

View File

@@ -0,0 +1,19 @@
const axios = require('axios');
const config = require('./../config');
// 配置爬虫的基地址
axios.defaults.baseURL = config.TargetUrl.baseUrl;
module.exports = {
get(params) {
return new Promise(function(resolve,reject){
axios.get(params.url)
.then( (success) => {
resolve(success.data);
})
.catch( (error) => {
reject(error);
});
})
}
}

View File

@@ -0,0 +1,21 @@
{
"name": "xiaoshuo-backend",
"version": "1.2.1",
"private": true,
"scripts": {
"dev": "node-dev ./bin/www"
},
"dependencies": {
"md5": "^2.2.1",
"axios": "^0.18.0",
"cheerio": "^1.0.0-rc.2",
"cookie-parser": "~1.4.3",
"debug": "~2.6.9",
"express": "~4.16.0",
"express-load-router": "^2.1.4",
"express-session": "^1.15.6",
"http-errors": "~1.6.2",
"morgan": "~1.9.0",
"yn-mysql-utils": "^1.0.4"
}
}

View File

@@ -0,0 +1,38 @@
const config = require('../config')
const http = require('../http')
const cheerio = require('cheerio')
module.exports = {
async AnalyticalData(par) {
let urls = '';
if(par.hasOwnProperty('page')) {
urls = `${config.TargetUrl.class.title}${par.class}_${par.page}.html`
} else if(par.hasOwnProperty('siteUrl')){
urls = par.siteUrl
} else {
urls = config.TargetUrl.class.hotlist
}
// 网络请求获取 目标 页面的 html源码
return this.returnDOM(await http.get({ url: urls }))
},
returnDOM(data) {
return cheerio.load(data)
},
returnJson(status, data,res) {
config.apiStatus.status = status
config.apiStatus.data = data
res.json(config.apiStatus);
},
/**
* 返回客户端ip地址
* @returns {Promise<*|string>}
* @constructor
*/
async GetIp () {
let ipData = await http.get({ url: config.getIpAddres })
return ipData.match(/\[([\s\S]*?)\]/)[1]
}
}

View File

@@ -0,0 +1,54 @@
const { http, utils, db, config,router } = require("../../_");
// http://127.0.0.1:3000/api/json/1.0/class?class=1&page=1
router.get('/class', async function(req,res,next){
// 请求数据 and 返回dom数据
let $ = await utils.AnalyticalData({ class:req.query.class, page: req.query.page } ),
left = [],
father = null;
/**
* 存在 action 爬取每个分类下面的更多小说
*/
if(req.query.hasOwnProperty("action")){
// http://127.0.0.1:3000/api/json/1.0/class?class=2&page=1&action=more
father = $("#newscontent .l ul li")
father.each(function(){
left.push({
title: $(this).find(".s2 a").text(),
url: $(this).find(".s2 a").attr("href")
})
})
/**
* 不存在 action 爬取每个分类小面的推荐小说
*/
} else {
// http://127.0.0.1:3000/api/json/1.0/class?class=2
father = $("#hotcontent .ll .item")
// 插入数据之前先删除之前的数据
// await db.Query({ sql: config.sql.index.delete, par: [ req.query.class ] });
father.each(async function() {
left.push({
title: $(this).find("dl dt a").text(),
imgURL: $(this).find(".image img").attr("src"),
author: $(this).find("dl dt span").text(),
introduce: $(this).find("dl dd").text(),
url: $(this).find(".image a").attr("href")
})
})
}
return res.json(left)
})
module.exports = router