first commit

This commit is contained in:
编码猿
2024-09-27 01:31:25 +08:00
commit 07c46ac300
558 changed files with 37414 additions and 0 deletions

13
mApi/.gitignore vendored Executable file
View File

@@ -0,0 +1,13 @@
.DS_Store
node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln

1
mApi/.pydio Normal file
View File

@@ -0,0 +1 @@
a19759e8-d650-433b-8bf3-365303d5bf79

163
mApi/README.md Executable file
View File

@@ -0,0 +1,163 @@
# 1. 前言
---
为何写这个接口?其实本来不准备写的,这两天在练习 [TypeScript](https://www.tslang.cn/index.html),然后一直在用[TypeScript](https://www.tslang.cn/index.html)改造[express](http://www.expressjs.com.cn/)框架的应用生成器代码。改着改着发现有点无聊了。
无聊,而且没有挑战,怎么办?....Google翻墙后随意逛了下然后无意中看到一个比较`好欺负`的 学(dongzuo)习(pian)网站,然后抄起还没最终完善好的项目代码来练练手!
**注意:代码本无罪,请以学习的目的来正确看待本项目!**
## 2. 技术实现
---
项目基于[Node.js](http://nodejs.cn/),主要开发语言使用的是[TypeScript](https://www.tslang.cn/index.html),通过自动化构建工具[gulp](https://www.gulpjs.com.cn/)实现对`*.ts`文件的修改后的自动编译成`*.js`
使用模块 [gulp-uglify](https://www.npmjs.com/package/gulp-uglify) 压缩编译成功后的js代码然后通过模块 [gulp-babel](https://www.npmjs.com/package/gulp-babel)将编译后的`es6`转换成`es5`代码。
因为当我们还没写完某段ts代码gulp在后台编译的时候就会报错报错直接导致了gulp终止了进程也就无法对代码进行有效的实时编译。所以这里使用模块 [gulp-plumber](https://www.npmjs.com/package/gulp-plumber)在容易出错的地方写错误监听。这个插件可以阻止 gulp 插件发生错误导致进程退出并输出错误日志。
代码位置:`gulpfile.js` 第18行
```js
//编译ts文件为js
gulp.task('build-ts',function(){
return tsp.src()
.pipe(plumber()) // 捕捉编译ts编译的错误避免因为错误导致gulp进场关闭
.pipe(tsp())
.pipe(gulp.dest(PATHS.output));
});
```
因为我们要让gulp在后台监听并实时编译ts文件然后还想让我们的nodejs服务器同步我们ts文件编译结束能自动重启查看到效果所以我这里安装了模块 [concurrently](https://www.npmjs.com/package/concurrently) 。主要用于同步运行gulp任何和启动nodejs网站服务器。
`package.json`中代码:
```js
"scripts": {
...
"all": "concurrently \"gulp dev\" \"npm run dev\" "
}
```
所以请先全局安装 `concurrently`,命令:
> npm install -g concurrently
然后在安装你的网站依赖。
**项目里有些文件和代码和此次爬虫的代码无关,只是我之前还没写完的代码留在那,懒得删除!并不影响使用...**
## 3. 接口
---
> 测试阶段接口统一运行在 [http://127.0.0.1:3000](http://127.0.0.1:3000)
### 3.1: 获取网站主页数据
---
这个接口获取的是网站主页的数据,视频数量有限,不过是推荐的热门视频。
| 接口地址 | 请求方式 | 参数 |
| :-----------| :------- | ---------------------- |
| /index | GET | 无 |
- 实例:[http://127.0.0.1:3000/index](http://127.0.0.1:3000/index)
- 数据返回:
```json
{
status: 200,
length: 24,
time: "2018-08-11 23:22:26",
result: [
{
title: "SM调教最新**娘",
href: "https://www.**.com/Html/93/5949.html",
gif: "https://pic.**.com/Uploads/vod/2018-08-11/7.mp4.gif",
time: "2018-08-11",
mp4: "https://d.e**i.com/20180811/93/7/7.mp4"
},
{
title: "极品**女主播96年美女赵晓晓人这么瘦却有这么一对****",
href: "https://www.**.com/Html/91/5948.html",
gif: "https://pic.**.com/Uploads/vod/2018-08-11/924.mp4.gif",
time: "2018-08-11",
mp4: "https://d.**.com/20180811/91/924/924.mp4"
},
]
}
```
### 3.2: 获取网站各栏目的视频数据
---
这个接口是用来获取各个网站分类栏目的数据,视频数量和分类更多!
| 接口地址 | 请求方式 | 参数 |
| :------------------------ | :------- | ---------- |
| /videoClass | GET | type具体栏目编号(参考下面的表) <br> page: 页码|
- 实例:[http://127.0.0.1:3000/videoClass?type=60&page=1](http://127.0.0.1:3000/videoClass?type=60&page=1)
- 数据返回:
```json
{
status: 200,
length: 12,
time: "2018-08-11 23:35:55",
total: "共2642部 4/221",
result: [
{
title: "藝壇風雨錄-[粵語中字]",
href: "https://www.**.com/Html/109/5865.html",
gif: "https://pic.**.com/Uploads/vod/2018-08-06/11.mp4.jpg",
time: "2018-08-06",
mp4: "https://d.**.com/20180806/109/11/11.mp4"
},
{
title: "正点留学妹妹**视频被**漂亮",
href: "https://www.**.com/Html/87/5857.html",
gif: "https://pic.**.com/Uploads/vod/2018-08-05/64.mp4.gif",
time: "2018-08-05",
mp4: "https://d.**.com/20180805/87/64/64.mp4"
}
]
}
```
注意type 字段的具体编号请看下表:
| 栏目名称 | 栏目编号 |
| :--------| :------|
| 国产精品 | 60 |
| 中文字幕 | 94 |
| S级女优 | 100 |
| 欧美性爱 | 62 |
| 成人动漫 | 101 |
| 自拍偷拍 | 89 |
| 夫妻同房 | 87 |
| 开放90后 | 93 |
| 换妻游戏 | 90 |
| 网红主播 | 91 |
| 手机小视频 | 88 |
| 明星艳照门 | 92 |
| 经典三级 | 109 |
| 无码在线 | 110 |
| 熟女人妻 | 111 |
| 美颜巨乳 | 112 |
| 颜射吃精 | 113 |
| 丝袜制服 | 114 |

1
mApi/api/.pydio Normal file
View File

@@ -0,0 +1 @@
320ef53f-50f1-4129-ae6b-50dcb9c34f8e

127
mApi/api/api.md Executable file
View File

@@ -0,0 +1,127 @@
## API接口文档
---
> 接口统一运行在:[http://149.28.26.249:3000/](http://149.28.26.249:3000/)
注意:
- 1接口全部使用GET方式进行数据请求
- 2接口参数全部采用 RSA 非对称性加密,所以需要文件里面的 `rsa_pub.pem` 公钥对接口参数进行加密
- 3参数需要放在http请求头部的 `token` 中进行数据传递。
完整的流程:
举例,例如这边访问 分类接口:
- 1接口URL地址如下http://149.28.26.249:3000:3000/class
- 2接口参数拼接后type=60&page=1
- 3使用 `rsa_pub.pem` 对 步骤2 进行加密得到密文:
```txt
qwe87LzOllYQYOHnLERCw0u/xIoBePAuNa36iu4e27hMdLCrOVd/YXesT+Q5qXVmB/FC0vR5H7rHrW+MmdTX78ey546ua3pTSlTieO8J4QGcwE+YERPJ1mG8QKmNyOVVffQaa9ltOWaHcNUH5EKEjUIU2wDDIBT5Xxl1/4CsZPc=
```
- 4将加密后的密文放到http请求的头部`token`中,发送请求。
![token](./img/token.png)
- 5后端Node.js将会从header中拿到`token`然后用私钥解密,等到参数 type=60&page=1然后爬虫根据规则去爬取网站返回json数据。数据格式如下
![json](./img/json.png)
### 1首页
这个接口获取的是网站主页的数据,视频数量有限,不过是推荐的热门视频。
| 接口地址 | 请求方式 | 参数 |
| :-----------| :------- | ---------------------- |
| /index | GET | 无 |
数据返回:
```json
{
length: 24, // 数据长度
status: 200, // 请求状态 200成功
time: "2018-08-25 15:29:24", // 请求时间
result: [
{
// 视频的gif地址
gif: "https://pic.5ppav.com/Uploads/vod/2018-08-25/7.mp4.gif",
// 视频的详情页网址(一般前端用不上)
href: "https://www.974sp.com/Html/93/6153.html",
// 视频的 mp4 播放地址
mp4: "https://d.eeoai.com/20180825/93/7/7.mp4",
// 视频发布时间
time: "2018-08-25",
// 视频的标题
title: "台南召妓全紀錄~長X女大學生"
}
...
]
}
```
### 2: 分类接口
这个接口是用来获取各个网站分类栏目的数据,视频数量和分类更多!
| 接口地址 | 请求方式 | 参数 |
| :------------------------ | :------- | ---------- |
| /class | GET | type具体栏目编号(参考下面的表) <br> page: 页码(请求第几页)|
数据返回:
```json
{
length: 12, // 数据长度
status: 200, // 请求状态 200成功
time: "2018-08-25 15:29:24", // 请求时间
total: {
// 当前第几页
currentPage: 1,
// 该分类总计多少视频
totalNumber: 2725,
// 该分类总计有多少页
totalPage: 228
},
result: [
{
// 视频的gif地址
gif: "https://pic.5ppav.com/Uploads/vod/2018-08-25/7.mp4.gif",
// 视频的详情页网址(一般前端用不上)
href: "https://www.974sp.com/Html/93/6153.html",
// 视频的 mp4 播放地址
mp4: "https://d.eeoai.com/20180825/93/7/7.mp4",
// 视频发布时间
time: "2018-08-25",
// 视频的标题
title: "台南召妓全紀錄~長X女大學生"
}
...
]
}
```
注意type 字段的具体编号请看下表:
| 栏目名称 | 栏目编号 |
| :--------| :------|
| 国产精品 | 60 |
| 中文字幕 | 94 |
| S级女优 | 100 |
| 欧美性爱 | 62 |
| 成人动漫 | 101 |
| 自拍偷拍 | 89 |
| 夫妻同房 | 87 |
| 开放90后 | 93 |
| 换妻游戏 | 90 |
| 网红主播 | 91 |
| 手机小视频 | 88 |
| 明星艳照门 | 92 |
| 经典三级 | 109 |
| 无码在线 | 110 |
| 熟女人妻 | 111 |
| 美颜巨乳 | 112 |
| 颜射吃精 | 113 |
| 丝袜制服 | 114 |

1
mApi/api/img/.pydio Normal file
View File

@@ -0,0 +1 @@
44d5909a-9e22-4c51-a408-fc68f3a6abac

BIN
mApi/api/img/json.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

BIN
mApi/api/img/token.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

6
mApi/api/rsa_pub.pem Executable file
View File

@@ -0,0 +1,6 @@
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDUbu7pfBOXprboQ4P/jBDHYAQq
B7GTp/uzcDGiwHfdaGnjAxioNHgim9zO+/Bz/A/K9g5z7LKy/URl94QOKVpyTf1a
c1WF6E7N0H3ALkylc0rS5Ciy5FY9MdwhyhuzlBkYoBsd7tY8/QxuDuzZ/hwFooZs
GW9/C7DnjeEQ7RRRgQIDAQAB
-----END PUBLIC KEY-----

14
mApi/cluster.js Executable file
View File

@@ -0,0 +1,14 @@
var cluster = require('cluster');
if (cluster.isMaster) {
//Fork a worker to run the main program
for (var i = 0; i < 2; i++) var worker = cluster.fork();
} else {
//Run main program
require('./build/www.js');
console.log('worker is running');
}
cluster.on('death', function(worker) {
console.log('worker ' + worker.pid + ' died. restart...');
cluster.fork();
});

47
mApi/gulpfile.js Executable file
View File

@@ -0,0 +1,47 @@
let gulp = require('gulp');
let ts = require('gulp-typescript');
let uglify = require('gulp-uglify');
let babel = require('gulp-babel');
let plumber = require('gulp-plumber');
let tsp = ts.createProject('tsconfig.json');
//常量配置
const PATHS = {
scripts:['./src/**/*.ts'],
output:'./build',
minifyJs: './build/**/*.js'
};
//编译ts文件为js
gulp.task('build-ts',function(){
return tsp.src()
.pipe(plumber())
.pipe(tsp())
.pipe(gulp.dest(PATHS.output));
});
// 压缩混淆转换js代码
gulp.task('minify-js',function(){
gulp.src(PATHS.minifyJs)
.pipe(babel({
presets: ['env']
}))
.pipe(uglify({
ie8: true
}))
.pipe(gulp.dest(PATHS.output));
});
//当ts文件变化重新编译ts文件为js
gulp.task('watch-ts',function(){
gulp.watch(PATHS.scripts,['build-ts'],['minify-js']);
});
// 复制 RSA非对称加密的 api 接口加密文件到build文件夹
gulp.task('copy',function(){
gulp.src('src/rsa/*.pem')
.pipe(gulp.dest(`${PATHS.output}/rsa`));
});
//开发任务
gulp.task('dev',['build-ts','minify-js','watch-ts','copy']);

5099
mApi/package-lock.json generated Executable file

File diff suppressed because it is too large Load Diff

37
mApi/package.json Executable file
View File

@@ -0,0 +1,37 @@
{
"name": "nodechat",
"version": "1.0.0",
"description": "本来用typescript练手重写express的应用生成器不过到后面用这个写爬虫练手。",
"main": "index.js",
"scripts": {
"dev": "node-dev ./build/www.js",
"build": "gulp dev",
"all": "concurrently \"gulp dev\" \"npm run dev\" "
},
"keywords": [
"chat",
"nodejs"
],
"author": "bmy",
"license": "ISC",
"devDependencies": {
"@types/cheerio": "^0.22.8",
"@types/mysql": "^2.15.5",
"@types/node": "^10.5.7",
"babel-core": "^6.26.3",
"babel-preset-env": "^1.7.0",
"cheerio": "^1.0.0-rc.2",
"concurrently": "^3.6.1",
"express": "^4.16.3",
"gulp": "^3.9.1",
"gulp-babel": "^7.0.1",
"gulp-plumber": "^1.2.0",
"gulp-typescript": "^5.0.0-alpha.3",
"gulp-uglify": "^3.0.1",
"moment": "^2.22.2",
"mysql": "^2.16.0",
"node-jsencrypt": "^1.0.0",
"request": "^2.88.0",
"typescript": "^3.0.1"
}
}

1
mApi/src/.pydio Normal file
View File

@@ -0,0 +1 @@
b13a6818-95b9-48ac-88f1-9dde569b879d

1
mApi/src/api/.pydio Normal file
View File

@@ -0,0 +1 @@
6dab72fe-06a3-4667-bf7a-d3183fc559c8

225
mApi/src/api/index.ts Executable file
View File

@@ -0,0 +1,225 @@
import * as request from "request";
import * as cheerio from "cheerio";
import config from "./../config";
class api {
private videoList: Array<any>;
private imgList: Array<any>;
private pagination: string;
private videBaseUrl: string;
private videoType: string;
constructor() {
this.videoList = [];
this.videBaseUrl = config.url.video
this.videoType = '.mp4'
}
/**
* 发送GET请求获取网站html代码
* @param url 网站地址
*/
public get(url: string) {
let options: any = {
url: url,
headers: {
'Referer': 'https://www.657xd.com/'
}
};
return new Promise((resolve, reject) => {
request(url, function (error, response, body) {
if (error) {
console.log(`-----请求出错----`);
console.log(error);
console.log(`-----请求出错----`);
reject('error')
} else {
resolve(body)
}
});
})
}
/**
* 解析网页代码,获取数据
* @param html html代码
* @param callback 回调
*/
public cheerios(html: string, callback: any, total?: boolean) {
let videoListHtml: any = this.CheerioLoad(html, '.movie2_list ul li')
total ? this.pagination = videoListHtml.$('.pagination').find('strong').text() : ''
this.videoList = [];
(_this => {
videoListHtml.data.each(function (item) {
let list: any = videoListHtml.$(this);
if (list.find('span').text().length != 0) {
let mp4Url = _this.mp4URL(
list.find('span').text(),
list.find('a').attr('href'),
list.html().match(/get_local\(\"([\s\S]*?).jpg/)[1]
);
_this.videoList.push({
title: list.find('h3').text(), // 视频标题
href: `${config.url.BaseUrl()}${list.find('a').attr('href')}`, // 视频链接地址
gif: `${config.url.video}${list.html().match(/get_local\(\"([\s\S]*?)\"\)/)[1]}`, // 视频缩略图
time: list.find('span').text(), // 视频发布时间
mp4: config.url.video+mp4Url,
m3u8: _this.mp42(mp4Url)
})
}
})
})(this)
callback(this.videoList, this.pagination)
}
/**
* http://127.0.0.1:3000/image?type=63&page=3
* 查看首页图片
* @param html
* @param callback
* @param total
*/
public cheeriosIMG(html: string, callback: any, total?: boolean) {
let imgListHtml: any = this.CheerioLoad(html, '.box ul li')
total ? this.pagination = imgListHtml.$('.pagination').text() : ''
this.imgList = [];
(_this => {
imgListHtml.data.each(function (item) {
let list: any = imgListHtml.$(this);
_this.imgList.push({
title: list.find('a').text(),
href: list.find('a').attr('href'),
time: list.find('a').find('span').text()
})
})
})(this)
callback(this.imgList, this.pagination)
}
/**
* http://127.0.0.1:3000/imageinfo?id=/Html/63/12544.html
* 从图片列表进入图片详情
* @param html
* @param callback
*/
public enterInfos(html: string, callback: any) {
let list: any = this.CheerioLoad(html, '.content img')
let title: string = list.$('.page_title h1').text()
let imgdata: Array<any> = []
list.data.each(function (item) {
let img: any = list.$(this);
imgdata.push({
title: title,
url: img.attr('src')
})
});
callback(imgdata)
}
/**
* 根据规律直接分析得到视频的真实mp4地址
* @param time 视频发布时间
* @param href 视频链接地址
* @param id 视频缩略图
*/
public mp4URL(time: string, href: string, id: string) {
let times: string = time.replace(/-/ig, '')
let hrefs: string = href.match(/\/(\d*)\//)[1]
let ids: string = this.chechImgUrl(id);
let URLS: string;
if (ids != 'null') {
URLS = `/${times}/${hrefs}/${ids}/${ids}${this.videoType}`
} else {
URLS = 'http://static.runoob.com/video/f0381j0mxd7.m701.mp4'
}
return URLS;
// return this.mp42(URLS);
}
//线路一 根据视频MP4地址获取m3u8播放地址
public mp42(s) {
var purls = [
"https://1dbffh8.com",
"https://1bhut8.com",
"https://1bfsdgfht.com",
"https://1bhyr6.com",
"https://1bryts1.com"
];
var spcl_url = "https://p.043vb.com";
var purl = purls[Math.floor(Math.random() * purls.length)];
var uu = purl + s + ".m3u8";
var hour = new Date().getHours();
var sdate = new Date().getDay();
var x = Math.random();//0-1之间
if (hour >= 21 || hour < 22) {
if (x < 0.3) {
uu = spcl_url + s + ".m3u8";
}
if (sdate == 5 || sdate == 6) {
if (x < 0.5) {
uu = spcl_url + s + ".m3u8";
}
}
} else {
uu = purl + s + ".m3u8";
}
if (hour >= 22 || hour <= 1) {
if (x < 0.45) {
uu = spcl_url + s + ".m3u8";
}
if (sdate == 5 || sdate == 6) {
if (x < 0.55) {
uu = spcl_url + s + ".m3u8";
}
}
} else {
uu = purl + s + ".m3u8";
}
return uu;
}
/**
* 返回Cheerio数据
* @param html html代码
* @param dom 需要获取的dom节点
*/
public CheerioLoad(html: any, dom) {
let $: any = cheerio.load(html);
return {
$: $,
data: $(dom)
};
}
public chechImgUrl(url) {
if (url.match(/\/(\d*).mp4/) == null) {
url = 'null'
} else {
url = url.match(/\/(\d*).mp4/)[1]
}
return url
}
}
export default api

14
mApi/src/app.ts Executable file
View File

@@ -0,0 +1,14 @@
import * as express from 'express';
import * as path from 'path';
let app = express();
import indexRouter from './routes/modular/index';
app.use(express.static(path.join(__dirname, "public")));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use("/", indexRouter);
export default app

1
mApi/src/config/.pydio Normal file
View File

@@ -0,0 +1 @@
6bec38f6-dcf5-4ff3-95a1-23cd9c666a11

185
mApi/src/config/index.ts Executable file
View File

@@ -0,0 +1,185 @@
export default {
address: 'http://127.0.0.1',
port: 1314,
rsa: {
public: "../../rsa/rsa_pub.pem",
private: "../../rsa/rsa_priv.pem"
},
mysql: {
connectionLimit: 10,
host: "127.0.0.1",
user: "root",
password: "714500",
database: "haozhuo"
},
url: {
BaseUrl: () => {
var host = "";
var getdz = "";
var url = new Array();
url.push("1b572.com");
url.push("1b667.com");
url.push("1b669.com");
url.push("1b670.com");
url.push("1b671.com");
url.push("11b12.com");
url.push("11b13.com");
url.push("11b15.com");
url.push("11b16.com");
url.push("11b17.com");
url.push("11b18.com");
url.push("11b19.com");
url.push("11b20.com");
url.push("11b21.com");
url.push("11b23.com");
url.push("11b25.com");
url.push("11b26.com");
url.push("11b27.com");
url.push("11b28.com");
url.push("11b29.com");
url.push("11b30.com");
url.push("11b32.com");
url.push("22b11.com");
url.push("22b12.com");
url.push("22b13.com");
url.push("22b15.com");
url.push("22b16.com");
url.push("22b17.com");
url.push("22b18.com");
url.push("22b19.com");
url.push("22b20.com");
url.push("22b21.com");
url.push("22b23.com");
url.push("22b25.com");
url.push("22b26.com");
url.push("22b27.com");
url.push("22b28.com");
url.push("22b29.com");
url.push("22b30.com");
url.push("22b32.com");
url.push("33b12.com");
url.push("33b13.com");
url.push("33b15.com");
url.push("33b16.com");
url.push("33b17.com");
url.push("33b18.com");
url.push("33b19.com");
url.push("33b20.com");
url.push("33b21.com");
url.push("33b22.com");
url.push("33b23.com");
url.push("33b25.com");
url.push("33b26.com");
url.push("33b27.com");
url.push("33b28.com");
var UyoOfAKC1 = new Date("2020/12/24 18:30:00".replace(/\-/g, "/")).getTime();
var VK2 = 1; //VK2>=1
var PhKNMS3 = (Math.floor((new Date().getTime() - UyoOfAKC1) / 86400000) + 1) * VK2;
var i4 = 0;
var cncOFZ5 = VK2 - 1;
var MrZRcYn6 = Math.floor(Math.random() * (cncOFZ5 - i4 + 1) + i4);
var a7 = PhKNMS3 + MrZRcYn6;
if (a7 < 0 || a7 >= url.length) {
a7 = url.length - (MrZRcYn6 + 1);
PhKNMS3 = url.length - (cncOFZ5 + 1);
}
var D$Uja8 = true;
for (var j = i4; j <= cncOFZ5; j++) {
if (host.indexOf(url[PhKNMS3 + j]) >= 0) {
D$Uja8 = false;
break
}
}
var i = a7;
if (D$Uja8 == true) {
var Rvq9 = url[a7];
if (typeof (Rvq9) == "undefined") {
Rvq9 = url[url.length - 1]
}
var x_ratio = Math.random();//0~1
if (x_ratio < 0.5) {
getdz = "https://" + escape(Rvq9);
} else {
getdz = "https://www." + escape(Rvq9);
}
}
return getdz;
},
video: 'https://1dhgjyr9.com',
index: '/index.html',
class: [
{
title: '国产精品',
url: '/60/'
},
{
title: '中文字幕',
url: '/94/'
},
{
title: 'S级女优',
url: '/100/'
},
{
title: '欧美性爱',
url: '/62/'
},
{
title: '成人动漫',
url: '/101/'
},
{
title: '自拍偷拍',
url: '/89/'
},
{
title: '夫妻同房',
url: '/87/'
},
{
title: '开放90后',
url: '/93/'
},
{
title: '换妻游戏',
url: '/90/'
},
{
title: '网红主播',
url: '/91/'
},
{
title: '手机小视频',
url: '/88/'
},
{
title: '经典三级',
url: '/109/'
},
{
title: '无码在线',
url: '/110/'
},
{
title: '熟女人妻',
url: '/111/'
},
{
title: '美颜巨乳',
url: '/112/'
},
{
title: '颜射吃精',
url: '/113/'
},
{
title: '丝袜制服',
url: '/114/'
}
]
}
}

1
mApi/src/mysql/.pydio Normal file
View File

@@ -0,0 +1 @@
aa1faf5e-b04a-4264-9ba0-745100cdd496

38
mApi/src/mysql/index.ts Executable file
View File

@@ -0,0 +1,38 @@
import * as mysql from "mysql";
import config from "../config";
class MySql {
private pool:any
constructor(){
this.pool = mysql.createPool(config.mysql);
}
/**
* 执行数据库操作
* @param sql sql语句
* @param values 参数值
*/
query (sql:string, values?:Array<any>) {
return new Promise((resolve, reject) => {
this.pool.getConnection(function(err, connection) {
if (err) {
reject(err);
} else {
connection.query(sql, values, (err, rows) => {
if (err) {
reject(err);
} else {
rows.length == 0 ? (rows = ["NoData..."]) : rows;
resolve(rows);
}
// 结束会话
connection.release();
});
}
});
})
}
}
export default MySql

1
mApi/src/routes/.pydio Normal file
View File

@@ -0,0 +1 @@
195eddce-af6f-49ce-9e1f-ddc16e30ca4a

103
mApi/src/routes/controller.ts Executable file
View File

@@ -0,0 +1,103 @@
import * as express from 'express'
import utils from '../utils'
const router = express.Router()
class Controller {
public routers: any;
public response: any;
public request: any;
public utils = new utils();
constructor () {
router.all('*', (req, res, next) => {
this.response = res
this.request= req
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Content-Type,Content-Length, Authorization, Accept,X-Requested-With, token, X-Bmob-Application-Id, X-Bmob-REST-API-Key");
res.header("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS");
if(req.method == "OPTIONS") res.send(200);
else next();
});
// 暴露 router方便其他路由使用
this.routers = router
}
/**
* GET 请求
* @param url 请求的地址
* @param callback 回调
*/
public GetRoutes(url: string,callback: any) : void {
router.get(url, (req, res) => {
let params;
switch (url) {
case '/index':
callback(res, req)
break;
case '/imageinfo':
if(params.id != ''){
callback(res, req, params)
}else {
res.json({
status: 404,
message: '参数缺失'
})
}
break;
case '/image':
if(req.query.type != '' &&
req.query.page != ''){
callback(res, req)
}else {
res.json({
status: 404,
message: '参数缺失'
})
}
case '/videoClass':
// 判断是否存在必要参数key和值
console.log(req.query);
if(req.query.type != '' &&
req.query.page != '') {
callback(res, req)
}else {
res.json({
status: 404,
message: '参数缺失'
})
}
break;
default:
break;
}
})
}
/**
* POST 请求
* @param url 请求的地址
* @param callback 回调
*/
public PostRoutes(url: string, callback: any) : void {
router.post(url, (req, res) => {
callback(res, req)
})
}
private useApiStyle (req,res) {
if(req.query.hasOwnProperty("_rsa") && req.query._rsa != ''){
res.json({
status: "SRA接口等待实现..."
})
}else {
return false
}
}
}
export default Controller

View File

@@ -0,0 +1 @@
5ada070d-70e8-4b4f-80f2-ada2d255cf95

138
mApi/src/routes/modular/index.ts Executable file
View File

@@ -0,0 +1,138 @@
import Controller from "../controller";
// import mysql from "./../../mysql";
import utils from "./../../utils";
import api from "./../../api";
import config from "./../../config";
class index extends Controller{
public utils = new utils();
// public db = new mysql();
public api = new api();
constructor () {
super();
this.indexRouter()
this.otherClass()
this.imageRouter()
this.enterInfo()
}
/**
* 网站首页api
* 接口地址http://127.0.0.1:3000/index
*/
private indexRouter(): void {
this.GetRoutes('/index',async (res, req) => {
let data:any = await this.api.get(config.url.BaseUrl())
this.api.cheerios(data, (result)=>{
return res.json({
status: 200,
length: result.length,
time: this.utils.GetTime(),
result: result
})
})
})
}
/**
* 网站分类页面api
* 接口地址http://127.0.0.1:3000/videoClass?type=60&page=1
* @param type 网站栏目id
* @param page 想要获取第几页的数据
*/
private otherClass(): void {
this.GetRoutes('/videoClass', async (res,req) => {
let type: string = req.query.type
let page: string = req.query.page
let data: any = await this.api.get(this.returnURL(type, page))
this.api.cheerios(data, (result, pagination)=>{
return res.json({
status: 200,
length: result.length,
time: this.utils.GetTime(),
resultList: {
total: this.returnPage(pagination),
video: result
}
})
}, true)
})
}
/**
* 获取图片列表
*/
private imageRouter(): void {
this.GetRoutes('/image', async (res,req) => {
let type: string = req.query.type
let page: string = req.query.page
let data:any = await this.api.get(this.returnURL(type,page))
this.api.cheeriosIMG(data, (result, pagination)=>{
return res.json({
status: 200,
length: result.length,
time: this.utils.GetTime(),
total: this.returnPage(pagination.substring(0,22), true),
result: result
})
},true)
})
}
/**
* 进入图片详情
*/
private enterInfo() {
this.GetRoutes('/imageinfo', async (res,req,params) => {
let id: string = params.id
let data:any = await this.api.get(`${config.url.BaseUrl()}${id}`)
this.api.enterInfos(data,(result) =>{
return res.json({
status: 200,
length: result.length,
time: this.utils.GetTime(),
result: result
})
})
})
}
/**
* 因为这里第一个URL地址和其他之后每一个的地址不一样所以这里做下简单判断
* @param type
* @param page
*/
private returnURL(type:string, page:string): any {
let URL:string;
page === '1' ? URL = `${config.url.BaseUrl()}/Html/${type}/` : URL = `${config.url.BaseUrl()}/Html/${type}/index-${page}.html`
return URL
}
/**
* 返回总视频数,当前页,总页数
* @param page
*/
private returnPage(page:string, action?:boolean):any {
let pages:any;
if(action) {
pages = {
totalNumber: page.match(/共(\S*)篇/)[1],
currentPage: page.match(/:(\S*)\//)[1],
totalPage: page.match(/\/(\S*)页/)[1]
}
}else {
pages = {
totalNumber: page.match(/共(\S*)部/)[1],
currentPage: page.match(/\s+(\S*)\//)[1],
totalPage: page.match(/\/(\S*)/)[1]
}
}
return pages
}
}
export default new index().routers

1
mApi/src/rsa/.pydio Normal file
View File

@@ -0,0 +1 @@
e91c2418-1979-4c6b-83c4-c5d6a1ea9a0b

5
mApi/src/rsa/help.md Executable file
View File

@@ -0,0 +1,5 @@
RSA 非对称性加密在api接口中的使用
rsa_pub.pem 公钥发给前端前端ajax请求用公钥加密参数
rsa_priv.pem私钥留在服务器上用来解密前端ajax参数

15
mApi/src/rsa/rsa_priv.pem Executable file
View File

@@ -0,0 +1,15 @@
-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQDUbu7pfBOXprboQ4P/jBDHYAQqB7GTp/uzcDGiwHfdaGnjAxio
NHgim9zO+/Bz/A/K9g5z7LKy/URl94QOKVpyTf1ac1WF6E7N0H3ALkylc0rS5Ciy
5FY9MdwhyhuzlBkYoBsd7tY8/QxuDuzZ/hwFooZsGW9/C7DnjeEQ7RRRgQIDAQAB
AoGBALp9LHWCfDysmXOrGaz0jCJLEzzGhkjGtnsfKiPjbRbMu/ATXskDTterFpwK
pltb041sluGD0Ji7KBmPwbkg6olFB8EUCu5U281+8vNXaynzwCYF/tdaRoRUcIVf
VZS1y48LwH1JdPCtQvNldIAho5Nop7dWgz0qT2dCUezu7ab1AkEA/twQt1vnRpbk
vp3donKkux6hI8gwV58X7jtZjIOstVWlbGUBI90nln71lEUUmkuWK6YCyJy6fhAo
+gZ1OlltzwJBANViRRFJoGmZZLL9Jhgmeg52syVSWYcxquihKUdP4Wg6lbiETaHT
iqNnIBTG/c5W462zJ4JfKMB1NPKUwcYy768CQQDdVZle5H3SnDFEKi3VauMNbuha
/vyc3BoYV4rJsYFIan0jygRXdTVwSgpVHjlqVsKwXZiFSTfdS4OKwBkG96uZAkAL
YfqdOXg9KdmdUrO9hg/sxZg43XO5KoUbc71n+HLlxHpIJYaeo1I59T5Y3WScpTXw
BiaNZdYeriHs7tKHaCAxAkB548KLYhkxrl1SjLdmW8OcvenVORTim03dmsgUDg57
RRenq8VNgEvEdl/5N2k8evrr3bciMAxnk0oAGUYCsbDI
-----END RSA PRIVATE KEY-----

6
mApi/src/rsa/rsa_pub.pem Executable file
View File

@@ -0,0 +1,6 @@
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDUbu7pfBOXprboQ4P/jBDHYAQq
B7GTp/uzcDGiwHfdaGnjAxioNHgim9zO+/Bz/A/K9g5z7LKy/URl94QOKVpyTf1a
c1WF6E7N0H3ALkylc0rS5Ciy5FY9MdwhyhuzlBkYoBsd7tY8/QxuDuzZ/hwFooZs
GW9/C7DnjeEQ7RRRgQIDAQAB
-----END PUBLIC KEY-----

1
mApi/src/utils/.pydio Normal file
View File

@@ -0,0 +1 @@
1ce2a739-84af-4587-a927-daf5d4118598

48
mApi/src/utils/index.ts Executable file
View File

@@ -0,0 +1,48 @@
import * as moment from "moment";
import * as fs from "fs";
import * as path from "path";
import config from "./../config";
import * as JsEncrypt from "node-jsencrypt";
import * as querystring from "querystring";
class Utils {
private publicKey;
private privateKey;
private RsaEncrypt = new JsEncrypt()
constructor () {
// 公钥
this.publicKey = fs.readFileSync(path.join(__filename,config.rsa.public), "utf8")
// 私钥
this.privateKey = fs.readFileSync(path.join(__filename,config.rsa.private), "utf8")
}
/**
* 返回指定的时间
* @param timer 时间的格式
*/
public GetTime (timer?:string) {
timer = timer || 'YYYY-MM-DD HH:mm:ss'
return moment().format(timer);
}
/**
* 使用公钥( rsa_pub.pem )对接口参数进行加密
* @param ciphertext 需要加密的数据
*/
// public Encryption (ciphertext: string) {
// let encrypted = crypto.publicEncrypt(this.publicKey, Buffer.from(ciphertext))
// return encrypted.toString('base64')
// }
/**
* 使用私钥( rsa_priv.pem ) 对接口参数 token 进行解密
* 解密后在使用 querystring 进行序列化
* @param ciphertext 需要解密的参数
*/
public Decrypt(ciphertext: string) {
this.RsaEncrypt.setPrivateKey(this.privateKey)
return this.RsaEncrypt.decrypt(ciphertext)
}
}
export default Utils

50
mApi/src/www.ts Executable file
View File

@@ -0,0 +1,50 @@
import app from './app';
import config from "./config";
import * as http from 'http';
class service {
port: number;
server: any;
constructor () {
this.port = config.port
this.init()
}
private init (): void {
this.server = http.createServer(app)
this.start()
}
private start (): void {
this.server.listen(this.port);
this.server.on('error', this.error);
this.server.on('listening', this.onListening);
}
private error (error:any): void {
if (error.syscall !== 'listen') {
throw error;
}
var bind = this.port;
switch (error.code) {
case 'EACCES':
console.error(`端口:${bind} 需要提升权限..`);
process.exit(1);
break;
case 'EADDRINUSE':
console.error(`端口:${bind} 被占用...`);
process.exit(1);
break;
default:
throw error;
}
}
private onListening(): void {
console.log(`App is run: ${config.address}:${config.port}`)
}
}
new service()

3
mApi/test.rest Executable file
View File

@@ -0,0 +1,3 @@
GET http://127.0.0.1:3000/class?type=60&page=2 HTTP/1.1
content-type: application/json
token: 'wwwww'

19
mApi/tsconfig.json Executable file
View File

@@ -0,0 +1,19 @@
{
"compilerOptions": {
"lib":["es6"],
"module": "commonjs", //指定生成哪个模块系统代码
"target": "es5", //目标代码类型
"noImplicitAny": false, //在表达式和声明上有隐含的'any'类型时报错。
"sourceMap": false, //用于debug
"rootDir":"./src", //仅用来控制输出的目录结构--outDir。
"outDir":"./build", //重定向输出目录。
"watch":false //在监视模式下运行编译器。会监视输出文件,在它们改变时重新编译。
},
"include":[
"src/**/*"
],
"exclude":[
"views",
"static"
]
}