mirror of
https://github.com/tl-open-source/tl-rtc-file.git
synced 2025-10-07 16:31:28 +08:00
tl-rtc-file
This commit is contained in:
46
README.md
46
README.md
@@ -1,3 +1,47 @@
|
|||||||
# tl-rtc-file
|
# tl-rtc-file
|
||||||
|
|
||||||
开源 tl webrtc file工具
|
#### 简介 : (tl webrtc datachannel filetools)用webrt在web端传输文件,支持传输超大文件。
|
||||||
|
#### 优点 : 分片传输,跨终端,不限平台,方便使用,支持私有部署
|
||||||
|
|
||||||
|
|
||||||
|
## 准备
|
||||||
|
|
||||||
|
npm install
|
||||||
|
|
||||||
|
cd build/webpack/ & npm install
|
||||||
|
|
||||||
|
## 测试环境
|
||||||
|
|
||||||
|
npm run dev & npm run devsocket
|
||||||
|
|
||||||
|
## 线上环境
|
||||||
|
|
||||||
|
npm run svr & npm run svrsocket
|
||||||
|
|
||||||
|
|
||||||
|
## 修改res
|
||||||
|
|
||||||
|
cd build/webpack & npm run build 保持开启即可
|
||||||
|
|
||||||
|
## 接入db
|
||||||
|
|
||||||
|
修改conf/cfg.json中相应db配置即可, 如open, dbName, host, port, user, pwd 等
|
||||||
|
|
||||||
|
|
||||||
|
## 接入wss
|
||||||
|
|
||||||
|
修改conf/cfg.json中相应ws配置即可,如port, ws_online等
|
||||||
|
|
||||||
|
|
||||||
|
## 配置turnserver
|
||||||
|
|
||||||
|
ubuntu:
|
||||||
|
|
||||||
|
1. sudo apt-get install coturn #安装coturn
|
||||||
|
|
||||||
|
2. cp conf/turn/turnserver.conf /etc/turnserver.conf #修改配置文件, 文件内容按需修改
|
||||||
|
|
||||||
|
3. chomd +x bin/genTurnUser.sh && ./genTurnUser.sh #文件内容按需修改
|
||||||
|
|
||||||
|
4. chomd +x bin/startTurnServer.sh && ./startTurnServer.sh #启动turnserver,文件内容按需修改
|
||||||
|
|
||||||
|
1
bin/genTurnUser.sh
Normal file
1
bin/genTurnUser.sh
Normal file
@@ -0,0 +1 @@
|
|||||||
|
turnadmin -a -u 用户名 -p 密码 -r 公网ip
|
1
bin/startTurnserver.sh
Normal file
1
bin/startTurnserver.sh
Normal file
@@ -0,0 +1 @@
|
|||||||
|
turnserver -o -a -f -user=用户名:密码
|
91
build/webpack/cli/cli.js
Normal file
91
build/webpack/cli/cli.js
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
const webpack = require('webpack');
|
||||||
|
const merge = require('webpack-merge');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const devAllConfig = require('../webpack.dev-all');
|
||||||
|
const {
|
||||||
|
ROOT_FILE_PATH,
|
||||||
|
JS_PATH,
|
||||||
|
CSS_PATH,
|
||||||
|
|
||||||
|
ROOT_LIVE_PATH,
|
||||||
|
JS_LIVE_PATH,
|
||||||
|
CSS_LIVE_PATH
|
||||||
|
} = require('../comm/path');
|
||||||
|
|
||||||
|
const {
|
||||||
|
startCompilerCss,
|
||||||
|
startCompilerJs,
|
||||||
|
watchFile
|
||||||
|
} = require('../comm/util');
|
||||||
|
const deleteCssJs = require('../plugin/deleteCssJs'); // 用于对打包css生成的无用js和js.map进行删除的操作
|
||||||
|
const watchNewEntry = require('../plugin/watchNewEntry'); // 用于watch时监听新增文件的监听
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用于开发环境监听打包,打包出min版
|
||||||
|
* @watchJs
|
||||||
|
* @watchCss
|
||||||
|
* * */
|
||||||
|
class Cli {
|
||||||
|
constructor() {
|
||||||
|
this.jsComplier = null; // 对应 js/dist 出口
|
||||||
|
this.cssComplier = null; // 对应 css/dist 出口
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 监听单个文件
|
||||||
|
*/
|
||||||
|
watchSingleJs(){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
watchJs() {
|
||||||
|
this.jsComplier = webpack(merge(devAllConfig, {
|
||||||
|
entry: {
|
||||||
|
'index' : path.resolve(JS_PATH, "./index.js")
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
libraryTarget: 'umd',
|
||||||
|
globalObject: 'this',
|
||||||
|
path: path.resolve(JS_PATH, "./"),
|
||||||
|
filename: '[name].min.js',
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
// 开始编译并且监听
|
||||||
|
this.jsComplier.watch({
|
||||||
|
aggregateTimeout: 400,
|
||||||
|
}, startCompilerJs);
|
||||||
|
|
||||||
|
process.stdout.write('开始webpack编译js目录' + '\n\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
watchCss() {
|
||||||
|
this.cssComplier = webpack(merge(devAllConfig, {
|
||||||
|
entry: watchNewEntry.getEntries(
|
||||||
|
[
|
||||||
|
path.resolve(CSS_PATH, './!(*.min).css'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
output: {
|
||||||
|
libraryTarget: 'umd',
|
||||||
|
globalObject: 'this',
|
||||||
|
path: path.resolve(CSS_PATH, "./"),
|
||||||
|
filename: "./cssJsDist/[name].src.js",
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new deleteCssJs(),
|
||||||
|
],
|
||||||
|
}));
|
||||||
|
|
||||||
|
// 开始编译监听
|
||||||
|
this.cssComplier.watch({
|
||||||
|
aggregateTimeout: 400,
|
||||||
|
}, startCompilerCss);
|
||||||
|
|
||||||
|
process.stdout.write('开始webpack编译css目录' + '\n\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = new Cli();
|
38
build/webpack/comm/log.js
Normal file
38
build/webpack/comm/log.js
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
const colors = require('ansi-colors');
|
||||||
|
const fancyLog = require('fancy-log');
|
||||||
|
const argv = require('yargs').argv;
|
||||||
|
const supportsColor = require('color-support');
|
||||||
|
|
||||||
|
class Log {
|
||||||
|
constructor() {
|
||||||
|
this.colors = colors;
|
||||||
|
}
|
||||||
|
|
||||||
|
addColor(str, type) {
|
||||||
|
if (supportsColor() && (typeof argv.color === 'undefined' || argv.color)) {
|
||||||
|
if (type === 'warn') {
|
||||||
|
return this.colors.yellow(str);
|
||||||
|
} else if (type === 'error') {
|
||||||
|
return this.colors.red(str);
|
||||||
|
} else if (type === 'info') {
|
||||||
|
return this.colors.gray(str);
|
||||||
|
}
|
||||||
|
return this.colors.green(str);
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
log(content, tag = '1.0.0') {
|
||||||
|
fancyLog(this.addColor(`[BLOG-CLI] ${tag}: `, 'log') + content);
|
||||||
|
}
|
||||||
|
|
||||||
|
warn(content, tag = '1.0.0') {
|
||||||
|
fancyLog(this.addColor(`[BLOG-CLI] ${tag}: `, 'warn') + content);
|
||||||
|
}
|
||||||
|
|
||||||
|
error(content, tag = '1.0.0') {
|
||||||
|
fancyLog(this.addColor(`[BLOG-CLI] ${tag}: `, 'error') + content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = new Log();
|
16
build/webpack/comm/path.js
Normal file
16
build/webpack/comm/path.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
const path = require("path");
|
||||||
|
|
||||||
|
function getBasePath(dir){
|
||||||
|
return path.resolve(process.cwd(),dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
const ROOT_PATH = getBasePath('./../../res');
|
||||||
|
const JS_PATH = getBasePath('./../../res/js');
|
||||||
|
const CSS_PATH = getBasePath('./../../res/css');
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
ROOT_PATH,
|
||||||
|
JS_PATH,
|
||||||
|
CSS_PATH
|
||||||
|
};
|
181
build/webpack/comm/util.js
Normal file
181
build/webpack/comm/util.js
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
const glob = require('glob');
|
||||||
|
const path = require("path");
|
||||||
|
const fs = require("fs");
|
||||||
|
|
||||||
|
const readline = require('readline')
|
||||||
|
const Concat = require('concat-with-sourcemaps');
|
||||||
|
|
||||||
|
let jsCount = 1;
|
||||||
|
let jsIsFirst = true;
|
||||||
|
|
||||||
|
const Log = require("./log");
|
||||||
|
const {
|
||||||
|
JS_PATH,
|
||||||
|
CSS_PATH,
|
||||||
|
JS_SINGLE_PATH
|
||||||
|
} = require("./path");
|
||||||
|
|
||||||
|
// 将后缀 .src.css .src.js .js .css 截断
|
||||||
|
const removeExt = (pathname) => {
|
||||||
|
let idx = -1;
|
||||||
|
if (pathname.indexOf('.src.') !== -1) {
|
||||||
|
idx = pathname.indexOf('.src.');
|
||||||
|
} else {
|
||||||
|
idx = pathname.indexOf('.js') === -1 ? pathname.indexOf('.css') : pathname.indexOf('.js');
|
||||||
|
}
|
||||||
|
if (idx === -1) {
|
||||||
|
return pathname;
|
||||||
|
} else {
|
||||||
|
return pathname.substring(0, idx);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// 将 \ 反斜杠 换成 / 正斜杠
|
||||||
|
const unifiedSlash = (pathname) => {
|
||||||
|
return pathname.replace(/\\/g, '/');
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// 执行编译后的回调函数
|
||||||
|
const startCompiler = (err, stats, resolve, reject) => {
|
||||||
|
return new Promise((res)=>{
|
||||||
|
if (err) {
|
||||||
|
Log.error(err.stack || err);
|
||||||
|
if (err.details) {
|
||||||
|
Log.error(err.details);
|
||||||
|
}
|
||||||
|
typeof reject === "function" && reject();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const info = stats.toJson();
|
||||||
|
|
||||||
|
if (stats.hasErrors()) {
|
||||||
|
Log.error('webpack', '编译错误,错误信息如下:');
|
||||||
|
info.errors.forEach(err => {
|
||||||
|
Log.error('error', err);
|
||||||
|
});
|
||||||
|
typeof reject === 'function' && reject();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stats.hasWarnings()) {
|
||||||
|
Log.warn('webpack', '编译提醒信息如下:');
|
||||||
|
info.warnings.forEach(warning => {
|
||||||
|
Log.warn(warning)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
typeof resolve === 'function' && resolve();
|
||||||
|
Log.log(stats.toString({
|
||||||
|
hash: true,
|
||||||
|
timings: true,
|
||||||
|
version: true,
|
||||||
|
cached: false,
|
||||||
|
cachedAssets: false,
|
||||||
|
colors: true,
|
||||||
|
modules: false,
|
||||||
|
children: false,
|
||||||
|
resons: false,
|
||||||
|
source: false,
|
||||||
|
chunks: false,
|
||||||
|
chunkModules: false,
|
||||||
|
entrypoints: false
|
||||||
|
}) + '\n\n');
|
||||||
|
res();
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件是否更新
|
||||||
|
* @param pathArr
|
||||||
|
*/
|
||||||
|
const watchFile = ( pathArr ) => {
|
||||||
|
if(!pathArr || !(pathArr instanceof Array)){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (let pathArrKey in pathArr) {
|
||||||
|
fs.watch(pathArr[pathArrKey],(event,filename)=>{
|
||||||
|
if (filename && event === 'change') {
|
||||||
|
buildAllFListJS(['features.js','template.js']);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const startCompilerCss = (err, stats, resolve, reject) => {
|
||||||
|
return startCompiler(err, stats, resolve, reject);
|
||||||
|
};
|
||||||
|
|
||||||
|
const startCompilerJs = (err, stats, resolve, reject) => {
|
||||||
|
return startCompiler(err, stats, resolve, reject);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
function transformFListFileToArray(partitionFilePath) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const rl = readline.createInterface({
|
||||||
|
input: fs.createReadStream(partitionFilePath),
|
||||||
|
terminal: false,
|
||||||
|
crlfDelay: Infinity,
|
||||||
|
});
|
||||||
|
|
||||||
|
const files = [];
|
||||||
|
|
||||||
|
rl.on('line', (line) => {
|
||||||
|
if (!line.includes("<!--")) {
|
||||||
|
files.push(line)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
rl.on('close', function () {
|
||||||
|
resolve(files);
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 合并某个目录下的js到js目录下
|
||||||
|
*/
|
||||||
|
function buildSingleFListJs(pathname = '') {
|
||||||
|
if (pathname === '') return buildAllFListJS();
|
||||||
|
|
||||||
|
const _flist = path.resolve(pathname, './_flist')
|
||||||
|
const outputFileName = path.basename(pathname).replace('.js', '.src.js');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按照_flist的顺序合并为一个src文件,src文件统一放到flist下面
|
||||||
|
*/
|
||||||
|
transformFListFileToArray(_flist).then((files) => {
|
||||||
|
const concat = new Concat(false, outputFileName, '\n');
|
||||||
|
files.forEach(filename => {
|
||||||
|
concat.add(filename, fs.readFileSync(path.resolve(pathname, `./${filename}`)))
|
||||||
|
});
|
||||||
|
fs.writeFile(path.resolve(pathname, `../single/${outputFileName}`), concat.content, (err) => {
|
||||||
|
if (err) throw err
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 合并目录下所有的xx.js的目录
|
||||||
|
*/
|
||||||
|
function buildAllFListJS(pathArr = []) {
|
||||||
|
if(pathArr === undefined || pathArr.length === 0){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for(let i = 0; i < pathArr.length; i++){
|
||||||
|
glob.sync(path.resolve(JS_PATH, pathArr[i])).forEach(buildSingleFListJs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
unifiedSlash,
|
||||||
|
startCompilerCss,
|
||||||
|
startCompilerJs,
|
||||||
|
startCompiler,
|
||||||
|
removeExt,
|
||||||
|
buildAllFListJS,
|
||||||
|
watchFile
|
||||||
|
};
|
6
build/webpack/config/.babelrc
Normal file
6
build/webpack/config/.babelrc
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"presets":["@babel/preset-env"],
|
||||||
|
"plugins": [
|
||||||
|
"@vue/babel-plugin-transform-vue-jsx"
|
||||||
|
]
|
||||||
|
}
|
5
build/webpack/devBuild.js
Normal file
5
build/webpack/devBuild.js
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
const cliService = require('./cli/cli.js');
|
||||||
|
|
||||||
|
cliService.watchCss();
|
||||||
|
cliService.watchJs();
|
||||||
|
cliService.watchSingleJs();
|
11433
build/webpack/package-lock.json
generated
Normal file
11433
build/webpack/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
75
build/webpack/package.json
Normal file
75
build/webpack/package.json
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
{
|
||||||
|
"name": "webpack",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "webpack.config.js",
|
||||||
|
"dependencies": {},
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/core": "^7.6.0",
|
||||||
|
"@babel/plugin-proposal-class-properties": "^7.5.5",
|
||||||
|
"@babel/plugin-proposal-optional-chaining": "^7.10.4",
|
||||||
|
"@babel/plugin-transform-runtime": "^7.6.0",
|
||||||
|
"@babel/polyfill": "^7.12.1",
|
||||||
|
"@babel/preset-env": "^7.12.7",
|
||||||
|
"@vue/babel-plugin-transform-vue-jsx": "^1.2.1",
|
||||||
|
"ansi-colors": "^4.1.1",
|
||||||
|
"autoprefixer": "^9.6.1",
|
||||||
|
"babel-eslint": "^10.0.3",
|
||||||
|
"babel-loader": "^8.0.6",
|
||||||
|
"cache-loader": "^2.0.1",
|
||||||
|
"chalk": "^2.4.2",
|
||||||
|
"chokidar": "^2.1.8",
|
||||||
|
"commander": "^6.2.0",
|
||||||
|
"concat-with-sourcemaps": "^1.1.0",
|
||||||
|
"cross-env": "^5.2.1",
|
||||||
|
"css-loader": "^2.1.0",
|
||||||
|
"eslint": "^5.16.0",
|
||||||
|
"eslint-config-airbnb-base": "^13.2.0",
|
||||||
|
"eslint-formatter-friendly": "^6.0.0",
|
||||||
|
"eslint-loader": "^2.2.1",
|
||||||
|
"eslint-plugin-babel": "^5.3.0",
|
||||||
|
"eslint-plugin-import": "^2.18.2",
|
||||||
|
"eslint-plugin-vue": "^5.2.3",
|
||||||
|
"fancy-log": "^1.3.3",
|
||||||
|
"file-loader": "^3.0.1",
|
||||||
|
"glob": "^7.1.4",
|
||||||
|
"glob-base": "^0.3.0",
|
||||||
|
"happypack": "^5.0.1",
|
||||||
|
"hard-source-webpack-plugin": "^0.13.1",
|
||||||
|
"mini-css-extract-plugin": "^0.5.0",
|
||||||
|
"node-sass": "^4.12.0",
|
||||||
|
"optimize-css-assets-webpack-plugin": "^5.0.4",
|
||||||
|
"parallel-webpack": "^2.6.0",
|
||||||
|
"postcss": "^7.0.18",
|
||||||
|
"postcss-flexbugs-fixes": "^4.2.1",
|
||||||
|
"postcss-loader": "^4.1.0",
|
||||||
|
"postcss-preset-env": "^6.7.0",
|
||||||
|
"sass": "^1.26.10",
|
||||||
|
"sass-loader": "^9.0.3",
|
||||||
|
"sass-resources-loader": "^2.0.3",
|
||||||
|
"style-loader": "^2.0.0",
|
||||||
|
"svg-sprite-loader": "^4.2.5-drop-webpack-version-detector.0",
|
||||||
|
"svgo": "^1.3.0",
|
||||||
|
"svgo-loader": "^2.2.1",
|
||||||
|
"terser-webpack-plugin": "^5.0.3",
|
||||||
|
"thread-loader": "^2.1.3",
|
||||||
|
"ts-loader": "^5.4.5",
|
||||||
|
"uglifyjs-webpack-plugin": "^2.2.0",
|
||||||
|
"url-loader": "^4.1.1",
|
||||||
|
"vue-eslint-parser": "^6.0.4",
|
||||||
|
"vue-loader": "^15.9.5",
|
||||||
|
"vue-template-compiler": "^2.6.6",
|
||||||
|
"webpack": "^4.44.2",
|
||||||
|
"webpack-cli": "^4.2.0",
|
||||||
|
"webpack-merge": "^4.2.2",
|
||||||
|
"webpack-watched-glob-entries-plugin": "^2.1.6",
|
||||||
|
"yargs": "^16.1.1"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
|
"build": "node devBuild.js"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC"
|
||||||
|
}
|
14
build/webpack/plugin/deleteCssJs.js
Normal file
14
build/webpack/plugin/deleteCssJs.js
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
class DeleteCssJs {
|
||||||
|
apply(compiler) {
|
||||||
|
compiler.hooks.emit.tapAsync(this.constructor.name, (compilation, callback) => {
|
||||||
|
compilation.getAssets().forEach((asset) => {
|
||||||
|
const pathname = asset.name;
|
||||||
|
if(pathname.includes('cssJsDist')){
|
||||||
|
delete compilation.assets[asset.name];
|
||||||
|
}
|
||||||
|
})
|
||||||
|
callback()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
module.exports = DeleteCssJs;
|
58
build/webpack/plugin/watchNewEntry.js
Normal file
58
build/webpack/plugin/watchNewEntry.js
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
const path = require('path');
|
||||||
|
const glob = require('glob');
|
||||||
|
const globBase = require('glob-base');
|
||||||
|
const { removeExt, unifiedSlash } = require('../comm/util');
|
||||||
|
|
||||||
|
const directories = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 由于webpack的watch不会对新增文件进行编译,使用这个插件可以在watch状态下可以检测到新增文件入口。
|
||||||
|
* */
|
||||||
|
class WatchNewEntry {
|
||||||
|
static getEntries(globEntries, globOptions = {}) {
|
||||||
|
return function () {
|
||||||
|
if (!Array.isArray(globEntries)) {
|
||||||
|
throw new TypeError('globEntries must be an array of strings');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (globOptions && typeof globOptions !== 'object') {
|
||||||
|
throw new TypeError('globOptions must be an object');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let globbedFiles = {};
|
||||||
|
|
||||||
|
globEntries.forEach((globString) => {
|
||||||
|
const globBaseOptions = globBase(globString);
|
||||||
|
if (directories.indexOf(globBaseOptions.base) === -1) {
|
||||||
|
directories.push(globBaseOptions.base);
|
||||||
|
}
|
||||||
|
const files = WatchNewEntry.getFiles(globString, globOptions);
|
||||||
|
|
||||||
|
globbedFiles = Object.assign(files, globbedFiles);
|
||||||
|
});
|
||||||
|
|
||||||
|
return globbedFiles;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static getFiles(globString, globOptions) {
|
||||||
|
const files = {};
|
||||||
|
glob.sync(globString, globOptions).forEach((file) => {
|
||||||
|
files[unifiedSlash(path.basename(removeExt(file)))] = unifiedSlash(file);
|
||||||
|
});
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
apply(compiler) {
|
||||||
|
compiler.hooks.afterCompile.tapAsync(this.constructor.name, this.afterCompile);
|
||||||
|
}
|
||||||
|
|
||||||
|
afterCompile(compilation, callback) {
|
||||||
|
for (const directory of directories) {
|
||||||
|
compilation.contextDependencies.add(directory);
|
||||||
|
}
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
module.exports = WatchNewEntry;
|
101
build/webpack/webpack.dev-all.js
Normal file
101
build/webpack/webpack.dev-all.js
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
const path = require('path');
|
||||||
|
const VueLoaderPlugin = require('vue-loader/lib/plugin');
|
||||||
|
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||||
|
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
|
||||||
|
const watchNewEntry = require('./plugin/watchNewEntry');
|
||||||
|
const { JS_PATH, CSS_PATH } = require('./comm/path');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
mode: 'development',
|
||||||
|
devtool: 'source-map',
|
||||||
|
watchOptions: {
|
||||||
|
ignored: /node_modules/,
|
||||||
|
aggregateTimeout: 500,
|
||||||
|
},
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.vue$/,
|
||||||
|
use: [
|
||||||
|
{
|
||||||
|
loader: 'vue-loader',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
exclude: /node_modules/,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.js$/,
|
||||||
|
exclude: /node_modules/,
|
||||||
|
include: JS_PATH,
|
||||||
|
use: [
|
||||||
|
'thread-loader',
|
||||||
|
{
|
||||||
|
loader: 'babel-loader',
|
||||||
|
options: {
|
||||||
|
//configFile: path.join(__dirname, './config/.babelrc'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.css$/,
|
||||||
|
exclude: /node_modules/,
|
||||||
|
use: [
|
||||||
|
MiniCssExtractPlugin.loader,
|
||||||
|
{
|
||||||
|
loader: 'css-loader',
|
||||||
|
options: {
|
||||||
|
importLoaders: 1,
|
||||||
|
url: false
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.svg$/,
|
||||||
|
exclude: /node_modules/,
|
||||||
|
use: [
|
||||||
|
{
|
||||||
|
loader: 'svg-sprite-loader',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
loader: 'svgo-loader',
|
||||||
|
options: {
|
||||||
|
plugins: [
|
||||||
|
{ convertPathData: false },
|
||||||
|
{ mergePaths: false },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new VueLoaderPlugin(),
|
||||||
|
new MiniCssExtractPlugin({
|
||||||
|
path: path.resolve(CSS_PATH, "./"),
|
||||||
|
filename: '[name].min.css',
|
||||||
|
ignoreOrder: false,
|
||||||
|
}),
|
||||||
|
new watchNewEntry(),
|
||||||
|
],
|
||||||
|
resolve: {
|
||||||
|
extensions: [
|
||||||
|
'.js',
|
||||||
|
'.vue',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
stats: {
|
||||||
|
children: false,
|
||||||
|
},
|
||||||
|
externals: {
|
||||||
|
jquery: 'jQuery',
|
||||||
|
},
|
||||||
|
optimization: {
|
||||||
|
minimize: true,
|
||||||
|
minimizer: [
|
||||||
|
new UglifyJSPlugin()
|
||||||
|
]
|
||||||
|
},
|
||||||
|
};
|
68
conf/cfg.json
Normal file
68
conf/cfg.json
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
{
|
||||||
|
"ws" : {
|
||||||
|
"port": 8444,
|
||||||
|
"ssl_port": 8444,
|
||||||
|
"ws_online": "your online socket ip/host like wss://xxx.xxx.xxx, if just local test, dont need"
|
||||||
|
},
|
||||||
|
"node" : {
|
||||||
|
"port" : 9092
|
||||||
|
},
|
||||||
|
"webrtc" : {
|
||||||
|
"iceServers": [{
|
||||||
|
"urls": "stun:stun.xten.com"
|
||||||
|
}],
|
||||||
|
"options" : {
|
||||||
|
"offerToReceiveAudio": 1,
|
||||||
|
"offerToReceiveVideo": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"router" : {
|
||||||
|
"filter" : {
|
||||||
|
"whiteDir" : ["socket","tables","worker","module","room","user"],
|
||||||
|
"whiteFile" : ["router.js"]
|
||||||
|
},
|
||||||
|
"res" : {
|
||||||
|
"/" : "res/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"db" : {
|
||||||
|
"open" : false,
|
||||||
|
"mysql" : {
|
||||||
|
"host" : "your db host",
|
||||||
|
"port" : 3306,
|
||||||
|
"dbName" : "your dbName",
|
||||||
|
"user" : "your name",
|
||||||
|
"password" : "your pwd",
|
||||||
|
"other" : {
|
||||||
|
"sequelize" : {
|
||||||
|
"dialect": "mysql",
|
||||||
|
"host": "your ip",
|
||||||
|
"port": 3306,
|
||||||
|
"logging": false,
|
||||||
|
"pool": {
|
||||||
|
"max": 5,
|
||||||
|
"min": 0,
|
||||||
|
"acquire": 30000,
|
||||||
|
"idle": 10000
|
||||||
|
},
|
||||||
|
"timezone": "+08:00",
|
||||||
|
"define": {
|
||||||
|
"freezeTableName": true,
|
||||||
|
"underscored": true,
|
||||||
|
"charset": "utf8",
|
||||||
|
"collate": "utf8_general_ci",
|
||||||
|
"timestamps": false,
|
||||||
|
"paranoid": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"oracle" : {
|
||||||
|
"host" : "localhost",
|
||||||
|
"port" : 3306,
|
||||||
|
"user" : "root",
|
||||||
|
"password" : "xxxxxx",
|
||||||
|
"other" : {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
15
conf/keys/server.crt
Normal file
15
conf/keys/server.crt
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIICVTCCAb4CCQCCDZ6FebPIqjANBgkqhkiG9w0BAQsFADBvMQswCQYDVQQGEwJV
|
||||||
|
UzENMAsGA1UECAwETWFyczETMBEGA1UEBwwKaVRyYW5zd2FycDETMBEGA1UECgwK
|
||||||
|
aVRyYW5zd2FycDETMBEGA1UECwwKaVRyYW5zd2FycDESMBAGA1UEAwwJMTI3LjAu
|
||||||
|
MC4xMB4XDTE4MDcxMDAzMTYzN1oXDTI4MDcwNzAzMTYzN1owbzELMAkGA1UEBhMC
|
||||||
|
VVMxDTALBgNVBAgMBE1hcnMxEzARBgNVBAcMCmlUcmFuc3dhcnAxEzARBgNVBAoM
|
||||||
|
CmlUcmFuc3dhcnAxEzARBgNVBAsMCmlUcmFuc3dhcnAxEjAQBgNVBAMMCTEyNy4w
|
||||||
|
LjAuMTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1I6AQ6eNez85kcKjwy3g
|
||||||
|
/vcnXtw+EbP4Ab37fLhIIWG+XzmEBAqnCYjM3nmlDIGEfNylGReo9mD2OHg46a1D
|
||||||
|
wjd3pxTMit41pCTCiu8S9A2UJfbhSzQrfs+IZcNye4KR9/FzNEW6KoKQ0uc6X33E
|
||||||
|
0xe41hbRMQoKB3WmxvyN8PcCAwEAATANBgkqhkiG9w0BAQsFAAOBgQAd7GNDtKWA
|
||||||
|
0OpSCzMu0pbmss9Erh4/RC8D+wK4+TXgPDKyZ6hX4FYPvk+ryMvwxJf0o4jjx5cx
|
||||||
|
Yew7UjaKHlGXq+CNVRFYlltsbvO3oQTNkajuyYGWzMSuNxNsT3apOxH7SIu3qao8
|
||||||
|
COSwj5FxZ2JU7O+SBVFZoJrFXEa+KJMQzQ==
|
||||||
|
-----END CERTIFICATE-----
|
15
conf/keys/server.key
Normal file
15
conf/keys/server.key
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIICXAIBAAKBgQDUjoBDp417PzmRwqPDLeD+9yde3D4Rs/gBvft8uEghYb5fOYQE
|
||||||
|
CqcJiMzeeaUMgYR83KUZF6j2YPY4eDjprUPCN3enFMyK3jWkJMKK7xL0DZQl9uFL
|
||||||
|
NCt+z4hlw3J7gpH38XM0RboqgpDS5zpffcTTF7jWFtExCgoHdabG/I3w9wIDAQAB
|
||||||
|
AoGAWsy1BjGhQrDzisy24D3NC53Q97jl2vIiU7wwnkqqpXf3tv3+4ysZx/zkZ3VX
|
||||||
|
iEwbqKso69srlnQ9OkpBJbGaa6lZe+z7BGzv2eJr+hKjjVjR122eDjAtXw+Tmt6c
|
||||||
|
iBUG9+ITC1GdhXLEgTXtYuPq8hbDhoAVI007E+5JuQoO8kECQQD3aR6zGfR7EOmn
|
||||||
|
byGkNMMPY/FoH894BpB4l7gIlNXH3pxBqukrEwnmVXSfak2PAkeLFO+bgCc6baIZ
|
||||||
|
+R4iOLn/AkEA2++dIndbY7nmGs7Q//sM7MkFMiFQ4h1nN38V9AEHaUonxlwo+Nks
|
||||||
|
PTAaVd78YIh6yBlfexm0Fxi1mEVQApqZCQJAFUPqyJglhGJqwuJxcMy8K1l6yWla
|
||||||
|
isV9q2/W+J3aViiTI63OBs7HHg4gTQd1DSK0BYdSJPp55LLBqRvZdDWN/wJBAMQa
|
||||||
|
M46exAL4p55xl9MWwyCB4LshD6B9vSGzlBx7qmMMNsjcNcAkzBhGwsScTYW5S1kN
|
||||||
|
nqABfB037/s0mjGoLRkCQF18j4MovyFaj8VAqY8YUmf86Ez9JmL9kHNA8yEoSjuI
|
||||||
|
xsRa6y5Nza5y83Mojt4W+PfS386riJ7txqrPdezyag4=
|
||||||
|
-----END RSA PRIVATE KEY-----
|
636
conf/turn/turnserver.conf
Normal file
636
conf/turn/turnserver.conf
Normal file
@@ -0,0 +1,636 @@
|
|||||||
|
# Coturn TURN SERVER configuration file
|
||||||
|
#
|
||||||
|
# Boolean values note: where boolean value is supposed to be used,
|
||||||
|
# you can use '0', 'off', 'no', 'false', 'f' as 'false,
|
||||||
|
# and you can use '1', 'on', 'yes', 'true', 't' as 'true'
|
||||||
|
# If the value is missed, then it means 'true'.
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
cli-password=qwerty
|
||||||
|
listening-device=eth0
|
||||||
|
listening-ip=内网ip
|
||||||
|
listening-port=3478
|
||||||
|
#tls-listening-port=5349
|
||||||
|
external-ip=公网ip
|
||||||
|
#relay-threads=50
|
||||||
|
#lt-cred-mech
|
||||||
|
user=用户名:密码
|
||||||
|
realm=公网ip:3478
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Listener interface device (optional, Linux only).
|
||||||
|
# NOT RECOMMENDED.
|
||||||
|
#
|
||||||
|
#listening-device=eth0
|
||||||
|
|
||||||
|
# TURN listener port for UDP and TCP (Default: 3478).
|
||||||
|
# Note: actually, TLS & DTLS sessions can connect to the
|
||||||
|
# "plain" TCP & UDP port(s), too - if allowed by configuration.
|
||||||
|
#
|
||||||
|
#listening-port=3478
|
||||||
|
|
||||||
|
# TURN listener port for TLS (Default: 5349).
|
||||||
|
# Note: actually, "plain" TCP & UDP sessions can connect to the TLS & DTLS
|
||||||
|
# port(s), too - if allowed by configuration. The TURN server
|
||||||
|
# "automatically" recognizes the type of traffic. Actually, two listening
|
||||||
|
# endpoints (the "plain" one and the "tls" one) are equivalent in terms of
|
||||||
|
# functionality; but we keep both endpoints to satisfy the RFC 5766 specs.
|
||||||
|
# For secure TCP connections, we currently support SSL version 3 and
|
||||||
|
# TLS version 1.0, 1.1 and 1.2.
|
||||||
|
# For secure UDP connections, we support DTLS version 1.
|
||||||
|
#
|
||||||
|
#tls-listening-port=5349
|
||||||
|
|
||||||
|
# Alternative listening port for UDP and TCP listeners;
|
||||||
|
# default (or zero) value means "listening port plus one".
|
||||||
|
# This is needed for RFC 5780 support
|
||||||
|
# (STUN extension specs, NAT behavior discovery). The TURN Server
|
||||||
|
# supports RFC 5780 only if it is started with more than one
|
||||||
|
# listening IP address of the same family (IPv4 or IPv6).
|
||||||
|
# RFC 5780 is supported only by UDP protocol, other protocols
|
||||||
|
# are listening to that endpoint only for "symmetry".
|
||||||
|
#
|
||||||
|
#alt-listening-port=0
|
||||||
|
|
||||||
|
# Alternative listening port for TLS and DTLS protocols.
|
||||||
|
# Default (or zero) value means "TLS listening port plus one".
|
||||||
|
#
|
||||||
|
#alt-tls-listening-port=0
|
||||||
|
|
||||||
|
# Listener IP address of relay server. Multiple listeners can be specified.
|
||||||
|
# If no IP(s) specified in the config file or in the command line options,
|
||||||
|
# then all IPv4 and IPv6 system IPs will be used for listening.
|
||||||
|
#
|
||||||
|
#listening-ip=172.17.19.101
|
||||||
|
#listening-ip=10.207.21.238
|
||||||
|
#listening-ip=2607:f0d0:1002:51::4
|
||||||
|
|
||||||
|
# Auxiliary STUN/TURN server listening endpoint.
|
||||||
|
# Aux servers have almost full TURN and STUN functionality.
|
||||||
|
# The (minor) limitations are:
|
||||||
|
#
|
||||||
|
# 1) Auxiliary servers do not have alternative ports and
|
||||||
|
# they do not support STUN RFC 5780 functionality (CHANGE REQUEST).
|
||||||
|
#
|
||||||
|
# 2) Auxiliary servers also are never returning ALTERNATIVE-SERVER reply.
|
||||||
|
#
|
||||||
|
# Valid formats are 1.2.3.4:5555 for IPv4 and [1:2::3:4]:5555 for IPv6.
|
||||||
|
#
|
||||||
|
# There may be multiple aux-server options, each will be used for listening
|
||||||
|
# to client requests.
|
||||||
|
#
|
||||||
|
#aux-server=172.17.19.110:33478
|
||||||
|
#aux-server=[2607:f0d0:1002:51::4]:33478
|
||||||
|
|
||||||
|
# (recommended for older Linuxes only)
|
||||||
|
# Automatically balance UDP traffic over auxiliary servers (if configured).
|
||||||
|
# The load balancing is using the ALTERNATE-SERVER mechanism.
|
||||||
|
# The TURN client must support 300 ALTERNATE-SERVER response for this
|
||||||
|
# functionality.
|
||||||
|
#
|
||||||
|
#udp-self-balance
|
||||||
|
|
||||||
|
# Relay interface device for relay sockets (optional, Linux only).
|
||||||
|
# NOT RECOMMENDED.
|
||||||
|
#
|
||||||
|
#relay-device=eth1
|
||||||
|
|
||||||
|
# Relay address (the local IP address that will be used to relay the
|
||||||
|
# packets to the peer).
|
||||||
|
# Multiple relay addresses may be used.
|
||||||
|
# The same IP(s) can be used as both listening IP(s) and relay IP(s).
|
||||||
|
#
|
||||||
|
# If no relay IP(s) specified, then the turnserver will apply the default
|
||||||
|
# policy: it will decide itself which relay addresses to be used, and it
|
||||||
|
# will always be using the client socket IP address as the relay IP address
|
||||||
|
# of the TURN session (if the requested relay address family is the same
|
||||||
|
# as the family of the client socket).
|
||||||
|
#
|
||||||
|
#relay-ip=172.17.19.105
|
||||||
|
#relay-ip=2607:f0d0:1002:51::5
|
||||||
|
|
||||||
|
# For Amazon EC2 users:
|
||||||
|
#
|
||||||
|
# TURN Server public/private address mapping, if the server is behind NAT.
|
||||||
|
# In that situation, if a -X is used in form "-X <ip>" then that ip will be reported
|
||||||
|
# as relay IP address of all allocations. This scenario works only in a simple case
|
||||||
|
# when one single relay address is be used, and no RFC5780 functionality is required.
|
||||||
|
# That single relay address must be mapped by NAT to the 'external' IP.
|
||||||
|
# The "external-ip" value, if not empty, is returned in XOR-RELAYED-ADDRESS field.
|
||||||
|
# For that 'external' IP, NAT must forward ports directly (relayed port 12345
|
||||||
|
# must be always mapped to the same 'external' port 12345).
|
||||||
|
#
|
||||||
|
# In more complex case when more than one IP address is involved,
|
||||||
|
# that option must be used several times, each entry must
|
||||||
|
# have form "-X <public-ip/private-ip>", to map all involved addresses.
|
||||||
|
# RFC5780 NAT discovery STUN functionality will work correctly,
|
||||||
|
# if the addresses are mapped properly, even when the TURN server itself
|
||||||
|
# is behind A NAT.
|
||||||
|
#
|
||||||
|
# By default, this value is empty, and no address mapping is used.
|
||||||
|
#
|
||||||
|
#external-ip=60.70.80.91
|
||||||
|
#
|
||||||
|
#OR:
|
||||||
|
#
|
||||||
|
#external-ip=60.70.80.91/172.17.19.101
|
||||||
|
#external-ip=60.70.80.92/172.17.19.102
|
||||||
|
|
||||||
|
|
||||||
|
# Number of the relay threads to handle the established connections
|
||||||
|
# (in addition to authentication thread and the listener thread).
|
||||||
|
# If explicitly set to 0 then application runs relay process in a
|
||||||
|
# single thread, in the same thread with the listener process
|
||||||
|
# (the authentication thread will still be a separate thread).
|
||||||
|
#
|
||||||
|
# If this parameter is not set, then the default OS-dependent
|
||||||
|
# thread pattern algorithm will be employed. Usually the default
|
||||||
|
# algorithm is the most optimal, so you have to change this option
|
||||||
|
# only if you want to make some fine tweaks.
|
||||||
|
#
|
||||||
|
# In the older systems (Linux kernel before 3.9),
|
||||||
|
# the number of UDP threads is always one thread per network listening
|
||||||
|
# endpoint - including the auxiliary endpoints - unless 0 (zero) or
|
||||||
|
# 1 (one) value is set.
|
||||||
|
#
|
||||||
|
#relay-threads=0
|
||||||
|
|
||||||
|
# Lower and upper bounds of the UDP relay endpoints:
|
||||||
|
# (default values are 49152 and 65535)
|
||||||
|
#
|
||||||
|
#min-port=49152
|
||||||
|
#max-port=65535
|
||||||
|
|
||||||
|
# Uncomment to run TURN server in 'normal' 'moderate' verbose mode.
|
||||||
|
# By default the verbose mode is off.
|
||||||
|
#verbose
|
||||||
|
|
||||||
|
# Uncomment to run TURN server in 'extra' verbose mode.
|
||||||
|
# This mode is very annoying and produces lots of output.
|
||||||
|
# Not recommended under any normal circumstances.
|
||||||
|
#
|
||||||
|
#Verbose
|
||||||
|
|
||||||
|
# Uncomment to use fingerprints in the TURN messages.
|
||||||
|
# By default the fingerprints are off.
|
||||||
|
#
|
||||||
|
#fingerprint
|
||||||
|
|
||||||
|
# Uncomment to use long-term credential mechanism.
|
||||||
|
# By default no credentials mechanism is used (any user allowed).
|
||||||
|
#
|
||||||
|
#lt-cred-mech
|
||||||
|
|
||||||
|
# This option is opposite to lt-cred-mech.
|
||||||
|
# (TURN Server with no-auth option allows anonymous access).
|
||||||
|
# If neither option is defined, and no users are defined,
|
||||||
|
# then no-auth is default. If at least one user is defined,
|
||||||
|
# in this file or in command line or in usersdb file, then
|
||||||
|
# lt-cred-mech is default.
|
||||||
|
#
|
||||||
|
#no-auth
|
||||||
|
|
||||||
|
# TURN REST API flag.
|
||||||
|
# Flag that sets a special authorization option that is based upon authentication secret.
|
||||||
|
# This feature can be used with the long-term authentication mechanism, only.
|
||||||
|
# This feature purpose is to support "TURN Server REST API", see
|
||||||
|
# "TURN REST API" link in the project's page
|
||||||
|
# https://github.com/coturn/coturn/
|
||||||
|
#
|
||||||
|
# This option is used with timestamp:
|
||||||
|
#
|
||||||
|
# usercombo -> "timestamp:userid"
|
||||||
|
# turn user -> usercombo
|
||||||
|
# turn password -> base64(hmac(secret key, usercombo))
|
||||||
|
#
|
||||||
|
# This allows TURN credentials to be accounted for a specific user id.
|
||||||
|
# If you don't have a suitable id, the timestamp alone can be used.
|
||||||
|
# This option is just turning on secret-based authentication.
|
||||||
|
# The actual value of the secret is defined either by option static-auth-secret,
|
||||||
|
# or can be found in the turn_secret table in the database (see below).
|
||||||
|
#
|
||||||
|
#use-auth-secret
|
||||||
|
|
||||||
|
# 'Static' authentication secret value (a string) for TURN REST API only.
|
||||||
|
# If not set, then the turn server
|
||||||
|
# will try to use the 'dynamic' value in turn_secret table
|
||||||
|
# in user database (if present). The database-stored value can be changed on-the-fly
|
||||||
|
# by a separate program, so this is why that other mode is 'dynamic'.
|
||||||
|
#
|
||||||
|
#static-auth-secret=north
|
||||||
|
|
||||||
|
# Server name used for
|
||||||
|
# the oAuth authentication purposes.
|
||||||
|
# The default value is the realm name.
|
||||||
|
#
|
||||||
|
#server-name=blackdow.carleon.gov
|
||||||
|
|
||||||
|
# Flag that allows oAuth authentication.
|
||||||
|
#
|
||||||
|
#oauth
|
||||||
|
|
||||||
|
# 'Static' user accounts for long term credentials mechanism, only.
|
||||||
|
# This option cannot be used with TURN REST API.
|
||||||
|
# 'Static' user accounts are NOT dynamically checked by the turnserver process,
|
||||||
|
# so that they can NOT be changed while the turnserver is running.
|
||||||
|
#
|
||||||
|
#user=username1:key1
|
||||||
|
#user=username2:key2
|
||||||
|
# OR:
|
||||||
|
#user=username1:password1
|
||||||
|
#user=username2:password2
|
||||||
|
#
|
||||||
|
# Keys must be generated by turnadmin utility. The key value depends
|
||||||
|
# on user name, realm, and password:
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
# $ turnadmin -k -u ninefingers -r north.gov -p youhavetoberealistic
|
||||||
|
# Output: 0xbc807ee29df3c9ffa736523fb2c4e8ee
|
||||||
|
# ('0x' in the beginning of the key is what differentiates the key from
|
||||||
|
# password. If it has 0x then it is a key, otherwise it is a password).
|
||||||
|
#
|
||||||
|
# The corresponding user account entry in the config file will be:
|
||||||
|
#
|
||||||
|
#user=ninefingers:0xbc807ee29df3c9ffa736523fb2c4e8ee
|
||||||
|
# Or, equivalently, with open clear password (less secure):
|
||||||
|
#user=ninefingers:youhavetoberealistic
|
||||||
|
#
|
||||||
|
|
||||||
|
# SQLite database file name.
|
||||||
|
#
|
||||||
|
# Default file name is /var/db/turndb or /usr/local/var/db/turndb or
|
||||||
|
# /var/lib/turn/turndb.
|
||||||
|
#
|
||||||
|
#userdb=/var/db/turndb
|
||||||
|
|
||||||
|
# PostgreSQL database connection string in the case that we are using PostgreSQL
|
||||||
|
# as the user database.
|
||||||
|
# This database can be used for long-term credential mechanism
|
||||||
|
# and it can store the secret value for secret-based timed authentication in TURN RESP API.
|
||||||
|
# See http://www.postgresql.org/docs/8.4/static/libpq-connect.html for 8.x PostgreSQL
|
||||||
|
# versions connection string format, see
|
||||||
|
# http://www.postgresql.org/docs/9.2/static/libpq-connect.html#LIBPQ-CONNSTRING
|
||||||
|
# for 9.x and newer connection string formats.
|
||||||
|
#
|
||||||
|
#psql-userdb="host=<host> dbname=<database-name> user=<database-user> password=<database-user-password> connect_timeout=30"
|
||||||
|
|
||||||
|
# MySQL database connection string in the case that we are using MySQL
|
||||||
|
# as the user database.
|
||||||
|
# This database can be used for long-term credential mechanism
|
||||||
|
# and it can store the secret value for secret-based timed authentication in TURN RESP API.
|
||||||
|
#
|
||||||
|
# Optional connection string parameters for the secure communications (SSL):
|
||||||
|
# ca, capath, cert, key, cipher
|
||||||
|
# (see http://dev.mysql.com/doc/refman/5.1/en/ssl-options.html for the
|
||||||
|
# command options description).
|
||||||
|
#
|
||||||
|
# Use string format as below (space separated parameters, all optional):
|
||||||
|
#
|
||||||
|
#mysql-userdb="host=<host> dbname=<database-name> user=<database-user> password=<database-user-password> port=<port> connect_timeout=<seconds>"
|
||||||
|
|
||||||
|
# MongoDB database connection string in the case that we are using MongoDB
|
||||||
|
# as the user database.
|
||||||
|
# This database can be used for long-term credential mechanism
|
||||||
|
# and it can store the secret value for secret-based timed authentication in TURN RESP API.
|
||||||
|
# Use string format is described at http://hergert.me/docs/mongo-c-driver/mongoc_uri.html
|
||||||
|
#
|
||||||
|
#mongo-userdb="mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]]"
|
||||||
|
|
||||||
|
# Redis database connection string in the case that we are using Redis
|
||||||
|
# as the user database.
|
||||||
|
# This database can be used for long-term credential mechanism
|
||||||
|
# and it can store the secret value for secret-based timed authentication in TURN RESP API.
|
||||||
|
# Use string format as below (space separated parameters, all optional):
|
||||||
|
#
|
||||||
|
#redis-userdb="ip=<ip-address> dbname=<database-number> password=<database-user-password> port=<port> connect_timeout=<seconds>"
|
||||||
|
|
||||||
|
# Redis status and statistics database connection string, if used (default - empty, no Redis stats DB used).
|
||||||
|
# This database keeps allocations status information, and it can be also used for publishing
|
||||||
|
# and delivering traffic and allocation event notifications.
|
||||||
|
# The connection string has the same parameters as redis-userdb connection string.
|
||||||
|
# Use string format as below (space separated parameters, all optional):
|
||||||
|
#
|
||||||
|
#redis-statsdb="ip=<ip-address> dbname=<database-number> password=<database-user-password> port=<port> connect_timeout=<seconds>"
|
||||||
|
|
||||||
|
# The default realm to be used for the users when no explicit
|
||||||
|
# origin/realm relationship was found in the database, or if the TURN
|
||||||
|
# server is not using any database (just the commands-line settings
|
||||||
|
# and the userdb file). Must be used with long-term credentials
|
||||||
|
# mechanism or with TURN REST API.
|
||||||
|
#
|
||||||
|
#realm=mycompany.org
|
||||||
|
|
||||||
|
# The flag that sets the origin consistency
|
||||||
|
# check: across the session, all requests must have the same
|
||||||
|
# main ORIGIN attribute value (if the ORIGIN was
|
||||||
|
# initially used by the session).
|
||||||
|
#
|
||||||
|
#check-origin-consistency
|
||||||
|
|
||||||
|
# Per-user allocation quota.
|
||||||
|
# default value is 0 (no quota, unlimited number of sessions per user).
|
||||||
|
# This option can also be set through the database, for a particular realm.
|
||||||
|
#
|
||||||
|
#user-quota=0
|
||||||
|
|
||||||
|
# Total allocation quota.
|
||||||
|
# default value is 0 (no quota).
|
||||||
|
# This option can also be set through the database, for a particular realm.
|
||||||
|
#
|
||||||
|
#total-quota=0
|
||||||
|
|
||||||
|
# Max bytes-per-second bandwidth a TURN session is allowed to handle
|
||||||
|
# (input and output network streams are treated separately). Anything above
|
||||||
|
# that limit will be dropped or temporary suppressed (within
|
||||||
|
# the available buffer limits).
|
||||||
|
# This option can also be set through the database, for a particular realm.
|
||||||
|
#
|
||||||
|
#max-bps=0
|
||||||
|
|
||||||
|
#
|
||||||
|
# Maximum server capacity.
|
||||||
|
# Total bytes-per-second bandwidth the TURN server is allowed to allocate
|
||||||
|
# for the sessions, combined (input and output network streams are treated separately).
|
||||||
|
#
|
||||||
|
# bps-capacity=0
|
||||||
|
|
||||||
|
# Uncomment if no UDP client listener is desired.
|
||||||
|
# By default UDP client listener is always started.
|
||||||
|
#
|
||||||
|
#no-udp
|
||||||
|
|
||||||
|
# Uncomment if no TCP client listener is desired.
|
||||||
|
# By default TCP client listener is always started.
|
||||||
|
#
|
||||||
|
#no-tcp
|
||||||
|
|
||||||
|
# Uncomment if no TLS client listener is desired.
|
||||||
|
# By default TLS client listener is always started.
|
||||||
|
#
|
||||||
|
#no-tls
|
||||||
|
|
||||||
|
# Uncomment if no DTLS client listener is desired.
|
||||||
|
# By default DTLS client listener is always started.
|
||||||
|
#
|
||||||
|
#no-dtls
|
||||||
|
|
||||||
|
# Uncomment if no UDP relay endpoints are allowed.
|
||||||
|
# By default UDP relay endpoints are enabled (like in RFC 5766).
|
||||||
|
#
|
||||||
|
#no-udp-relay
|
||||||
|
|
||||||
|
# Uncomment if no TCP relay endpoints are allowed.
|
||||||
|
# By default TCP relay endpoints are enabled (like in RFC 6062).
|
||||||
|
#
|
||||||
|
#no-tcp-relay
|
||||||
|
|
||||||
|
# Uncomment if extra security is desired,
|
||||||
|
# with nonce value having limited lifetime (600 secs).
|
||||||
|
# By default, the nonce value is unique for a session,
|
||||||
|
# but it has unlimited lifetime. With this option,
|
||||||
|
# the nonce lifetime is limited to 600 seconds, after that
|
||||||
|
# the client will get 438 error and will have to re-authenticate itself.
|
||||||
|
#
|
||||||
|
#stale-nonce
|
||||||
|
|
||||||
|
# Certificate file.
|
||||||
|
# Use an absolute path or path relative to the
|
||||||
|
# configuration file.
|
||||||
|
#
|
||||||
|
#cert=/usr/local/etc/turn_server_cert.pem
|
||||||
|
|
||||||
|
# Private key file.
|
||||||
|
# Use an absolute path or path relative to the
|
||||||
|
# configuration file.
|
||||||
|
# Use PEM file format.
|
||||||
|
#
|
||||||
|
#pkey=/usr/local/etc/turn_server_pkey.pem
|
||||||
|
|
||||||
|
# Private key file password, if it is in encoded format.
|
||||||
|
# This option has no default value.
|
||||||
|
#
|
||||||
|
#pkey-pwd=...
|
||||||
|
|
||||||
|
# Allowed OpenSSL cipher list for TLS/DTLS connections.
|
||||||
|
# Default value is "DEFAULT".
|
||||||
|
#
|
||||||
|
#cipher-list="DEFAULT"
|
||||||
|
|
||||||
|
# CA file in OpenSSL format.
|
||||||
|
# Forces TURN server to verify the client SSL certificates.
|
||||||
|
# By default it is not set: there is no default value and the client
|
||||||
|
# certificate is not checked.
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
#CA-file=/etc/ssh/id_rsa.cert
|
||||||
|
|
||||||
|
# Curve name for EC ciphers, if supported by OpenSSL
|
||||||
|
# library (TLS and DTLS). The default value is prime256v1,
|
||||||
|
# if pre-OpenSSL 1.0.2 is used. With OpenSSL 1.0.2+,
|
||||||
|
# an optimal curve will be automatically calculated, if not defined
|
||||||
|
# by this option.
|
||||||
|
#
|
||||||
|
#ec-curve-name=prime256v1
|
||||||
|
|
||||||
|
# Use 566 bits predefined DH TLS key. Default size of the key is 1066.
|
||||||
|
#
|
||||||
|
#dh566
|
||||||
|
|
||||||
|
# Use 2066 bits predefined DH TLS key. Default size of the key is 1066.
|
||||||
|
#
|
||||||
|
#dh2066
|
||||||
|
|
||||||
|
# Use custom DH TLS key, stored in PEM format in the file.
|
||||||
|
# Flags --dh566 and --dh2066 are ignored when the DH key is taken from a file.
|
||||||
|
#
|
||||||
|
#dh-file=<DH-PEM-file-name>
|
||||||
|
|
||||||
|
# Flag to prevent stdout log messages.
|
||||||
|
# By default, all log messages are going to both stdout and to
|
||||||
|
# the configured log file. With this option everything will be
|
||||||
|
# going to the configured log only (unless the log file itself is stdout).
|
||||||
|
#
|
||||||
|
#no-stdout-log
|
||||||
|
|
||||||
|
# Option to set the log file name.
|
||||||
|
# By default, the turnserver tries to open a log file in
|
||||||
|
# /var/log, /var/tmp, /tmp and current directories directories
|
||||||
|
# (which open operation succeeds first that file will be used).
|
||||||
|
# With this option you can set the definite log file name.
|
||||||
|
# The special names are "stdout" and "-" - they will force everything
|
||||||
|
# to the stdout. Also, the "syslog" name will force everything to
|
||||||
|
# the system log (syslog).
|
||||||
|
# In the runtime, the logfile can be reset with the SIGHUP signal
|
||||||
|
# to the turnserver process.
|
||||||
|
#
|
||||||
|
#log-file=/var/tmp/turn.log
|
||||||
|
|
||||||
|
# Option to redirect all log output into system log (syslog).
|
||||||
|
#
|
||||||
|
#syslog
|
||||||
|
|
||||||
|
# This flag means that no log file rollover will be used, and the log file
|
||||||
|
# name will be constructed as-is, without PID and date appendage.
|
||||||
|
# This option can be used, for example, together with the logrotate tool.
|
||||||
|
#
|
||||||
|
#simple-log
|
||||||
|
|
||||||
|
# Option to set the "redirection" mode. The value of this option
|
||||||
|
# will be the address of the alternate server for UDP & TCP service in form of
|
||||||
|
# <ip>[:<port>]. The server will send this value in the attribute
|
||||||
|
# ALTERNATE-SERVER, with error 300, on ALLOCATE request, to the client.
|
||||||
|
# Client will receive only values with the same address family
|
||||||
|
# as the client network endpoint address family.
|
||||||
|
# See RFC 5389 and RFC 5766 for ALTERNATE-SERVER functionality description.
|
||||||
|
# The client must use the obtained value for subsequent TURN communications.
|
||||||
|
# If more than one --alternate-server options are provided, then the functionality
|
||||||
|
# can be more accurately described as "load-balancing" than a mere "redirection".
|
||||||
|
# If the port number is omitted, then the default port
|
||||||
|
# number 3478 for the UDP/TCP protocols will be used.
|
||||||
|
# Colon (:) characters in IPv6 addresses may conflict with the syntax of
|
||||||
|
# the option. To alleviate this conflict, literal IPv6 addresses are enclosed
|
||||||
|
# in square brackets in such resource identifiers, for example:
|
||||||
|
# [2001:db8:85a3:8d3:1319:8a2e:370:7348]:3478 .
|
||||||
|
# Multiple alternate servers can be set. They will be used in the
|
||||||
|
# round-robin manner. All servers in the pool are considered of equal weight and
|
||||||
|
# the load will be distributed equally. For example, if we have 4 alternate servers,
|
||||||
|
# then each server will receive 25% of ALLOCATE requests. A alternate TURN server
|
||||||
|
# address can be used more than one time with the alternate-server option, so this
|
||||||
|
# can emulate "weighting" of the servers.
|
||||||
|
#
|
||||||
|
# Examples:
|
||||||
|
#alternate-server=1.2.3.4:5678
|
||||||
|
#alternate-server=11.22.33.44:56789
|
||||||
|
#alternate-server=5.6.7.8
|
||||||
|
#alternate-server=[2001:db8:85a3:8d3:1319:8a2e:370:7348]:3478
|
||||||
|
|
||||||
|
# Option to set alternative server for TLS & DTLS services in form of
|
||||||
|
# <ip>:<port>. If the port number is omitted, then the default port
|
||||||
|
# number 5349 for the TLS/DTLS protocols will be used. See the previous
|
||||||
|
# option for the functionality description.
|
||||||
|
#
|
||||||
|
# Examples:
|
||||||
|
#tls-alternate-server=1.2.3.4:5678
|
||||||
|
#tls-alternate-server=11.22.33.44:56789
|
||||||
|
#tls-alternate-server=[2001:db8:85a3:8d3:1319:8a2e:370:7348]:3478
|
||||||
|
|
||||||
|
# Option to suppress TURN functionality, only STUN requests will be processed.
|
||||||
|
# Run as STUN server only, all TURN requests will be ignored.
|
||||||
|
# By default, this option is NOT set.
|
||||||
|
#
|
||||||
|
#stun-only
|
||||||
|
|
||||||
|
# Option to suppress STUN functionality, only TURN requests will be processed.
|
||||||
|
# Run as TURN server only, all STUN requests will be ignored.
|
||||||
|
# By default, this option is NOT set.
|
||||||
|
#
|
||||||
|
#no-stun
|
||||||
|
|
||||||
|
# This is the timestamp/username separator symbol (character) in TURN REST API.
|
||||||
|
# The default value is ':'.
|
||||||
|
# rest-api-separator=:
|
||||||
|
|
||||||
|
# Flag that can be used to disallow peers on the loopback addresses (127.x.x.x and ::1).
|
||||||
|
# This is an extra security measure.
|
||||||
|
#
|
||||||
|
#no-loopback-peers
|
||||||
|
|
||||||
|
# Flag that can be used to disallow peers on well-known broadcast addresses (224.0.0.0 and above, and FFXX:*).
|
||||||
|
# This is an extra security measure.
|
||||||
|
#
|
||||||
|
#no-multicast-peers
|
||||||
|
|
||||||
|
# Option to set the max time, in seconds, allowed for full allocation establishment.
|
||||||
|
# Default is 60 seconds.
|
||||||
|
#
|
||||||
|
#max-allocate-timeout=60
|
||||||
|
|
||||||
|
# Option to allow or ban specific ip addresses or ranges of ip addresses.
|
||||||
|
# If an ip address is specified as both allowed and denied, then the ip address is
|
||||||
|
# considered to be allowed. This is useful when you wish to ban a range of ip
|
||||||
|
# addresses, except for a few specific ips within that range.
|
||||||
|
#
|
||||||
|
# This can be used when you do not want users of the turn server to be able to access
|
||||||
|
# machines reachable by the turn server, but would otherwise be unreachable from the
|
||||||
|
# internet (e.g. when the turn server is sitting behind a NAT)
|
||||||
|
#
|
||||||
|
# Examples:
|
||||||
|
# denied-peer-ip=83.166.64.0-83.166.95.255
|
||||||
|
# allowed-peer-ip=83.166.68.45
|
||||||
|
|
||||||
|
# File name to store the pid of the process.
|
||||||
|
# Default is /var/run/turnserver.pid (if superuser account is used) or
|
||||||
|
# /var/tmp/turnserver.pid .
|
||||||
|
#
|
||||||
|
#pidfile="/var/run/turnserver.pid"
|
||||||
|
|
||||||
|
# Require authentication of the STUN Binding request.
|
||||||
|
# By default, the clients are allowed anonymous access to the STUN Binding functionality.
|
||||||
|
#
|
||||||
|
#secure-stun
|
||||||
|
|
||||||
|
# Mobility with ICE (MICE) specs support.
|
||||||
|
#
|
||||||
|
#mobility
|
||||||
|
|
||||||
|
# User name to run the process. After the initialization, the turnserver process
|
||||||
|
# will make an attempt to change the current user ID to that user.
|
||||||
|
#
|
||||||
|
#proc-user=<user-name>
|
||||||
|
|
||||||
|
# Group name to run the process. After the initialization, the turnserver process
|
||||||
|
# will make an attempt to change the current group ID to that group.
|
||||||
|
#
|
||||||
|
#proc-group=<group-name>
|
||||||
|
|
||||||
|
# Turn OFF the CLI support.
|
||||||
|
# By default it is always ON.
|
||||||
|
# See also options cli-ip and cli-port.
|
||||||
|
#
|
||||||
|
#no-cli
|
||||||
|
|
||||||
|
#Local system IP address to be used for CLI server endpoint. Default value
|
||||||
|
# is 127.0.0.1.
|
||||||
|
#
|
||||||
|
#cli-ip=127.0.0.1
|
||||||
|
|
||||||
|
# CLI server port. Default is 5766.
|
||||||
|
#
|
||||||
|
#cli-port=5766
|
||||||
|
|
||||||
|
# CLI access password. Default is empty (no password).
|
||||||
|
# For the security reasons, it is recommended to use the encrypted
|
||||||
|
# for of the password (see the -P command in the turnadmin utility).
|
||||||
|
#
|
||||||
|
# Secure form for password 'qwerty':
|
||||||
|
#
|
||||||
|
#cli-password=$5$79a316b350311570$81df9cfb9af7f5e5a76eada31e7097b663a0670f99a3c07ded3f1c8e59c5658a
|
||||||
|
#
|
||||||
|
# Or unsecure form for the same paassword:
|
||||||
|
#
|
||||||
|
#cli-password=qwerty
|
||||||
|
|
||||||
|
# Server relay. NON-STANDARD AND DANGEROUS OPTION.
|
||||||
|
# Only for those applications when we want to run
|
||||||
|
# server applications on the relay endpoints.
|
||||||
|
# This option eliminates the IP permissions check on
|
||||||
|
# the packets incoming to the relay endpoints.
|
||||||
|
#
|
||||||
|
#server-relay
|
||||||
|
|
||||||
|
# Maximum number of output sessions in ps CLI command.
|
||||||
|
# This value can be changed on-the-fly in CLI. The default value is 256.
|
||||||
|
#
|
||||||
|
#cli-max-output-sessions
|
||||||
|
|
||||||
|
# Set network engine type for the process (for internal purposes).
|
||||||
|
#
|
||||||
|
#ne=[1|2|3]
|
||||||
|
|
||||||
|
# Do not allow an TLS/DTLS version of protocol
|
||||||
|
#
|
||||||
|
#no-tlsv1
|
||||||
|
#no-tlsv1_1
|
||||||
|
#no-tlsv1_2
|
20
local.js
Normal file
20
local.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
const express = require("express"); //express
|
||||||
|
const conf = require("./conf/cfg"); //conf
|
||||||
|
const fileApiRouters = require("./src/router")(conf); //file routers
|
||||||
|
let resRouter = conf.router.res;
|
||||||
|
|
||||||
|
let app = express();
|
||||||
|
console.log("resource including...")
|
||||||
|
|
||||||
|
//res
|
||||||
|
for(let key in resRouter) app.use(key,express.static(resRouter[key]));
|
||||||
|
|
||||||
|
//file api
|
||||||
|
for(let key in fileApiRouters) app.use(key,fileApiRouters[key])
|
||||||
|
|
||||||
|
|
||||||
|
app.listen(conf.node.port);
|
||||||
|
console.log("express init...")
|
||||||
|
|
||||||
|
|
||||||
|
console.log("server runing on ",conf.node.port," successful");
|
40
localsocket.js
Normal file
40
localsocket.js
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
const http = require('http'); // http
|
||||||
|
const socketIO = require('socket.io'); //socket
|
||||||
|
const app = require("express")(); //express
|
||||||
|
const db = require("./src/tables/db"); //db
|
||||||
|
const conf = require("./conf/cfg"); //conf
|
||||||
|
const utils = require("./utils/request"); //utils
|
||||||
|
const socket = require("./src/socket/index") //socket handler
|
||||||
|
|
||||||
|
let tables = {};
|
||||||
|
if(conf.db.open){
|
||||||
|
// db init
|
||||||
|
let {tables,sql,Sql} = db.excute(conf);
|
||||||
|
app.use(async function (req,res,next) {
|
||||||
|
req.ctx = {};
|
||||||
|
req.ctx.tables = tables;
|
||||||
|
req.ctx.sql = sql;
|
||||||
|
req.ctx.Sql = Sql;
|
||||||
|
await next();
|
||||||
|
})
|
||||||
|
console.log("db init...")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//log flow init --日志流水初始
|
||||||
|
app.use(async function (req,res,next) {
|
||||||
|
res.tl = {};
|
||||||
|
res.tl.flowId = utils.genFlow();
|
||||||
|
await next();
|
||||||
|
})
|
||||||
|
console.log("flow init...")
|
||||||
|
|
||||||
|
//Socket连接监听
|
||||||
|
let io = socketIO.listen(
|
||||||
|
http.createServer().listen(conf.ws.port)
|
||||||
|
);
|
||||||
|
conf.ws.io = io;
|
||||||
|
socket.excute(tables,conf);
|
||||||
|
console.log("socket init...")
|
||||||
|
|
||||||
|
console.log("socket listen on ",conf.ws.port," successful");
|
3
package-lock.json
generated
Normal file
3
package-lock.json
generated
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"lockfileVersion": 1
|
||||||
|
}
|
40
package.json
Normal file
40
package.json
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
{
|
||||||
|
"name": "tl-rtc-file",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "webrtc, p2p,file",
|
||||||
|
"main": "main.js",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "node local",
|
||||||
|
"svr" : "node server",
|
||||||
|
"devsocket" : "node localsocket",
|
||||||
|
"svrsocket" : "node serversocket"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": ""
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"none"
|
||||||
|
],
|
||||||
|
"author": "iamtsm",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"cookie-parser": "^1.4.5",
|
||||||
|
"dottie": "^2.0.2",
|
||||||
|
"ejs": "^3.1.3",
|
||||||
|
"express": "^4.17.1",
|
||||||
|
"inflection": "^1.12.0",
|
||||||
|
"moment-timezone": "^0.5.31",
|
||||||
|
"mysql2": "^2.1.0",
|
||||||
|
"retry-as-promised": "^3.2.0",
|
||||||
|
"sequelize": "^6.1.0",
|
||||||
|
"sequelize-pool": "^6.0.0",
|
||||||
|
"socket.io": "^2.3.0",
|
||||||
|
"toposort-class": "^1.0.1",
|
||||||
|
"validator": "^13.1.1",
|
||||||
|
"wkx": "^0.5.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
88
res/css/comm.css
Normal file
88
res/css/comm.css
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
|
||||||
|
[v-cloak]{
|
||||||
|
visibility: hidden !important; /*防止被覆盖*/
|
||||||
|
}
|
||||||
|
|
||||||
|
/* svg主题 */
|
||||||
|
.svg_black{
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
fill: black;
|
||||||
|
}
|
||||||
|
.svg_white{
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
fill: #168ce6;
|
||||||
|
}
|
||||||
|
.svg_black:hover{
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
fill: #168ce6;
|
||||||
|
}
|
||||||
|
.svg_white:hover{
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
fill: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 按钮主题 */
|
||||||
|
.btn_black{
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
background-color: black;
|
||||||
|
}
|
||||||
|
.btn_white{
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
background-color: #168ce6;
|
||||||
|
}
|
||||||
|
.btn_black:hover{
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
background-color: #168ce6;
|
||||||
|
}
|
||||||
|
.btn_white:hover{
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
background-color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 字体主题 */
|
||||||
|
.font_black{
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
.font_white{
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
color: #168ce6;
|
||||||
|
}
|
||||||
|
.font_black:hover{
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
color: #168ce6;
|
||||||
|
}
|
||||||
|
.font_white:hover{
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* 侧边栏主题 */
|
||||||
|
::-webkit-scrollbar{
|
||||||
|
width: 4px;
|
||||||
|
height: 4px;
|
||||||
|
background-color: #F5F5F5;
|
||||||
|
}
|
||||||
|
::-webkit-scrollbar-track{
|
||||||
|
-webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3);
|
||||||
|
border-radius: 10px;
|
||||||
|
background-color: #FFF;
|
||||||
|
}
|
||||||
|
::-webkit-scrollbar-thumb{
|
||||||
|
border-radius: 10px;
|
||||||
|
-webkit-box-shadow: inset 0 0 6px rgba(0,0,0,.3);
|
||||||
|
background-color: #AAA;
|
||||||
|
}
|
90
res/css/comm.min.css
vendored
Normal file
90
res/css/comm.min.css
vendored
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
|
||||||
|
[v-cloak]{
|
||||||
|
visibility: hidden !important; /*防止被覆盖*/
|
||||||
|
}
|
||||||
|
|
||||||
|
/* svg主题 */
|
||||||
|
.svg_black{
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
fill: black;
|
||||||
|
}
|
||||||
|
.svg_white{
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
fill: #168ce6;
|
||||||
|
}
|
||||||
|
.svg_black:hover{
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
fill: #168ce6;
|
||||||
|
}
|
||||||
|
.svg_white:hover{
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
fill: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 按钮主题 */
|
||||||
|
.btn_black{
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
background-color: black;
|
||||||
|
}
|
||||||
|
.btn_white{
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
background-color: #168ce6;
|
||||||
|
}
|
||||||
|
.btn_black:hover{
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
background-color: #168ce6;
|
||||||
|
}
|
||||||
|
.btn_white:hover{
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
background-color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 字体主题 */
|
||||||
|
.font_black{
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
.font_white{
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
color: #168ce6;
|
||||||
|
}
|
||||||
|
.font_black:hover{
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
color: #168ce6;
|
||||||
|
}
|
||||||
|
.font_white:hover{
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* 侧边栏主题 */
|
||||||
|
::-webkit-scrollbar{
|
||||||
|
width: 4px;
|
||||||
|
height: 4px;
|
||||||
|
background-color: #F5F5F5;
|
||||||
|
}
|
||||||
|
::-webkit-scrollbar-track{
|
||||||
|
-webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3);
|
||||||
|
border-radius: 10px;
|
||||||
|
background-color: #FFF;
|
||||||
|
}
|
||||||
|
::-webkit-scrollbar-thumb{
|
||||||
|
border-radius: 10px;
|
||||||
|
-webkit-box-shadow: inset 0 0 6px rgba(0,0,0,.3);
|
||||||
|
background-color: #AAA;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*# sourceMappingURL=comm.min.css.map*/
|
1
res/css/comm.min.css.map
Normal file
1
res/css/comm.min.css.map
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sources":["webpack:///E:/code/tl-rtc-file/res/css/comm.css"],"names":[],"mappings":";AACA;AACA,kCAAkC;AAClC;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,C","file":"comm.min.css","sourcesContent":["\r\n[v-cloak]{\r\n visibility: hidden !important; /*防止被覆盖*/\r\n}\r\n\r\n/* svg主题 */\r\n.svg_black{\r\n width: 22px;\r\n height: 22px;\r\n fill: black;\r\n}\r\n.svg_white{\r\n width: 22px;\r\n height: 22px;\r\n fill: #168ce6;\r\n}\r\n.svg_black:hover{\r\n width: 22px;\r\n height: 22px;\r\n fill: #168ce6;\r\n}\r\n.svg_white:hover{\r\n width: 22px;\r\n height: 22px;\r\n fill: black;\r\n}\r\n\r\n/* 按钮主题 */\r\n.btn_black{\r\n width: 22px;\r\n height: 22px;\r\n background-color: black;\r\n}\r\n.btn_white{\r\n width: 22px;\r\n height: 22px;\r\n background-color: #168ce6;\r\n}\r\n.btn_black:hover{\r\n width: 22px;\r\n height: 22px;\r\n background-color: #168ce6;\r\n}\r\n.btn_white:hover{\r\n width: 22px;\r\n height: 22px;\r\n background-color: black;\r\n}\r\n\r\n/* 字体主题 */\r\n.font_black{\r\n width: 22px;\r\n height: 22px;\r\n color: black;\r\n}\r\n.font_white{\r\n width: 22px;\r\n height: 22px;\r\n color: #168ce6;\r\n}\r\n.font_black:hover{\r\n width: 22px;\r\n height: 22px;\r\n color: #168ce6;\r\n}\r\n.font_white:hover{\r\n width: 22px;\r\n height: 22px;\r\n color: black;\r\n}\r\n\r\n\r\n/* 侧边栏主题 */\r\n::-webkit-scrollbar{\r\n width: 4px;\r\n height: 4px;\r\n background-color: #F5F5F5;\r\n}\r\n::-webkit-scrollbar-track{\r\n -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3);\r\n border-radius: 10px;\r\n background-color: #FFF;\r\n}\r\n::-webkit-scrollbar-thumb{\r\n border-radius: 10px;\r\n -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,.3);\r\n background-color: #AAA;\r\n}"],"sourceRoot":""}
|
433
res/css/index.css
Normal file
433
res/css/index.css
Normal file
@@ -0,0 +1,433 @@
|
|||||||
|
html {
|
||||||
|
box-sizing: border-box;
|
||||||
|
--bgColorMenu : #1d1d27;
|
||||||
|
--duration: .7s;
|
||||||
|
}
|
||||||
|
html *,
|
||||||
|
html *::before,
|
||||||
|
html *::after {
|
||||||
|
box-sizing: inherit;
|
||||||
|
}
|
||||||
|
body{
|
||||||
|
margin: 0;
|
||||||
|
margin-left: 5%;
|
||||||
|
max-width: 90%;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background-color: #ffb457;
|
||||||
|
transition: background-color var(--duration);
|
||||||
|
-webkit-tap-highlight-color: transparent;
|
||||||
|
}
|
||||||
|
.menu{
|
||||||
|
display: flex;
|
||||||
|
width: 90%;
|
||||||
|
font-size: .8em;
|
||||||
|
padding: 0 2.85em;
|
||||||
|
bottom: 0;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 10px;
|
||||||
|
background-color: var(--bgColorMenu);
|
||||||
|
position: fixed;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
.menu__item{
|
||||||
|
all: unset;
|
||||||
|
flex-grow: 1;
|
||||||
|
z-index: 80;
|
||||||
|
display: flex;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 0.55em 0 0.85em;
|
||||||
|
transition: transform var(--duration);
|
||||||
|
}
|
||||||
|
.menu__item::before{
|
||||||
|
content: "";
|
||||||
|
z-index: -1;
|
||||||
|
width: 4.2em;
|
||||||
|
height: 4.2em;
|
||||||
|
border-radius: 50%;
|
||||||
|
position: absolute;
|
||||||
|
transform: scale(0);
|
||||||
|
transition: background-color var(--duration), transform var(--duration);
|
||||||
|
}
|
||||||
|
.menu__item.active {
|
||||||
|
transform: translate3d(0, -.8em , 0);
|
||||||
|
}
|
||||||
|
.menu__item.active::before{
|
||||||
|
transform: scale(1);
|
||||||
|
background-color: var(--bgColorItem);
|
||||||
|
}
|
||||||
|
.icon{
|
||||||
|
width: 2.6em;
|
||||||
|
height: 2.6em;
|
||||||
|
stroke: white;
|
||||||
|
fill: transparent;
|
||||||
|
stroke-width: 1pt;
|
||||||
|
stroke-miterlimit: 10;
|
||||||
|
stroke-linecap: round;
|
||||||
|
stroke-linejoin: round;
|
||||||
|
stroke-dasharray: 400;
|
||||||
|
|
||||||
|
}
|
||||||
|
.menu__item.active .icon {
|
||||||
|
animation: strok 1.5s reverse;
|
||||||
|
}
|
||||||
|
@keyframes strok {
|
||||||
|
100% {
|
||||||
|
stroke-dashoffset: 400;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.menu__border{
|
||||||
|
left: 0;
|
||||||
|
bottom: 99%;
|
||||||
|
width: 10.9em;
|
||||||
|
height: 2.4em;
|
||||||
|
position: absolute;
|
||||||
|
clip-path: url(#menu);
|
||||||
|
background-color: var(--bgColorMenu);
|
||||||
|
transition: transform var(--duration);
|
||||||
|
}
|
||||||
|
#fileApp .cusInput{
|
||||||
|
border-radius: 10px;
|
||||||
|
width: 70%;
|
||||||
|
margin-left: 15%;
|
||||||
|
height: 54px;
|
||||||
|
padding: 0 10px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-family: "微软雅黑";
|
||||||
|
border: none;
|
||||||
|
color: #332828;
|
||||||
|
vertical-align: middle;
|
||||||
|
outline: none;
|
||||||
|
overflow: hidden;
|
||||||
|
background-color: #e9e9e9;
|
||||||
|
}
|
||||||
|
#fileApp .maskBottom{
|
||||||
|
position: fixed;
|
||||||
|
top: 50%;
|
||||||
|
left: 0;
|
||||||
|
height: 50%;
|
||||||
|
width: 100%;
|
||||||
|
background: white;
|
||||||
|
z-index: 99;
|
||||||
|
box-shadow: 0 0px 20px rgba(0,0,0,.4);
|
||||||
|
transition: all 1s;
|
||||||
|
-moz-transition: all 1s;
|
||||||
|
-webkit-transition: all 1s;
|
||||||
|
-o-transition: all 1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
#fileApp .maskRight{
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 50%;
|
||||||
|
height: 100%;
|
||||||
|
width: 50%;
|
||||||
|
background: white;
|
||||||
|
z-index: 99;
|
||||||
|
box-shadow: 0 0px 20px rgb(0 0 0 / 40%);
|
||||||
|
transition: all 1s;
|
||||||
|
-moz-transition: all 1s;
|
||||||
|
-webkit-transition: all 1s;
|
||||||
|
-o-transition: all 1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
#fileApp table tr td span{
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
#fileApp .exitBtn{
|
||||||
|
position: absolute;
|
||||||
|
right: 20%;
|
||||||
|
}
|
||||||
|
#fileApp .roomClass{
|
||||||
|
margin-top: 5%;
|
||||||
|
transition : margin-top 0.5s;
|
||||||
|
}
|
||||||
|
#fileApp .roomClass button{
|
||||||
|
-webkit-appearance: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
outline: none;
|
||||||
|
cursor: pointer;
|
||||||
|
width: 7rem;
|
||||||
|
height: 3rem;
|
||||||
|
background-image: linear-gradient(to top, #D8D9DB 0%, #fff 80%, #FDFDFD 100%);
|
||||||
|
border-radius: 30px;
|
||||||
|
border: 1px solid #8F9092;
|
||||||
|
box-shadow: 0 4px 3px 1px #FCFCFC, 0 6px 8px #D6D7D9, 0 -4px 4px #CECFD1, 0 -6px 4px #FEFEFE, inset 0 0 3px 0 #CECFD1;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
font-family: "Source Sans Pro", sans-serif;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #606060;
|
||||||
|
text-shadow: 0 1px #fff;
|
||||||
|
margin-left: 20px;
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
|
#fileApp .roomClass button[disabled]{
|
||||||
|
border: 1px solid #e6e6e6;
|
||||||
|
background-color: #FBFBFB;
|
||||||
|
color: #C9C9C9;
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
#fileApp .roomClass button:hover{
|
||||||
|
box-shadow: 0 4px 3px 1px #FCFCFC, 0 6px 8px #D6D7D9, 0 -4px 4px #CECFD1, 0 -6px 4px #FEFEFE, inset 0 0 3px 3px #CECFD1;
|
||||||
|
}
|
||||||
|
#fileApp .roomClass .center{
|
||||||
|
padding: 10px;
|
||||||
|
display: inline-flex;
|
||||||
|
width: 70%;
|
||||||
|
margin-left: 15%;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
#fileApp .roomClass .mobileCenter{
|
||||||
|
padding: 10px;
|
||||||
|
display: inline-flex;
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
#fileApp #self{
|
||||||
|
width: 70%;
|
||||||
|
margin-left: 15%;
|
||||||
|
margin-top: 40px;
|
||||||
|
display: grid;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
#fileApp #self span{
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
#fileApp #self input{
|
||||||
|
margin-bottom: 10px;
|
||||||
|
width: 72px;
|
||||||
|
}
|
||||||
|
#fileApp #self button{
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.sendFileBtn {
|
||||||
|
margin-left: 30%;
|
||||||
|
color: #0d1322;
|
||||||
|
background-color: #3be8b0;
|
||||||
|
border: 0;
|
||||||
|
padding: 0 25px;
|
||||||
|
outline: none;
|
||||||
|
width: 40%;
|
||||||
|
border-radius: 25px;
|
||||||
|
font-size: 1.4rem;
|
||||||
|
display: -ms-inline-flexbox;
|
||||||
|
display: inline-flex;
|
||||||
|
-ms-flex-align: center;
|
||||||
|
align-items: center;
|
||||||
|
-ms-flex-pack: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 50px;
|
||||||
|
line-height: 50px;
|
||||||
|
cursor: pointer;
|
||||||
|
box-shadow: 0 0px 20px rgba(0,0,0,.4);
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
transition: all .2s ease;
|
||||||
|
}
|
||||||
|
.sendFileBtn:hover{
|
||||||
|
box-shadow: 0 8px 35px rgba(0,0,0,.4);
|
||||||
|
transition: all .2s ease;
|
||||||
|
-webkit-tap-highlight-color: rgba(4,32,23,.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* 480px以下 */
|
||||||
|
@media screen and (max-width: 480px) {
|
||||||
|
#fileApp .exitBtn{
|
||||||
|
position: absolute;
|
||||||
|
right: 10%;
|
||||||
|
}
|
||||||
|
#fileApp .roomClass button{
|
||||||
|
width: 6rem;
|
||||||
|
height: 2.5rem;
|
||||||
|
}
|
||||||
|
#fileApp .roomClass .center{
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 20px;
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
.sendFilePannel{
|
||||||
|
/* margin-top: 15%; */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* 500px以下 */
|
||||||
|
@media screen and (max-width: 500px) {
|
||||||
|
.sendFileBtn{
|
||||||
|
width: 80%;
|
||||||
|
margin-left: 10%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* 480px到765px */
|
||||||
|
@media screen and (min-width: 480px) and (max-width: 765px) {
|
||||||
|
/* .sendFilePannel{
|
||||||
|
margin-top: 10%;
|
||||||
|
} */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 765px以上 */
|
||||||
|
@media screen and (min-width: 765px) {
|
||||||
|
.sendFilePannel{
|
||||||
|
/* margin-top: 25%; */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* 765px到1000px */
|
||||||
|
@media screen and (min-width: 765px) and (max-width: 775px) {
|
||||||
|
.sendFilePannel{
|
||||||
|
/* margin-top: 24%; */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media screen and (min-width: 1000px) {
|
||||||
|
.sendFilePannel{
|
||||||
|
/* margin-top: 20%; */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool{
|
||||||
|
position: absolute;
|
||||||
|
right: 5px;
|
||||||
|
float: right;
|
||||||
|
top: 10px;
|
||||||
|
}
|
||||||
|
.tool svg{
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.userRoom{
|
||||||
|
padding-left: 15px !important;
|
||||||
|
width: 80%;
|
||||||
|
margin-left: 10% !important;
|
||||||
|
margin-top: 130px;
|
||||||
|
height: 120px;
|
||||||
|
display: inline-flex;
|
||||||
|
}
|
||||||
|
.userBlocK{
|
||||||
|
cursor: pointer !important;
|
||||||
|
margin: 0 0 15px !important;
|
||||||
|
display: grid !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chooseFileName {
|
||||||
|
margin-top: 10px;
|
||||||
|
word-break: break-all;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
padding: 7px 3px 7px 3px;
|
||||||
|
background: rgb(255, 180, 87);
|
||||||
|
box-shadow: 0 0px 20px rgba(0,0,0,.4);
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
.remoteId{
|
||||||
|
width: 63px;
|
||||||
|
margin-top: -10px;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sendFilePannel{
|
||||||
|
transition: all .2s ease-in-out;
|
||||||
|
border-radius: 5px;
|
||||||
|
box-shadow: 0 8px 35px rgba(0,0,0,.4);
|
||||||
|
}
|
||||||
|
.sendFileHeader{
|
||||||
|
border-bottom: 1px solid #fbe1e1;
|
||||||
|
padding: .3rem 1rem;
|
||||||
|
display: -ms-flexbox;
|
||||||
|
display: flex;
|
||||||
|
-ms-flex-align: baseline;
|
||||||
|
align-items: baseline;
|
||||||
|
-ms-flex-pack: justify;
|
||||||
|
justify-content: space-between;
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
.sendFiles{
|
||||||
|
margin: 5px 0;
|
||||||
|
max-height: 330px;
|
||||||
|
padding: 0 0px 10px;
|
||||||
|
}
|
||||||
|
.sendFile{
|
||||||
|
display: -ms-flexbox;
|
||||||
|
display: flex;
|
||||||
|
-ms-flex-align: center;
|
||||||
|
align-items: center;
|
||||||
|
max-height: 340px;
|
||||||
|
padding: 5px 5px 4px;
|
||||||
|
overflow-x: hidden;
|
||||||
|
margin: 5px 11px;
|
||||||
|
}
|
||||||
|
.sendFileType{
|
||||||
|
margin-right: 1rem;
|
||||||
|
color: #636979;
|
||||||
|
}
|
||||||
|
.sendFileInfo{
|
||||||
|
text-align: left;
|
||||||
|
-ms-flex: 1;
|
||||||
|
flex: 1;
|
||||||
|
width: 60%;
|
||||||
|
margin-right: 1rem;
|
||||||
|
}
|
||||||
|
.sendFileName{
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 15px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow-x: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.sendFileOther{
|
||||||
|
margin: 0;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #636979;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
.sendInfoBackgroud{
|
||||||
|
display: none;
|
||||||
|
background: #ff00002e;
|
||||||
|
width: 116px;
|
||||||
|
height: 40px;
|
||||||
|
position: absolute;
|
||||||
|
padding: 25px;
|
||||||
|
border-radius: 3px;
|
||||||
|
margin-left: -20px;
|
||||||
|
}
|
||||||
|
.noSendFile{
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: url(./../../image/noSendFiles.png) no-repeat center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logs{
|
||||||
|
text-align: center;
|
||||||
|
border-bottom: 1px solid #f6f6f6;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.infoMsg{
|
||||||
|
width: 70%;
|
||||||
|
margin-left: 15%;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 0.1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layui-table td, .layui-table th{
|
||||||
|
font-size: 12px !important;
|
||||||
|
}
|
435
res/css/index.min.css
vendored
Normal file
435
res/css/index.min.css
vendored
Normal file
@@ -0,0 +1,435 @@
|
|||||||
|
html {
|
||||||
|
box-sizing: border-box;
|
||||||
|
--bgColorMenu : #1d1d27;
|
||||||
|
--duration: .7s;
|
||||||
|
}
|
||||||
|
html *,
|
||||||
|
html *::before,
|
||||||
|
html *::after {
|
||||||
|
box-sizing: inherit;
|
||||||
|
}
|
||||||
|
body{
|
||||||
|
margin: 0;
|
||||||
|
margin-left: 5%;
|
||||||
|
max-width: 90%;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background-color: #ffb457;
|
||||||
|
transition: background-color var(--duration);
|
||||||
|
-webkit-tap-highlight-color: transparent;
|
||||||
|
}
|
||||||
|
.menu{
|
||||||
|
display: flex;
|
||||||
|
width: 90%;
|
||||||
|
font-size: .8em;
|
||||||
|
padding: 0 2.85em;
|
||||||
|
bottom: 0;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 10px;
|
||||||
|
background-color: var(--bgColorMenu);
|
||||||
|
position: fixed;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
.menu__item{
|
||||||
|
all: unset;
|
||||||
|
flex-grow: 1;
|
||||||
|
z-index: 80;
|
||||||
|
display: flex;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 0.55em 0 0.85em;
|
||||||
|
transition: transform var(--duration);
|
||||||
|
}
|
||||||
|
.menu__item::before{
|
||||||
|
content: "";
|
||||||
|
z-index: -1;
|
||||||
|
width: 4.2em;
|
||||||
|
height: 4.2em;
|
||||||
|
border-radius: 50%;
|
||||||
|
position: absolute;
|
||||||
|
transform: scale(0);
|
||||||
|
transition: background-color var(--duration), transform var(--duration);
|
||||||
|
}
|
||||||
|
.menu__item.active {
|
||||||
|
transform: translate3d(0, -.8em , 0);
|
||||||
|
}
|
||||||
|
.menu__item.active::before{
|
||||||
|
transform: scale(1);
|
||||||
|
background-color: var(--bgColorItem);
|
||||||
|
}
|
||||||
|
.icon{
|
||||||
|
width: 2.6em;
|
||||||
|
height: 2.6em;
|
||||||
|
stroke: white;
|
||||||
|
fill: transparent;
|
||||||
|
stroke-width: 1pt;
|
||||||
|
stroke-miterlimit: 10;
|
||||||
|
stroke-linecap: round;
|
||||||
|
stroke-linejoin: round;
|
||||||
|
stroke-dasharray: 400;
|
||||||
|
|
||||||
|
}
|
||||||
|
.menu__item.active .icon {
|
||||||
|
animation: strok 1.5s reverse;
|
||||||
|
}
|
||||||
|
@keyframes strok {
|
||||||
|
100% {
|
||||||
|
stroke-dashoffset: 400;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.menu__border{
|
||||||
|
left: 0;
|
||||||
|
bottom: 99%;
|
||||||
|
width: 10.9em;
|
||||||
|
height: 2.4em;
|
||||||
|
position: absolute;
|
||||||
|
clip-path: url(#menu);
|
||||||
|
background-color: var(--bgColorMenu);
|
||||||
|
transition: transform var(--duration);
|
||||||
|
}
|
||||||
|
#fileApp .cusInput{
|
||||||
|
border-radius: 10px;
|
||||||
|
width: 70%;
|
||||||
|
margin-left: 15%;
|
||||||
|
height: 54px;
|
||||||
|
padding: 0 10px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-family: "微软雅黑";
|
||||||
|
border: none;
|
||||||
|
color: #332828;
|
||||||
|
vertical-align: middle;
|
||||||
|
outline: none;
|
||||||
|
overflow: hidden;
|
||||||
|
background-color: #e9e9e9;
|
||||||
|
}
|
||||||
|
#fileApp .maskBottom{
|
||||||
|
position: fixed;
|
||||||
|
top: 50%;
|
||||||
|
left: 0;
|
||||||
|
height: 50%;
|
||||||
|
width: 100%;
|
||||||
|
background: white;
|
||||||
|
z-index: 99;
|
||||||
|
box-shadow: 0 0px 20px rgba(0,0,0,.4);
|
||||||
|
transition: all 1s;
|
||||||
|
-moz-transition: all 1s;
|
||||||
|
-webkit-transition: all 1s;
|
||||||
|
-o-transition: all 1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
#fileApp .maskRight{
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 50%;
|
||||||
|
height: 100%;
|
||||||
|
width: 50%;
|
||||||
|
background: white;
|
||||||
|
z-index: 99;
|
||||||
|
box-shadow: 0 0px 20px rgb(0 0 0 / 40%);
|
||||||
|
transition: all 1s;
|
||||||
|
-moz-transition: all 1s;
|
||||||
|
-webkit-transition: all 1s;
|
||||||
|
-o-transition: all 1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
#fileApp table tr td span{
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
#fileApp .exitBtn{
|
||||||
|
position: absolute;
|
||||||
|
right: 20%;
|
||||||
|
}
|
||||||
|
#fileApp .roomClass{
|
||||||
|
margin-top: 5%;
|
||||||
|
transition : margin-top 0.5s;
|
||||||
|
}
|
||||||
|
#fileApp .roomClass button{
|
||||||
|
-webkit-appearance: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
outline: none;
|
||||||
|
cursor: pointer;
|
||||||
|
width: 7rem;
|
||||||
|
height: 3rem;
|
||||||
|
background-image: linear-gradient(to top, #D8D9DB 0%, #fff 80%, #FDFDFD 100%);
|
||||||
|
border-radius: 30px;
|
||||||
|
border: 1px solid #8F9092;
|
||||||
|
box-shadow: 0 4px 3px 1px #FCFCFC, 0 6px 8px #D6D7D9, 0 -4px 4px #CECFD1, 0 -6px 4px #FEFEFE, inset 0 0 3px 0 #CECFD1;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
font-family: "Source Sans Pro", sans-serif;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #606060;
|
||||||
|
text-shadow: 0 1px #fff;
|
||||||
|
margin-left: 20px;
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
|
#fileApp .roomClass button[disabled]{
|
||||||
|
border: 1px solid #e6e6e6;
|
||||||
|
background-color: #FBFBFB;
|
||||||
|
color: #C9C9C9;
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
#fileApp .roomClass button:hover{
|
||||||
|
box-shadow: 0 4px 3px 1px #FCFCFC, 0 6px 8px #D6D7D9, 0 -4px 4px #CECFD1, 0 -6px 4px #FEFEFE, inset 0 0 3px 3px #CECFD1;
|
||||||
|
}
|
||||||
|
#fileApp .roomClass .center{
|
||||||
|
padding: 10px;
|
||||||
|
display: inline-flex;
|
||||||
|
width: 70%;
|
||||||
|
margin-left: 15%;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
#fileApp .roomClass .mobileCenter{
|
||||||
|
padding: 10px;
|
||||||
|
display: inline-flex;
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
#fileApp #self{
|
||||||
|
width: 70%;
|
||||||
|
margin-left: 15%;
|
||||||
|
margin-top: 40px;
|
||||||
|
display: grid;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
#fileApp #self span{
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
#fileApp #self input{
|
||||||
|
margin-bottom: 10px;
|
||||||
|
width: 72px;
|
||||||
|
}
|
||||||
|
#fileApp #self button{
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.sendFileBtn {
|
||||||
|
margin-left: 30%;
|
||||||
|
color: #0d1322;
|
||||||
|
background-color: #3be8b0;
|
||||||
|
border: 0;
|
||||||
|
padding: 0 25px;
|
||||||
|
outline: none;
|
||||||
|
width: 40%;
|
||||||
|
border-radius: 25px;
|
||||||
|
font-size: 1.4rem;
|
||||||
|
display: -ms-inline-flexbox;
|
||||||
|
display: inline-flex;
|
||||||
|
-ms-flex-align: center;
|
||||||
|
align-items: center;
|
||||||
|
-ms-flex-pack: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 50px;
|
||||||
|
line-height: 50px;
|
||||||
|
cursor: pointer;
|
||||||
|
box-shadow: 0 0px 20px rgba(0,0,0,.4);
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
transition: all .2s ease;
|
||||||
|
}
|
||||||
|
.sendFileBtn:hover{
|
||||||
|
box-shadow: 0 8px 35px rgba(0,0,0,.4);
|
||||||
|
transition: all .2s ease;
|
||||||
|
-webkit-tap-highlight-color: rgba(4,32,23,.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* 480px以下 */
|
||||||
|
@media screen and (max-width: 480px) {
|
||||||
|
#fileApp .exitBtn{
|
||||||
|
position: absolute;
|
||||||
|
right: 10%;
|
||||||
|
}
|
||||||
|
#fileApp .roomClass button{
|
||||||
|
width: 6rem;
|
||||||
|
height: 2.5rem;
|
||||||
|
}
|
||||||
|
#fileApp .roomClass .center{
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 20px;
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
.sendFilePannel{
|
||||||
|
/* margin-top: 15%; */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* 500px以下 */
|
||||||
|
@media screen and (max-width: 500px) {
|
||||||
|
.sendFileBtn{
|
||||||
|
width: 80%;
|
||||||
|
margin-left: 10%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* 480px到765px */
|
||||||
|
@media screen and (min-width: 480px) and (max-width: 765px) {
|
||||||
|
/* .sendFilePannel{
|
||||||
|
margin-top: 10%;
|
||||||
|
} */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 765px以上 */
|
||||||
|
@media screen and (min-width: 765px) {
|
||||||
|
.sendFilePannel{
|
||||||
|
/* margin-top: 25%; */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* 765px到1000px */
|
||||||
|
@media screen and (min-width: 765px) and (max-width: 775px) {
|
||||||
|
.sendFilePannel{
|
||||||
|
/* margin-top: 24%; */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media screen and (min-width: 1000px) {
|
||||||
|
.sendFilePannel{
|
||||||
|
/* margin-top: 20%; */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool{
|
||||||
|
position: absolute;
|
||||||
|
right: 5px;
|
||||||
|
float: right;
|
||||||
|
top: 10px;
|
||||||
|
}
|
||||||
|
.tool svg{
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.userRoom{
|
||||||
|
padding-left: 15px !important;
|
||||||
|
width: 80%;
|
||||||
|
margin-left: 10% !important;
|
||||||
|
margin-top: 130px;
|
||||||
|
height: 120px;
|
||||||
|
display: inline-flex;
|
||||||
|
}
|
||||||
|
.userBlocK{
|
||||||
|
cursor: pointer !important;
|
||||||
|
margin: 0 0 15px !important;
|
||||||
|
display: grid !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chooseFileName {
|
||||||
|
margin-top: 10px;
|
||||||
|
word-break: break-all;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
padding: 7px 3px 7px 3px;
|
||||||
|
background: rgb(255, 180, 87);
|
||||||
|
box-shadow: 0 0px 20px rgba(0,0,0,.4);
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
.remoteId{
|
||||||
|
width: 63px;
|
||||||
|
margin-top: -10px;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sendFilePannel{
|
||||||
|
transition: all .2s ease-in-out;
|
||||||
|
border-radius: 5px;
|
||||||
|
box-shadow: 0 8px 35px rgba(0,0,0,.4);
|
||||||
|
}
|
||||||
|
.sendFileHeader{
|
||||||
|
border-bottom: 1px solid #fbe1e1;
|
||||||
|
padding: .3rem 1rem;
|
||||||
|
display: -ms-flexbox;
|
||||||
|
display: flex;
|
||||||
|
-ms-flex-align: baseline;
|
||||||
|
align-items: baseline;
|
||||||
|
-ms-flex-pack: justify;
|
||||||
|
justify-content: space-between;
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
.sendFiles{
|
||||||
|
margin: 5px 0;
|
||||||
|
max-height: 330px;
|
||||||
|
padding: 0 0px 10px;
|
||||||
|
}
|
||||||
|
.sendFile{
|
||||||
|
display: -ms-flexbox;
|
||||||
|
display: flex;
|
||||||
|
-ms-flex-align: center;
|
||||||
|
align-items: center;
|
||||||
|
max-height: 340px;
|
||||||
|
padding: 5px 5px 4px;
|
||||||
|
overflow-x: hidden;
|
||||||
|
margin: 5px 11px;
|
||||||
|
}
|
||||||
|
.sendFileType{
|
||||||
|
margin-right: 1rem;
|
||||||
|
color: #636979;
|
||||||
|
}
|
||||||
|
.sendFileInfo{
|
||||||
|
text-align: left;
|
||||||
|
-ms-flex: 1;
|
||||||
|
flex: 1;
|
||||||
|
width: 60%;
|
||||||
|
margin-right: 1rem;
|
||||||
|
}
|
||||||
|
.sendFileName{
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 15px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow-x: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.sendFileOther{
|
||||||
|
margin: 0;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #636979;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
.sendInfoBackgroud{
|
||||||
|
display: none;
|
||||||
|
background: #ff00002e;
|
||||||
|
width: 116px;
|
||||||
|
height: 40px;
|
||||||
|
position: absolute;
|
||||||
|
padding: 25px;
|
||||||
|
border-radius: 3px;
|
||||||
|
margin-left: -20px;
|
||||||
|
}
|
||||||
|
.noSendFile{
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: url(./../../image/noSendFiles.png) no-repeat center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logs{
|
||||||
|
text-align: center;
|
||||||
|
border-bottom: 1px solid #f6f6f6;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.infoMsg{
|
||||||
|
width: 70%;
|
||||||
|
margin-left: 15%;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 0.1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layui-table td, .layui-table th{
|
||||||
|
font-size: 12px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*# sourceMappingURL=index.min.css.map*/
|
1
res/css/index.min.css.map
Normal file
1
res/css/index.min.css.map
Normal file
File diff suppressed because one or more lines are too long
13
res/css/swiper-bundle.min.css
vendored
Normal file
13
res/css/swiper-bundle.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
res/image/11.ico
Normal file
BIN
res/image/11.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 50 KiB |
BIN
res/image/bg_body_001.jpg
Normal file
BIN
res/image/bg_body_001.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 274 KiB |
BIN
res/image/noSendFiles.png
Normal file
BIN
res/image/noSendFiles.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.9 KiB |
262
res/index.html
Normal file
262
res/index.html
Normal file
@@ -0,0 +1,262 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||||
|
<title>tlp2p</title>
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="https://www.layuicdn.com/layui/css/layui.css" media="all">
|
||||||
|
<link rel="stylesheet" href="css/index.min.css" media="all">
|
||||||
|
<link rel="stylesheet" href="css/comm.min.css" media="all">
|
||||||
|
<link rel="stylesheet" href="css/swiper-bundle.min.css" media="all">
|
||||||
|
<script type="text/javascript" src="js/swiper-bundle.min.js"></script>
|
||||||
|
<script type="text/javascript" src="js/socket.io.js"></script>
|
||||||
|
<script type="text/javascript" src="js/vue.min.js"></script>
|
||||||
|
<script type="text/javascript" src="js/axios.js"></script>
|
||||||
|
<script>
|
||||||
|
window.Bus = new Vue({});
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div class="verticalPannel">
|
||||||
|
<iframe frameborder="0" scrolling="no" src="./vertical.html" width="100%" height="30px"></iframe>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div id="fileApp" v-cloak>
|
||||||
|
<div class="tool" @click="clickLogs">
|
||||||
|
<svg t="1611756829585" class="icon" viewBox="0 0 1024 1024" >
|
||||||
|
<path d="M551.1 549.6m-306 0a306 306 0 1 0 612 0 306 306 0 1 0-612 0Z" fill="#25B195" p-id="4321"></path>
|
||||||
|
<path d="M512.7 871.2c-49.6 0-97.8-9.7-143.1-28.9-43.8-18.5-83.1-45-116.9-78.8-33.8-33.8-60.3-73.1-78.8-116.9-19.2-45.3-28.9-93.5-28.9-143.1s9.7-97.8 28.9-143.1c18.5-43.8 45-83.1 78.8-116.9 33.8-33.8 73.1-60.3 116.9-78.8 45.3-19.2 93.5-28.9 143.1-28.9s97.8 9.7 143.1 28.9c43.8 18.5 83.1 45 116.9 78.8 33.8 33.8 60.3 73.1 78.8 116.9 19.2 45.3 28.9 93.5 28.9 143.1s-9.7 97.8-28.9 143.1c-18.5 43.8-45 83.1-78.8 116.9-33.8 33.8-73.1 60.3-116.9 78.8-45.3 19.2-93.5 28.9-143.1 28.9z m0-694.5C332.5 176.7 186 323.3 186 503.5s146.6 326.7 326.7 326.7c180.2 0 326.7-146.6 326.7-326.7S692.9 176.7 512.7 176.7z" fill="#666767" p-id="4322"></path>
|
||||||
|
<path d="M512.7 629.8c9.6 0 17.2 2.9 22.7 8.7 6.4 6.4 9.6 14.3 9.6 23.6s-3.1 17-9.2 23.1c-6.4 6.4-14.1 9.6-23.1 9.6-9 0-16.7-3.2-23.1-9.6-6.1-5.8-9.2-13.5-9.2-23.1 0-9.3 3.2-17.2 9.6-23.6 6.1-5.8 13.7-8.7 22.7-8.7z m0-317.7c9.6 0 17 2.9 22.2 8.7 5.1 5.8 7.7 13.5 7.7 23.1 0 7.1-8.2 89.3-24.6 246.8h-10.6C491 433.3 482.8 351 482.8 343.9c0-9.6 2.6-17.4 7.7-23.1 5.2-5.8 12.6-8.7 22.2-8.7z" fill="#595857" p-id="4323"></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="text-align: center;font-size: 20px;color: beige;">注 : 仅用于学习用途, 请勿他用</div>
|
||||||
|
<div class="roomClass" >
|
||||||
|
<input type="text" name="title" maxlength="15" autocomplete="off" class="layui-input cusInput" v-model="roomId" placeholder="输入房间编号">
|
||||||
|
<div class="layui-btn-container center">
|
||||||
|
<button type="button" :class="[{'layui-btn-disabled':createDisabled}]" @click="createRoom()" :disabled="createDisabled"> 创建/加入房间 </button>
|
||||||
|
<button type="button" class="exitBtn" :class="[{'layui-btn-disabled':exsitDisabled}]" @click="exitRoom()" :disabled="exsitDisabled"> 退出房间 </button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="layui-col-sm6 layui-col-md3" id="self">
|
||||||
|
<input type="file" ref="self-file" v-model="fileName" v-show="false">
|
||||||
|
<button class="sendFileBtn" @click="clickChooseFile" v-show="uploadDisabled">
|
||||||
|
<svg viewBox="0 0 1024 1024" style="width: 30px;height: 30px;margin-left: -8px;margin-right: 10px;">
|
||||||
|
<path d="M840.65 540.62h-655.9c-12.18 0-22.05-9.87-22.05-22.05v-5.9c0-12.18 9.87-22.05 22.05-22.05h655.9c12.18 0 22.05 9.87 22.05 22.05v5.9c0 12.18-9.87 22.05-22.05 22.05z" fill="#565656" p-id="1269"></path>
|
||||||
|
<path d="M517.69 863.19h-5.9c-12.18 0-22.05-9.87-22.05-22.05v-655.9c0-12.18 9.87-22.05 22.05-22.05h5.9c12.18 0 22.05 9.87 22.05 22.05v655.9c0 12.17-9.87 22.05-22.05 22.05z" fill="#565656" p-id="1270"></path>
|
||||||
|
</svg>
|
||||||
|
<div style="font-size: 14px;word-break: keep-all;">选择文件</div>
|
||||||
|
</button>
|
||||||
|
<button class="sendFileBtn" style="background-color: #79b0e8;" @click="submitChooseFile" v-show="!uploadDisabled">
|
||||||
|
<svg viewBox="0 0 1024 1024" style="width: 30px;height: 25px;margin-left: -8px;margin-right: 10px;">
|
||||||
|
<path d="M341.333333 277.333333l115.2-115.2c8.533333-8.533333 17.066667-12.8 29.866667-17.066666v473.6c0 17.066667 12.8 29.866667 25.6 29.866666s25.6-12.8 25.6-29.866666V140.8c8.533333 4.266667 21.333333 8.533333 29.866667 17.066667L682.666667 277.333333c4.266667 4.266667 12.8 8.533333 21.333333 8.533334s12.8-4.266667 21.333333-8.533334c12.8-12.8 12.8-29.866667 0-38.4l-115.2-115.2C554.666667 68.266667 469.333333 68.266667 418.133333 119.466667L302.933333 234.666667c-12.8 12.8-12.8 29.866667 0 38.4s25.6 12.8 38.4 4.266666z" fill="#191A1A" p-id="2860"></path>
|
||||||
|
<path d="M738.133333 435.2h-72.533333c-17.066667 0-29.866667 12.8-29.866667 25.6s12.8 25.6 29.866667 25.6h72.533333c42.666667 0 76.8 34.133333 76.8 76.8V810.666667c0 42.666667-34.133333 76.8-76.8 76.8H285.866667c-42.666667 0-76.8-34.133333-76.8-76.8v-243.2c0-42.666667 34.133333-76.8 76.8-76.8h72.533333c17.066667 0 25.6-12.8 25.6-25.6s-12.8-25.6-25.6-25.6H285.866667c-72.533333 0-132.266667 59.733333-132.266667 132.266666V810.666667c0 72.533333 59.733333 132.266667 132.266667 132.266666h456.533333c72.533333 0 132.266667-59.733333 132.266667-132.266666v-243.2c0-72.533333-59.733333-132.266667-136.533334-132.266667z" fill="#191A1A" p-id="2861"></path>
|
||||||
|
</svg>
|
||||||
|
<div style="font-size: 14px;word-break: keep-all;">开始发送</div>
|
||||||
|
</button>
|
||||||
|
<div v-show="fileName" class="chooseFileName">{{fileName}}</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="maskBottom" @click="clickSendFile" :style="{top: numSendFile + '%'}" v-show="false">
|
||||||
|
<div class="sendFilePannel" v-show="sendFileList && sendFileList.length > 0">
|
||||||
|
<div class="sendFileHeader">发送的文件</div>
|
||||||
|
<ul class="sendFiles" :style="showSendFileList? 'overflow-y: scroll;':''">
|
||||||
|
<li class="sendFile" v-for="file in sendFileList">
|
||||||
|
<div class="sendFileType">
|
||||||
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"></path>
|
||||||
|
<polyline points="3.27 6.96 12 12.01 20.73 6.96"></polyline>
|
||||||
|
<line x1="12" y1="22.08" x2="12" y2="12"></line>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class="sendFileInfo" v-if="chooseFile">
|
||||||
|
<div class="sendFileName">{{file.name}}</div>
|
||||||
|
<div class="sendFileOther">{{getFileSizeStr(file.size)}} | {{file.toIdStr}}</div>
|
||||||
|
</div>
|
||||||
|
<div class="sendInfoBackgroud"></div>
|
||||||
|
<div></div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="noSendFile" v-show="!sendFileList || sendFileList.length === 0"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="maskBottom" @click="clickSendFile" :style="{top: numSendFile + '%'}">
|
||||||
|
<div class="layui-col-sm2" style="width: 100%;">
|
||||||
|
<div class="layui-card">
|
||||||
|
<div class="layui-card-header">发送的文件</div>
|
||||||
|
<div class="layui-card-body" style="overflow-y: scroll;height: 330px;">
|
||||||
|
<table class="layui-table">
|
||||||
|
<thead>
|
||||||
|
<tr><th>用户名ID</th><th>名称</th><th>格式</th><th>大小</th></tr>
|
||||||
|
</thead>
|
||||||
|
<tbody ref="files">
|
||||||
|
<tr v-for="file in sendFileList">
|
||||||
|
<td><span>自己</span></td>
|
||||||
|
<td><span>{{file.name}}</span></td>
|
||||||
|
<td><span>{{file.type}}</span></td>
|
||||||
|
<td><span>{{getFileSizeStr(file.size)}}</span></td>
|
||||||
|
</tr>
|
||||||
|
<div style="display: flex;padding:5px;">
|
||||||
|
<span style="word-break: keep-all;font-size: 12px;">进度:</span>
|
||||||
|
<progress ref="sendProgress" :value="offset" max="0" style="width: 100%;margin: auto;"></progress>
|
||||||
|
</div>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="maskBottom" @click="clickReceiveFile" :style="{top: numReceiveFile + '%'}">
|
||||||
|
<div class="layui-col-sm2" style="width: 100%;">
|
||||||
|
<div class="layui-card">
|
||||||
|
<div class="layui-card-header">接收的文件</div>
|
||||||
|
<div class="layui-card-body" style="overflow-y: scroll;height: 330px;">
|
||||||
|
<table class="layui-table">
|
||||||
|
<thead>
|
||||||
|
<tr><th>用户名ID</th><th>文件名称</th><th>文件格式</th><th>文件大小</th></tr>
|
||||||
|
</thead>
|
||||||
|
<tbody ref="files">
|
||||||
|
<div style="display: flex;padding:5px;">
|
||||||
|
<span style="word-break: keep-all;font-size: 12px;">进度:</span>
|
||||||
|
<progress ref="receiveProgress" :value="offset" max="0" style="width: 100%;margin: auto;"></progress>
|
||||||
|
</div>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="maskRight" @click="clickLogs" :style="{left: numLogs + '%'}">
|
||||||
|
<div class="layui-col-sm2" style="width: 100%;">
|
||||||
|
<div class="layui-card">
|
||||||
|
<div class="layui-card-header">执行日志</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-card" style="background-image: linear-gradient(to top, #D8D9DB 0%, #fff 80%, #FDFDFD 100%);">
|
||||||
|
<div :style="{height: logsHeight+'px',overflow: 'scroll'}">
|
||||||
|
<div class="layui-card-body logs" v-for="log in logs">
|
||||||
|
{{log}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- <div id="canvasPannel">
|
||||||
|
<iframe frameborder="0" scrolling="no" src="./room.html" width="100%" height="200px"></iframe>
|
||||||
|
</div> -->
|
||||||
|
|
||||||
|
<div class="userRoom swiper-container">
|
||||||
|
<div class="swiper-wrapper">
|
||||||
|
<div class="swiper-slide userBlocK" v-for="remote in remoteMap">
|
||||||
|
<svg style="margin-left: 5px;" viewBox="0 0 1024 1024" width="64" height="64">
|
||||||
|
<path d="M310.956564 662.25202zM307.071733 652.841638zM303.914275 643.972652c0.219038 0.669513 0.471139 1.326628 0.69431 1.992009-0.231437-0.661248-0.471139-1.322495-0.69431-1.992009zM539.239951 781.611372a3.0872 3.0872 0 0 0 0.962942-0.181843c-0.698443 0.086789-1.401019 0.136382-2.103594 0.219038l1.140652-0.037195zM315.841531 672.418704zM292.044878 564.370822a1501.734968 1501.734968 0 0 0-0.169445-17.436277c-0.02893 5.926433 0.02893 11.737148 0.169445 17.436277zM729.39414 534.32951c-0.169445 10.910588-0.376085 22.056745-0.376085 33.368215 0 21.507083-2.49621 41.373445-7.021624 59.661078 7.401842-28.264209 9.476507-58.218732 7.397709-93.029293z" fill="#002738" p-id="2725"></path>
|
||||||
|
<path d="M507.843081 8.435042c-280.451702 0-507.838272 227.266719-507.838272 507.722553 0 187.54226 101.608982 351.250672 252.745422 439.200756 0.421545-1.169582 0.818294-2.359828 1.25637-3.52941 16.915544-45.564103 44.76234-81.97819 79.184418-109.176136 22.445228-17.886752 48.200828-32.198633 76.609685-42.39838-58.995698-51.403747-60.739739-65.430465-40.489026-63.53351a193.757989 193.757989 0 0 1-47.122168-53.015538c0.396749 0.648849 0.739771 1.310097 1.144785 1.963079-193.113273-186.203234-50.234165-534.188995 184.508786-411.072929l0.23557 0.115718c161.344451-84.825688 283.667019 58.342716 280.381444 212.863916-0.938145 70.860962-28.115428 143.685003-89.785046 196.799729 0.442209-0.789365 0.830692-1.582862 1.268769-2.368093-13.46879 22.879172-31.000121 41.844584-51.180576 56.966493 10.183215 3.32277 5.996691 19.816769-28.22288 61.500174h-0.264499c27.788937 10.046833 53.019671 24.02809 75.084682 41.456101 34.029462 26.623488 61.73161 62.17382 78.911653 106.630332 144.858718-89.433758 241.413288-249.592096 241.413288-432.406435-0.004133-280.451702-227.38657-507.718421-507.842405-507.71842z" fill="#002738" p-id="2726"></path>
|
||||||
|
<path d="M538.103431 781.648567c0.702576-0.082656 1.405151-0.13225 2.103595-0.219038 10.551034-3.149192 88.218715-49.08938 108.560349-42.452106 20.180455-15.121909 37.711786-34.087322 51.180576-56.966494 9.943513-18.072728 17.167645-36.000807 22.052612-54.652126 4.529547-18.287633 7.021625-38.149862 7.021625-59.661078 0-11.311469 0.20664-22.46176 0.376085-33.368215-0.793497-13.369603-2.18625-27.445914-4.1204-42.423176-95.711479 5.856175-163.658818-27.883991-217.426526-82.002987-75.208666 51.312825-146.904453 80.949123-214.029366 82.002987-1.074528 19.498543-1.843228 37.761379-1.938282 55.028211 0.086789 5.740457 0.152914 11.555304 0.169445 17.436277 0.739771 29.082503 3.979885 55.276179 11.865264 79.605963 0.227304 0.669513 0.462873 1.326628 0.69431 1.992009 0.776966 2.310234 1.603526 4.595672 2.467281 6.872844 1.103457 2.851631 2.248242 5.661934 3.459152 8.422643 0.144648 0.326491 0.276897 0.661248 0.425678 0.987739 1.330761 2.98388 2.73178 5.901636 4.186525 8.773931 0.23557 0.462873 0.462873 0.934012 0.698443 1.392753 1.186113 2.297836 2.434218 4.53368 3.694722 6.757126 0.867888 1.512604 1.731643 3.029341 2.653256 4.533679a193.757989 193.757989 0 0 0 47.122168 53.015539c31.421667 2.93842 115.805145 44.21681 121.429884 43.716742 19.167919 2.52514 28.243545 1.789502 47.353604 1.206777zM695.356412 841.933698c35.033732 27.66082 62.062234 63.967454 78.804201 106.692324l0.10332-0.066125c-17.17591-44.45238-44.873925-80.002712-78.907521-106.626199zM333.191019 842.656937c-34.422078 27.197947-62.264741 63.612034-79.184418 109.176137 16.485733-43.753937 43.708476-80.907795 79.184418-109.176137z" fill="#FCE9EA" p-id="2727"></path>
|
||||||
|
<path d="M695.356412 841.933698c-22.065011-17.428011-47.295745-31.413401-75.084682-41.456101l-210.363573-0.119852-0.107453-0.095054c-28.408856 10.199747-54.164456 24.511628-76.609685 42.398379-35.471809 28.272474-62.698685 65.422199-79.184418 109.176136-0.438077 1.165449-0.834825 2.355695-1.25637 3.52941 74.964831 43.629953 162.104886 68.633384 255.09285 68.633384 97.670425 0 188.88542-27.590562 266.317532-75.365712-16.741966-42.737269-43.770468-79.03977-78.804201-106.70059z" fill="#B0C11D" p-id="2728"></path><path d="M620.27173 800.473464h0.264499c34.219571-41.683405 38.406096-58.177404 28.222881-61.500174-20.341634-6.633141-98.009315 39.307046-108.56035 42.452106a3.124396 3.124396 0 0 1-0.962942 0.181843l-1.136519 0.037195c-19.11006 0.586857-28.181553 1.318363-47.36187-1.206777-5.624739 0.500069-90.008217-40.778322-121.429884-43.716742-20.250712-1.896954-18.510804 12.133896 40.489026 63.53351l0.107453 0.095055 210.367706 0.123984z" fill="#FF5947" p-id="2729"></path>
|
||||||
|
<path d="M315.841531 672.418704c-0.239702-0.458741-0.462873-0.92988-0.698442-1.392753a180.789268 180.789268 0 0 1-4.186525-8.773931l-0.425679-0.987739a184.876605 184.876605 0 0 1-5.926432-15.295487c-0.223171-0.665381-0.475272-1.322495-0.694311-1.992009-7.885379-24.329784-11.125493-50.523461-11.865264-79.605963a563.192974 563.192974 0 0 1-0.169445-17.436277c0.095054-17.266832 0.859622-35.529668 1.938283-55.028211 67.124912-1.053864 138.8207-30.694294 214.029365-82.002987v-135.30782c-234.742951-123.116065-377.622059 224.869696-184.508786 411.072929-0.409147-0.648849-0.748037-1.310097-1.144785-1.963079-0.921614-1.500206-1.785369-3.016943-2.653257-4.53368-1.281168-2.235844-2.521007-4.488219-3.694722-6.752993zM698.675049 684.379023c61.669618-53.118858 88.846901-125.938767 89.785046-196.799729-1.49194 69.976543-28.727082 142.279852-89.785046 196.799729z" fill="#FECF77" p-id="2730"></path>
|
||||||
|
<path d="M507.843081 409.903347c53.77184 54.123128 121.715047 87.863295 217.426526 82.002987 1.93415 14.977262 3.326903 29.053573 4.1204 42.423176 2.078798 34.810561 0.004133 64.765084-7.397709 93.029293-4.884968 18.651319-12.109099 36.579399-22.052613 54.652126-0.433944 0.785232-0.822427 1.578729-1.268769 2.368094 61.053831-54.515744 88.293106-126.823186 89.785046-196.799729 3.285575-154.525334-119.036993-297.689604-280.381444-212.863916l-0.23557-0.115719v135.303688z" fill="#FF9F31" p-id="2731"></path>
|
||||||
|
</svg>
|
||||||
|
<div class="layui-badge layui-bg-blue layuiadmin-badge remoteId">{{remote.id}}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="swiper-scrollbar"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<menu class="menu">
|
||||||
|
<button ref="btnHome" @click="clickHome" class="menu__item" :class="currentMenu === 1 ? 'active' : ''" style="--bgColorBody: #ffb457; --bgColorItem: #ff8c00;">
|
||||||
|
<svg class="icon" viewBox="0 0 24 24">
|
||||||
|
<path d="M3.8,6.6h16.4"></path>
|
||||||
|
<path d="M20.2,12.1H3.8"></path>
|
||||||
|
<path d="M3.8,17.5h16.4"></path>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<button ref="btnRoom" @click="clickRoom" class="menu__item" :class="currentMenu === 2 ? 'active' : ''" style="--bgColorBody: rgb(112, 161, 255); --bgColorItem: rgb(112, 161, 255)">
|
||||||
|
<svg class="icon" viewBox="0 0 24 24">
|
||||||
|
<path d="M6.7,4.8h10.7c0.3,0,0.6,0.2,0.7,0.5l2.8,7.3c0,0.1,0,0.2,0,0.3v5.6c0,0.4-0.4,0.8-0.8,0.8H3.8
|
||||||
|
C3.4,19.3,3,19,3,18.5v-5.6c0-0.1,0-0.2,0.1-0.3L6,5.3C6.1,5,6.4,4.8,6.7,4.8z"></path>
|
||||||
|
<path d="M3.4,12.9H8l1.6,2.8h4.9l1.5-2.8h4.6"></path>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<!-- <button ref="btnFile" @click="clickFile" class="menu__item" :class="currentMenu === 3 ? 'active' : ''" style="--bgColorBody: #ffe797; --bgColorItem: #e0b115;">
|
||||||
|
<svg class="icon" viewBox="0 0 24 24">
|
||||||
|
<path d="M5.1,3.9h13.9c0.6,0,1.2,0.5,1.2,1.2v13.9c0,0.6-0.5,1.2-1.2,1.2H5.1c-0.6,0-1.2-0.5-1.2-1.2V5.1
|
||||||
|
C3.9,4.4,4.4,3.9,5.1,3.9z"></path>
|
||||||
|
<path d="M4.2,9.3h15.6"></path>
|
||||||
|
<path d="M9.1,9.5v10.3"></path>
|
||||||
|
</svg>
|
||||||
|
</button> -->
|
||||||
|
<div class="menu__border" style="transform: translate3d(108px, 0px, 0px);"></div>
|
||||||
|
</menu>
|
||||||
|
|
||||||
|
<div class="svg-container">
|
||||||
|
<svg viewBox="0 0 202.9 45.5">
|
||||||
|
<clippath id="menu" clipPathUnits="objectBoundingBox" transform="scale(0.0049285362247413 0.021978021978022)">
|
||||||
|
<path d="M6.7,45.5c5.7,0.1,14.1-0.4,23.3-4c5.7-2.3,9.9-5,18.1-10.5c10.7-7.1,11.8-9.2,20.6-14.3c5-2.9,9.2-5.2,15.2-7
|
||||||
|
c7.1-2.1,13.3-2.3,17.6-2.1c4.2-0.2,10.5,0.1,17.6,2.1c6.1,1.8,10.2,4.1,15.2,7c8.8,5,9.9,7.1,20.6,14.3c8.3,5.5,12.4,8.2,18.1,10.5
|
||||||
|
c9.2,3.6,17.6,4.2,23.3,4H6.7z"></path>
|
||||||
|
</clippath>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<script type="text/javascript" src="js/index.min.js"></script>
|
||||||
|
<script src="https://www.layuicdn.com/layui/layui.js"></script>
|
||||||
|
<script>
|
||||||
|
setTimeout(()=>{
|
||||||
|
let clientWidth = document.body.clientWidth;
|
||||||
|
let slidesPerView = parseInt((clientWidth / 100))-1;
|
||||||
|
var mySwiper = new Swiper ('.swiper-container', {
|
||||||
|
direction: 'horizontal', // 垂直切换选项
|
||||||
|
loop: false, // 循环模式选项
|
||||||
|
slidesPerView: slidesPerView,
|
||||||
|
// effect : "coverflow", //切换样式
|
||||||
|
observer:true,
|
||||||
|
scrollbar: {
|
||||||
|
el: '.swiper-scrollbar',
|
||||||
|
}
|
||||||
|
})
|
||||||
|
mySwiper.scrollbar.$dragEl.css('background','#ff6600');
|
||||||
|
mySwiper.scrollbar.$el.css('height','4px');
|
||||||
|
window.swiper = mySwiper;
|
||||||
|
},1000)
|
||||||
|
|
||||||
|
layui.config({}).extend({}).use(['form','layer'],function(){
|
||||||
|
layer.open({
|
||||||
|
type: 1
|
||||||
|
,title: false
|
||||||
|
,closeBtn: false
|
||||||
|
,area: '300px;'
|
||||||
|
,shade: 0.8
|
||||||
|
,id: 'layui-info-msg'
|
||||||
|
,btn: ['知道了']
|
||||||
|
,btnAlign: 'c'
|
||||||
|
,moveType: 1 //
|
||||||
|
,content: `
|
||||||
|
<div style="padding: 30px; line-height: 22px; background-color: #393D49; color: #fff; font-weight: 300;">
|
||||||
|
<div style="text-align: center;line-height: 40px;">1. 双方输入相同房间号加入</div>
|
||||||
|
<div style="text-align: center;line-height: 40px;">2. 双方选择需要的文件发送</div>
|
||||||
|
<div style="text-align: center;line-height: 40px;">3. 右下角是你接收到的文件</div>
|
||||||
|
<div style="text-align: center;line-height: 40px;">4. 左下角是你发送过的文件</div>
|
||||||
|
<div style="text-align: center;line-height: 26px;word-break: keep-all;color: #e87171;">5. 服务器配置带宽不高(有需要可 <a style="color: wheat;" target="_blank" href="https://new.tsmblogs.cn">联系我</a> 提高带宽),传输只能保持在500kb/s ~ 600kb/s</div>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
,success: function(layero){
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
5551
res/js/adapter.js
Normal file
5551
res/js/adapter.js
Normal file
File diff suppressed because it is too large
Load Diff
637
res/js/axios.js
Normal file
637
res/js/axios.js
Normal file
@@ -0,0 +1,637 @@
|
|||||||
|
/* axios v0.18.0 | (c) 2018 by Matt Zabriskie */ ! function(e, t) {
|
||||||
|
"object" == typeof exports && "object" == typeof module ? module.exports = t() :
|
||||||
|
"function" == typeof define && define.amd ? define([], t) : "object" ==
|
||||||
|
typeof exports ? exports.axios = t() : e.axios = t()
|
||||||
|
}(this, function() {
|
||||||
|
return function(e) {
|
||||||
|
function t(r) {
|
||||||
|
if (n[r]) return n[r].exports;
|
||||||
|
var o = n[r] = {
|
||||||
|
exports: {},
|
||||||
|
id: r,
|
||||||
|
loaded: !1
|
||||||
|
};
|
||||||
|
return e[r].call(o.exports, o, o.exports, t), o.loaded = !0, o.exports
|
||||||
|
}
|
||||||
|
var n = {};
|
||||||
|
return t.m = e, t.c = n, t.p = "", t(0)
|
||||||
|
}([function(e, t, n) {
|
||||||
|
e.exports = n(1)
|
||||||
|
}, function(e, t, n) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
function r(e) {
|
||||||
|
var t = new s(e),
|
||||||
|
n = i(s.prototype.request, t);
|
||||||
|
return o.extend(n, s.prototype, t), o.extend(n, t), n
|
||||||
|
}
|
||||||
|
var o = n(2),
|
||||||
|
i = n(3),
|
||||||
|
s = n(5),
|
||||||
|
u = n(6),
|
||||||
|
a = r(u);
|
||||||
|
a.Axios = s, a.create = function(e) {
|
||||||
|
return r(o.merge(u, e))
|
||||||
|
}, a.Cancel = n(23), a.CancelToken = n(24), a.isCancel = n(20), a
|
||||||
|
.all = function(e) {
|
||||||
|
return Promise.all(e)
|
||||||
|
}, a.spread = n(25), e.exports = a, e.exports.default = a
|
||||||
|
}, function(e, t, n) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
function r(e) {
|
||||||
|
return "[object Array]" === R.call(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
function o(e) {
|
||||||
|
return "[object ArrayBuffer]" === R.call(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
function i(e) {
|
||||||
|
return "undefined" != typeof FormData && e instanceof FormData
|
||||||
|
}
|
||||||
|
|
||||||
|
function s(e) {
|
||||||
|
var t;
|
||||||
|
return t = "undefined" != typeof ArrayBuffer && ArrayBuffer.isView ?
|
||||||
|
ArrayBuffer.isView(e) : e && e.buffer && e.buffer instanceof ArrayBuffer
|
||||||
|
}
|
||||||
|
|
||||||
|
function u(e) {
|
||||||
|
return "string" == typeof e
|
||||||
|
}
|
||||||
|
|
||||||
|
function a(e) {
|
||||||
|
return "number" == typeof e
|
||||||
|
}
|
||||||
|
|
||||||
|
function c(e) {
|
||||||
|
return "undefined" == typeof e
|
||||||
|
}
|
||||||
|
|
||||||
|
function f(e) {
|
||||||
|
return null !== e && "object" == typeof e
|
||||||
|
}
|
||||||
|
|
||||||
|
function p(e) {
|
||||||
|
return "[object Date]" === R.call(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
function d(e) {
|
||||||
|
return "[object File]" === R.call(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
function l(e) {
|
||||||
|
return "[object Blob]" === R.call(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
function h(e) {
|
||||||
|
return "[object Function]" === R.call(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
function m(e) {
|
||||||
|
return f(e) && h(e.pipe)
|
||||||
|
}
|
||||||
|
|
||||||
|
function y(e) {
|
||||||
|
return "undefined" != typeof URLSearchParams && e instanceof URLSearchParams
|
||||||
|
}
|
||||||
|
|
||||||
|
function w(e) {
|
||||||
|
return e.replace(/^\s*/, "").replace(/\s*$/, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
function g() {
|
||||||
|
return ("undefined" == typeof navigator || "ReactNative" !==
|
||||||
|
navigator.product) && ("undefined" != typeof window &&
|
||||||
|
"undefined" != typeof document)
|
||||||
|
}
|
||||||
|
|
||||||
|
function v(e, t) {
|
||||||
|
if (null !== e && "undefined" != typeof e)
|
||||||
|
if ("object" != typeof e && (e = [e]), r(e))
|
||||||
|
for (var n = 0, o = e.length; n < o; n++) t.call(null, e[n],
|
||||||
|
n, e);
|
||||||
|
else
|
||||||
|
for (var i in e) Object.prototype.hasOwnProperty.call(e, i) &&
|
||||||
|
t.call(null, e[i], i, e)
|
||||||
|
}
|
||||||
|
|
||||||
|
function x() {
|
||||||
|
function e(e, n) {
|
||||||
|
"object" == typeof t[n] && "object" == typeof e ? t[n] = x(t[n],
|
||||||
|
e) : t[n] = e
|
||||||
|
}
|
||||||
|
for (var t = {}, n = 0, r = arguments.length; n < r; n++) v(
|
||||||
|
arguments[n], e);
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
function b(e, t, n) {
|
||||||
|
return v(t, function(t, r) {
|
||||||
|
n && "function" == typeof t ? e[r] = E(t, n) : e[r] = t
|
||||||
|
}), e
|
||||||
|
}
|
||||||
|
var E = n(3),
|
||||||
|
C = n(4),
|
||||||
|
R = Object.prototype.toString;
|
||||||
|
e.exports = {
|
||||||
|
isArray: r,
|
||||||
|
isArrayBuffer: o,
|
||||||
|
isBuffer: C,
|
||||||
|
isFormData: i,
|
||||||
|
isArrayBufferView: s,
|
||||||
|
isString: u,
|
||||||
|
isNumber: a,
|
||||||
|
isObject: f,
|
||||||
|
isUndefined: c,
|
||||||
|
isDate: p,
|
||||||
|
isFile: d,
|
||||||
|
isBlob: l,
|
||||||
|
isFunction: h,
|
||||||
|
isStream: m,
|
||||||
|
isURLSearchParams: y,
|
||||||
|
isStandardBrowserEnv: g,
|
||||||
|
forEach: v,
|
||||||
|
merge: x,
|
||||||
|
extend: b,
|
||||||
|
trim: w
|
||||||
|
}
|
||||||
|
}, function(e, t) {
|
||||||
|
"use strict";
|
||||||
|
e.exports = function(e, t) {
|
||||||
|
return function() {
|
||||||
|
for (var n = new Array(arguments.length), r = 0; r < n.length; r++)
|
||||||
|
n[r] = arguments[r];
|
||||||
|
return e.apply(t, n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, function(e, t) {
|
||||||
|
function n(e) {
|
||||||
|
return !!e.constructor && "function" == typeof e.constructor.isBuffer &&
|
||||||
|
e.constructor.isBuffer(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
function r(e) {
|
||||||
|
return "function" == typeof e.readFloatLE && "function" == typeof e
|
||||||
|
.slice && n(e.slice(0, 0))
|
||||||
|
}
|
||||||
|
/*!
|
||||||
|
* Determine if an object is a Buffer
|
||||||
|
*
|
||||||
|
* @author Feross Aboukhadijeh <https://feross.org>
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
e.exports = function(e) {
|
||||||
|
return null != e && (n(e) || r(e) || !!e._isBuffer)
|
||||||
|
}
|
||||||
|
}, function(e, t, n) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
function r(e) {
|
||||||
|
this.defaults = e, this.interceptors = {
|
||||||
|
request: new s,
|
||||||
|
response: new s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var o = n(6),
|
||||||
|
i = n(2),
|
||||||
|
s = n(17),
|
||||||
|
u = n(18);
|
||||||
|
r.prototype.request = function(e) {
|
||||||
|
"string" == typeof e && (e = i.merge({
|
||||||
|
url: arguments[0]
|
||||||
|
}, arguments[1])), e = i.merge(o, {
|
||||||
|
method: "get"
|
||||||
|
}, this.defaults, e), e.method = e.method.toLowerCase();
|
||||||
|
var t = [u, void 0],
|
||||||
|
n = Promise.resolve(e);
|
||||||
|
for (this.interceptors.request.forEach(function(e) {
|
||||||
|
t.unshift(e.fulfilled, e.rejected)
|
||||||
|
}), this.interceptors.response.forEach(function(e) {
|
||||||
|
t.push(e.fulfilled, e.rejected)
|
||||||
|
}); t.length;) n = n.then(t.shift(), t.shift());
|
||||||
|
return n
|
||||||
|
}, i.forEach(["delete", "get", "head", "options"], function(e) {
|
||||||
|
r.prototype[e] = function(t, n) {
|
||||||
|
return this.request(i.merge(n || {}, {
|
||||||
|
method: e,
|
||||||
|
url: t
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}), i.forEach(["post", "put", "patch"], function(e) {
|
||||||
|
r.prototype[e] = function(t, n, r) {
|
||||||
|
return this.request(i.merge(r || {}, {
|
||||||
|
method: e,
|
||||||
|
url: t,
|
||||||
|
data: n
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}), e.exports = r
|
||||||
|
}, function(e, t, n) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
function r(e, t) {
|
||||||
|
!i.isUndefined(e) && i.isUndefined(e["Content-Type"]) && (e[
|
||||||
|
"Content-Type"] = t)
|
||||||
|
}
|
||||||
|
|
||||||
|
function o() {
|
||||||
|
var e;
|
||||||
|
return "undefined" != typeof XMLHttpRequest ? e = n(8) :
|
||||||
|
"undefined" != typeof process && (e = n(8)), e
|
||||||
|
}
|
||||||
|
var i = n(2),
|
||||||
|
s = n(7),
|
||||||
|
u = {
|
||||||
|
"Content-Type": "application/x-www-form-urlencoded"
|
||||||
|
},
|
||||||
|
a = {
|
||||||
|
adapter: o(),
|
||||||
|
transformRequest: [function(e, t) {
|
||||||
|
return s(t, "Content-Type"), i.isFormData(e) || i.isArrayBuffer(
|
||||||
|
e) || i.isBuffer(e) || i.isStream(e) || i.isFile(e) ||
|
||||||
|
i.isBlob(e) ? e : i.isArrayBufferView(e) ? e.buffer : i
|
||||||
|
.isURLSearchParams(e) ? (r(t,
|
||||||
|
"application/x-www-form-urlencoded;charset=utf-8"),
|
||||||
|
e.toString()) : i.isObject(e) ? (r(t,
|
||||||
|
"application/json;charset=utf-8"), JSON.stringify(e)) :
|
||||||
|
e
|
||||||
|
}],
|
||||||
|
transformResponse: [function(e) {
|
||||||
|
if ("string" == typeof e) try {
|
||||||
|
e = JSON.parse(e)
|
||||||
|
} catch (e) {}
|
||||||
|
return e
|
||||||
|
}],
|
||||||
|
timeout: 0,
|
||||||
|
xsrfCookieName: "XSRF-TOKEN",
|
||||||
|
xsrfHeaderName: "X-XSRF-TOKEN",
|
||||||
|
maxContentLength: -1,
|
||||||
|
validateStatus: function(e) {
|
||||||
|
return e >= 200 && e < 300
|
||||||
|
}
|
||||||
|
};
|
||||||
|
a.headers = {
|
||||||
|
common: {
|
||||||
|
Accept: "application/json, text/plain, */*"
|
||||||
|
}
|
||||||
|
}, i.forEach(["delete", "get", "head"], function(e) {
|
||||||
|
a.headers[e] = {}
|
||||||
|
}), i.forEach(["post", "put", "patch"], function(e) {
|
||||||
|
a.headers[e] = i.merge(u)
|
||||||
|
}), e.exports = a
|
||||||
|
}, function(e, t, n) {
|
||||||
|
"use strict";
|
||||||
|
var r = n(2);
|
||||||
|
e.exports = function(e, t) {
|
||||||
|
r.forEach(e, function(n, r) {
|
||||||
|
r !== t && r.toUpperCase() === t.toUpperCase() && (e[t] =
|
||||||
|
n, delete e[r])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, function(e, t, n) {
|
||||||
|
"use strict";
|
||||||
|
var r = n(2),
|
||||||
|
o = n(9),
|
||||||
|
i = n(12),
|
||||||
|
s = n(13),
|
||||||
|
u = n(14),
|
||||||
|
a = n(10),
|
||||||
|
c = "undefined" != typeof window && window.btoa && window.btoa.bind(
|
||||||
|
window) || n(15);
|
||||||
|
e.exports = function(e) {
|
||||||
|
return new Promise(function(t, f) {
|
||||||
|
var p = e.data,
|
||||||
|
d = e.headers;
|
||||||
|
r.isFormData(p) && delete d["Content-Type"];
|
||||||
|
var l = new XMLHttpRequest,
|
||||||
|
h = "onreadystatechange",
|
||||||
|
m = !1;
|
||||||
|
if ("undefined" == typeof window || !window.XDomainRequest ||
|
||||||
|
"withCredentials" in l || u(e.url) || (l = new window.XDomainRequest,
|
||||||
|
h = "onload", m = !0, l.onprogress = function() {}, l
|
||||||
|
.ontimeout = function() {}), e.auth) {
|
||||||
|
var y = e.auth.username || "",
|
||||||
|
w = e.auth.password || "";
|
||||||
|
d.Authorization = "Basic " + c(y + ":" + w)
|
||||||
|
}
|
||||||
|
if (l.open(e.method.toUpperCase(), i(e.url, e.params, e.paramsSerializer), !
|
||||||
|
0), l.timeout = e.timeout, l[h] = function() {
|
||||||
|
if (l && (4 === l.readyState || m) && (0 !== l.status ||
|
||||||
|
l.responseURL && 0 === l.responseURL.indexOf(
|
||||||
|
"file:"))) {
|
||||||
|
var n = "getAllResponseHeaders" in l ? s(l.getAllResponseHeaders()) :
|
||||||
|
null,
|
||||||
|
r = e.responseType && "text" !== e.responseType ?
|
||||||
|
l.response : l.responseText,
|
||||||
|
i = {
|
||||||
|
data: r,
|
||||||
|
status: 1223 === l.status ? 204 : l.status,
|
||||||
|
statusText: 1223 === l.status ? "No Content" : l
|
||||||
|
.statusText,
|
||||||
|
headers: n,
|
||||||
|
config: e,
|
||||||
|
request: l
|
||||||
|
};
|
||||||
|
o(t, f, i), l = null
|
||||||
|
}
|
||||||
|
}, l.onerror = function() {
|
||||||
|
f(a("Network Error", e, null, l)), l = null
|
||||||
|
}, l.ontimeout = function() {
|
||||||
|
f(a("timeout of " + e.timeout + "ms exceeded", e,
|
||||||
|
"ECONNABORTED", l)), l = null
|
||||||
|
}, r.isStandardBrowserEnv()) {
|
||||||
|
var g = n(16),
|
||||||
|
v = (e.withCredentials || u(e.url)) && e.xsrfCookieName ?
|
||||||
|
g.read(e.xsrfCookieName) : void 0;
|
||||||
|
v && (d[e.xsrfHeaderName] = v)
|
||||||
|
}
|
||||||
|
if ("setRequestHeader" in l && r.forEach(d, function(e, t) {
|
||||||
|
"undefined" == typeof p && "content-type" === t.toLowerCase() ?
|
||||||
|
delete d[t] : l.setRequestHeader(t, e)
|
||||||
|
}), e.withCredentials && (l.withCredentials = !0), e.responseType)
|
||||||
|
try {
|
||||||
|
l.responseType = e.responseType
|
||||||
|
} catch (t) {
|
||||||
|
if ("json" !== e.responseType) throw t
|
||||||
|
}
|
||||||
|
"function" == typeof e.onDownloadProgress && l.addEventListener(
|
||||||
|
"progress", e.onDownloadProgress), "function" ==
|
||||||
|
typeof e.onUploadProgress && l.upload && l.upload.addEventListener(
|
||||||
|
"progress", e.onUploadProgress), e.cancelToken && e.cancelToken
|
||||||
|
.promise.then(function(e) {
|
||||||
|
l && (l.abort(), f(e), l = null)
|
||||||
|
}), void 0 === p && (p = null), l.send(p)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, function(e, t, n) {
|
||||||
|
"use strict";
|
||||||
|
var r = n(10);
|
||||||
|
e.exports = function(e, t, n) {
|
||||||
|
var o = n.config.validateStatus;
|
||||||
|
n.status && o && !o(n.status) ? t(r(
|
||||||
|
"Request failed with status code " + n.status, n.config,
|
||||||
|
null, n.request, n)) : e(n)
|
||||||
|
}
|
||||||
|
}, function(e, t, n) {
|
||||||
|
"use strict";
|
||||||
|
var r = n(11);
|
||||||
|
e.exports = function(e, t, n, o, i) {
|
||||||
|
var s = new Error(e);
|
||||||
|
return r(s, t, n, o, i)
|
||||||
|
}
|
||||||
|
}, function(e, t) {
|
||||||
|
"use strict";
|
||||||
|
e.exports = function(e, t, n, r, o) {
|
||||||
|
return e.config = t, n && (e.code = n), e.request = r, e.response =
|
||||||
|
o, e
|
||||||
|
}
|
||||||
|
}, function(e, t, n) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
function r(e) {
|
||||||
|
return encodeURIComponent(e).replace(/%40/gi, "@").replace(
|
||||||
|
/%3A/gi, ":").replace(/%24/g, "$").replace(/%2C/gi, ",").replace(
|
||||||
|
/%20/g, "+").replace(/%5B/gi, "[").replace(/%5D/gi, "]")
|
||||||
|
}
|
||||||
|
var o = n(2);
|
||||||
|
e.exports = function(e, t, n) {
|
||||||
|
if (!t) return e;
|
||||||
|
var i;
|
||||||
|
if (n) i = n(t);
|
||||||
|
else if (o.isURLSearchParams(t)) i = t.toString();
|
||||||
|
else {
|
||||||
|
var s = [];
|
||||||
|
o.forEach(t, function(e, t) {
|
||||||
|
null !== e && "undefined" != typeof e && (o.isArray(e) ?
|
||||||
|
t += "[]" : e = [e], o.forEach(e, function(e) {
|
||||||
|
o.isDate(e) ? e = e.toISOString() : o.isObject(
|
||||||
|
e) && (e = JSON.stringify(e)), s.push(r(t) +
|
||||||
|
"=" + r(e))
|
||||||
|
}))
|
||||||
|
}), i = s.join("&")
|
||||||
|
}
|
||||||
|
return i && (e += (e.indexOf("?") === -1 ? "?" : "&") + i), e
|
||||||
|
}
|
||||||
|
}, function(e, t, n) {
|
||||||
|
"use strict";
|
||||||
|
var r = n(2),
|
||||||
|
o = ["age", "authorization", "content-length", "content-type",
|
||||||
|
"etag", "expires", "from", "host", "if-modified-since",
|
||||||
|
"if-unmodified-since", "last-modified", "location",
|
||||||
|
"max-forwards", "proxy-authorization", "referer", "retry-after",
|
||||||
|
"user-agent"
|
||||||
|
];
|
||||||
|
e.exports = function(e) {
|
||||||
|
var t, n, i, s = {};
|
||||||
|
return e ? (r.forEach(e.split("\n"), function(e) {
|
||||||
|
if (i = e.indexOf(":"), t = r.trim(e.substr(0, i)).toLowerCase(),
|
||||||
|
n = r.trim(e.substr(i + 1)), t) {
|
||||||
|
if (s[t] && o.indexOf(t) >= 0) return;
|
||||||
|
"set-cookie" === t ? s[t] = (s[t] ? s[t] : []).concat(
|
||||||
|
[n]) : s[t] = s[t] ? s[t] + ", " + n : n
|
||||||
|
}
|
||||||
|
}), s) : s
|
||||||
|
}
|
||||||
|
}, function(e, t, n) {
|
||||||
|
"use strict";
|
||||||
|
var r = n(2);
|
||||||
|
e.exports = r.isStandardBrowserEnv() ? function() {
|
||||||
|
function e(e) {
|
||||||
|
var t = e;
|
||||||
|
return n && (o.setAttribute("href", t), t = o.href), o.setAttribute(
|
||||||
|
"href", t), {
|
||||||
|
href: o.href,
|
||||||
|
protocol: o.protocol ? o.protocol.replace(/:$/, "") : "",
|
||||||
|
host: o.host,
|
||||||
|
search: o.search ? o.search.replace(/^\?/, "") : "",
|
||||||
|
hash: o.hash ? o.hash.replace(/^#/, "") : "",
|
||||||
|
hostname: o.hostname,
|
||||||
|
port: o.port,
|
||||||
|
pathname: "/" === o.pathname.charAt(0) ? o.pathname : "/" +
|
||||||
|
o.pathname
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var t, n = /(msie|trident)/i.test(navigator.userAgent),
|
||||||
|
o = document.createElement("a");
|
||||||
|
return t = e(window.location.href),
|
||||||
|
function(n) {
|
||||||
|
var o = r.isString(n) ? e(n) : n;
|
||||||
|
return o.protocol === t.protocol && o.host === t.host
|
||||||
|
}
|
||||||
|
}() : function() {
|
||||||
|
return function() {
|
||||||
|
return !0
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}, function(e, t) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
function n() {
|
||||||
|
this.message = "String contains an invalid character"
|
||||||
|
}
|
||||||
|
|
||||||
|
function r(e) {
|
||||||
|
for (var t, r, i = String(e), s = "", u = 0, a = o; i.charAt(0 |
|
||||||
|
u) || (a = "=", u % 1); s += a.charAt(63 & t >> 8 - u % 1 * 8)) {
|
||||||
|
if (r = i.charCodeAt(u += .75), r > 255) throw new n;
|
||||||
|
t = t << 8 | r
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
var o =
|
||||||
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
|
||||||
|
n.prototype = new Error, n.prototype.code = 5, n.prototype.name =
|
||||||
|
"InvalidCharacterError", e.exports = r
|
||||||
|
}, function(e, t, n) {
|
||||||
|
"use strict";
|
||||||
|
var r = n(2);
|
||||||
|
e.exports = r.isStandardBrowserEnv() ? function() {
|
||||||
|
return {
|
||||||
|
write: function(e, t, n, o, i, s) {
|
||||||
|
var u = [];
|
||||||
|
u.push(e + "=" + encodeURIComponent(t)), r.isNumber(n) &&
|
||||||
|
u.push("expires=" + new Date(n).toGMTString()), r.isString(
|
||||||
|
o) && u.push("path=" + o), r.isString(i) && u.push(
|
||||||
|
"domain=" + i), s === !0 && u.push("secure"),
|
||||||
|
document.cookie = u.join("; ")
|
||||||
|
},
|
||||||
|
read: function(e) {
|
||||||
|
var t = document.cookie.match(new RegExp("(^|;\\s*)(" + e +
|
||||||
|
")=([^;]*)"));
|
||||||
|
return t ? decodeURIComponent(t[3]) : null
|
||||||
|
},
|
||||||
|
remove: function(e) {
|
||||||
|
this.write(e, "", Date.now() - 864e5)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}() : function() {
|
||||||
|
return {
|
||||||
|
write: function() {},
|
||||||
|
read: function() {
|
||||||
|
return null
|
||||||
|
},
|
||||||
|
remove: function() {}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}, function(e, t, n) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
function r() {
|
||||||
|
this.handlers = []
|
||||||
|
}
|
||||||
|
var o = n(2);
|
||||||
|
r.prototype.use = function(e, t) {
|
||||||
|
return this.handlers.push({
|
||||||
|
fulfilled: e,
|
||||||
|
rejected: t
|
||||||
|
}), this.handlers.length - 1
|
||||||
|
}, r.prototype.eject = function(e) {
|
||||||
|
this.handlers[e] && (this.handlers[e] = null)
|
||||||
|
}, r.prototype.forEach = function(e) {
|
||||||
|
o.forEach(this.handlers, function(t) {
|
||||||
|
null !== t && e(t)
|
||||||
|
})
|
||||||
|
}, e.exports = r
|
||||||
|
}, function(e, t, n) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
function r(e) {
|
||||||
|
e.cancelToken && e.cancelToken.throwIfRequested()
|
||||||
|
}
|
||||||
|
var o = n(2),
|
||||||
|
i = n(19),
|
||||||
|
s = n(20),
|
||||||
|
u = n(6),
|
||||||
|
a = n(21),
|
||||||
|
c = n(22);
|
||||||
|
e.exports = function(e) {
|
||||||
|
r(e), e.baseURL && !a(e.url) && (e.url = c(e.baseURL, e.url)),
|
||||||
|
e.headers = e.headers || {}, e.data = i(e.data, e.headers, e.transformRequest),
|
||||||
|
e.headers = o.merge(e.headers.common || {}, e.headers[e.method] || {},
|
||||||
|
e.headers || {}), o.forEach(["delete", "get", "head",
|
||||||
|
"post", "put", "patch", "common"
|
||||||
|
], function(t) {
|
||||||
|
delete e.headers[t]
|
||||||
|
});
|
||||||
|
var t = e.adapter || u.adapter;
|
||||||
|
return t(e).then(function(t) {
|
||||||
|
return r(e), t.data = i(t.data, t.headers, e.transformResponse),
|
||||||
|
t
|
||||||
|
}, function(t) {
|
||||||
|
return s(t) || (r(e), t && t.response && (t.response.data =
|
||||||
|
i(t.response.data, t.response.headers, e.transformResponse)
|
||||||
|
)), Promise.reject(t)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, function(e, t, n) {
|
||||||
|
"use strict";
|
||||||
|
var r = n(2);
|
||||||
|
e.exports = function(e, t, n) {
|
||||||
|
return r.forEach(n, function(n) {
|
||||||
|
e = n(e, t)
|
||||||
|
}), e
|
||||||
|
}
|
||||||
|
}, function(e, t) {
|
||||||
|
"use strict";
|
||||||
|
e.exports = function(e) {
|
||||||
|
return !(!e || !e.__CANCEL__)
|
||||||
|
}
|
||||||
|
}, function(e, t) {
|
||||||
|
"use strict";
|
||||||
|
e.exports = function(e) {
|
||||||
|
return /^([a-z][a-z\d\+\-\.]*:)?\/\//i.test(e)
|
||||||
|
}
|
||||||
|
}, function(e, t) {
|
||||||
|
"use strict";
|
||||||
|
e.exports = function(e, t) {
|
||||||
|
return t ? e.replace(/\/+$/, "") + "/" + t.replace(/^\/+/, "") :
|
||||||
|
e
|
||||||
|
}
|
||||||
|
}, function(e, t) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
function n(e) {
|
||||||
|
this.message = e
|
||||||
|
}
|
||||||
|
n.prototype.toString = function() {
|
||||||
|
return "Cancel" + (this.message ? ": " + this.message : "")
|
||||||
|
}, n.prototype.__CANCEL__ = !0, e.exports = n
|
||||||
|
}, function(e, t, n) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
function r(e) {
|
||||||
|
if ("function" != typeof e) throw new TypeError(
|
||||||
|
"executor must be a function.");
|
||||||
|
var t;
|
||||||
|
this.promise = new Promise(function(e) {
|
||||||
|
t = e
|
||||||
|
});
|
||||||
|
var n = this;
|
||||||
|
e(function(e) {
|
||||||
|
n.reason || (n.reason = new o(e), t(n.reason))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
var o = n(23);
|
||||||
|
r.prototype.throwIfRequested = function() {
|
||||||
|
if (this.reason) throw this.reason
|
||||||
|
}, r.source = function() {
|
||||||
|
var e, t = new r(function(t) {
|
||||||
|
e = t
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
token: t,
|
||||||
|
cancel: e
|
||||||
|
}
|
||||||
|
}, e.exports = r
|
||||||
|
}, function(e, t) {
|
||||||
|
"use strict";
|
||||||
|
e.exports = function(e) {
|
||||||
|
return function(t) {
|
||||||
|
return e.apply(null, t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}])
|
||||||
|
});
|
||||||
|
//# sourceMappingURL=axios.min.map
|
862
res/js/index.js
Normal file
862
res/js/index.js
Normal file
@@ -0,0 +1,862 @@
|
|||||||
|
// file.js
|
||||||
|
var file = null;
|
||||||
|
axios.get("/api/comm/initData",{}).then((initData)=>{
|
||||||
|
let resData = initData.data;
|
||||||
|
file = new Vue({
|
||||||
|
el : '#fileApp',
|
||||||
|
data : function () {
|
||||||
|
let socket = null;
|
||||||
|
if (io){
|
||||||
|
socket = io(resData.wsHost);
|
||||||
|
}
|
||||||
|
return{
|
||||||
|
socket : socket,
|
||||||
|
config : resData.rtcConfig,
|
||||||
|
options : resData.options,
|
||||||
|
isJoined : false,
|
||||||
|
showReceiveFile : false,
|
||||||
|
showSendFile : false,
|
||||||
|
showLogs : false,
|
||||||
|
numSendFile: 150,
|
||||||
|
numReceiveFile : 150,
|
||||||
|
numLogs : 150,
|
||||||
|
tool_bg : false,
|
||||||
|
currentMenu : 1,
|
||||||
|
logsHeight : 0,
|
||||||
|
|
||||||
|
nickName : "", //本人名称
|
||||||
|
socketId : 0, //本人的id
|
||||||
|
roomId : "10086", //房间号
|
||||||
|
fileReader : null, //文件读取对象
|
||||||
|
rtcConns : {}, //远程连接
|
||||||
|
remoteMap : {}, //远程连接map
|
||||||
|
|
||||||
|
chunkSize : 256 * 1024, //一块512字节
|
||||||
|
offset : 0, //当前文件分片位移
|
||||||
|
fileName : null, //文件名称
|
||||||
|
allSended : false,//当前文件是否全部发送给房间内所有用户
|
||||||
|
allReceiveSize : 0, //统计收到文件的大小
|
||||||
|
currentSendingId : "", //当前正在发送的文件
|
||||||
|
chooseFile : null, //选择的文件
|
||||||
|
sendFileList : [],
|
||||||
|
logs : [], //记录日志
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed : {
|
||||||
|
createDisabled : function(){
|
||||||
|
return this.isJoined || this.fileName || !this.roomId;
|
||||||
|
},
|
||||||
|
exsitDisabled : function(){
|
||||||
|
return !this.isJoined;
|
||||||
|
},
|
||||||
|
uploadDisabled : function(){
|
||||||
|
return !this.fileName || this.allSended;
|
||||||
|
},
|
||||||
|
showSendFileList : function() {
|
||||||
|
return this.sendFileList && this.sendFileList.length > 5;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch : {
|
||||||
|
currentMenu :function(newV,oldV){
|
||||||
|
|
||||||
|
},
|
||||||
|
allSended :function(newV,oldV){
|
||||||
|
|
||||||
|
},
|
||||||
|
fileName : function(newV,oldV){
|
||||||
|
this.chooseFile = this.$refs['self-file'].files[0];
|
||||||
|
if(!this.chooseFile) return;
|
||||||
|
if(!this.socketId) return;
|
||||||
|
|
||||||
|
this.$refs['sendProgress'].max = this.chooseFile.size;
|
||||||
|
|
||||||
|
this.socket.emit('message', {
|
||||||
|
emitType : "sendFileInfo",
|
||||||
|
name: this.chooseFile.name,
|
||||||
|
type: this.chooseFile.type,
|
||||||
|
size: this.chooseFile.size,
|
||||||
|
room : this.roomId,
|
||||||
|
from : this.socketId,
|
||||||
|
});
|
||||||
|
this.allSended = false;
|
||||||
|
|
||||||
|
let idList = [];
|
||||||
|
for(let id in this.remoteMap){
|
||||||
|
this.setRemoteInfo(id,{status : 0})
|
||||||
|
idList.push(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this.socketId){
|
||||||
|
let toIdStr = "";
|
||||||
|
if(idList.length > 0){
|
||||||
|
toIdStr += "发送给房间的 "+idList[0]+" ...等"+idList.length+"人";
|
||||||
|
}
|
||||||
|
let fileData = {
|
||||||
|
name : this.chooseFile.name,
|
||||||
|
size : this.chooseFile.size,
|
||||||
|
type : this.chooseFile.type,
|
||||||
|
toIdStr : toIdStr,
|
||||||
|
}
|
||||||
|
this.sendFileList.push(fileData);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
allReceiveSize : function(newV,oldV){
|
||||||
|
this.allReceiveSize = newV;
|
||||||
|
},
|
||||||
|
remoteMap : {
|
||||||
|
handler : function(newV,oldV){
|
||||||
|
|
||||||
|
},
|
||||||
|
deep : true,
|
||||||
|
immediate : true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods : {
|
||||||
|
genNickName : function () {
|
||||||
|
// 获取指定范围内的随机数
|
||||||
|
function randomAccess(min,max){
|
||||||
|
return Math.floor(Math.random() * (min - max) + max)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解码
|
||||||
|
function decodeUnicode(str) {
|
||||||
|
//Unicode显示方式是\u4e00
|
||||||
|
str = "\\u"+str;
|
||||||
|
str = str.replace(/\\/g, "%");
|
||||||
|
//转换中文
|
||||||
|
str = unescape(str);
|
||||||
|
//将其他受影响的转换回原来
|
||||||
|
str = str.replace(/%/g, "\\");
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRandomName(len){
|
||||||
|
let name = ""
|
||||||
|
for(let i = 0;i < len; i++){
|
||||||
|
let unicodeNum = ""
|
||||||
|
unicodeNum = randomAccess(0x4e00,0x9fa5).toString(16)
|
||||||
|
name += decodeUnicode(unicodeNum)
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
return getRandomName(4);
|
||||||
|
},
|
||||||
|
addPopup : function(msg) {
|
||||||
|
window.Bus.$emit("addPopup",msg);
|
||||||
|
},
|
||||||
|
cleanPopup : function(){
|
||||||
|
window.Bus.$emit("popupMap");
|
||||||
|
},
|
||||||
|
clickChooseFile : function(){
|
||||||
|
this.$refs['self-file'].click();
|
||||||
|
},
|
||||||
|
clickHome : function(show = true){
|
||||||
|
this.currentMenu = 1;
|
||||||
|
|
||||||
|
let body = document.body;
|
||||||
|
let menuBorder = document.querySelector(".menu__border");
|
||||||
|
let active = this.$refs['btnHome'];
|
||||||
|
let box = active.getBoundingClientRect();
|
||||||
|
|
||||||
|
body.style.backgroundColor = active.style.getPropertyValue("--bgColorBody");
|
||||||
|
offsetMenuBorder (box, menuBorder);
|
||||||
|
|
||||||
|
function offsetMenuBorder(box, menuBorder) {
|
||||||
|
let left = Math.floor(box.left - menuBorder.closest("menu").offsetLeft - (menuBorder.offsetWidth - box.width) / 2) + "px";
|
||||||
|
menuBorder.style.transform = `translate3d(${left}, 0 , 0)`
|
||||||
|
}
|
||||||
|
|
||||||
|
if(show){
|
||||||
|
this.clickSendFile();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
clickRoom : function(show = true){
|
||||||
|
this.currentMenu = 2;
|
||||||
|
|
||||||
|
let body = document.body;
|
||||||
|
let menuBorder = document.querySelector(".menu__border");
|
||||||
|
let active = this.$refs['btnRoom'];
|
||||||
|
let box = active.getBoundingClientRect();
|
||||||
|
|
||||||
|
body.style.backgroundColor = active.style.getPropertyValue("--bgColorBody");
|
||||||
|
offsetMenuBorder (box, menuBorder);
|
||||||
|
|
||||||
|
function offsetMenuBorder(box, menuBorder) {
|
||||||
|
let left = Math.floor(box.left - menuBorder.closest("menu").offsetLeft - (menuBorder.offsetWidth - box.width) / 2) + "px";
|
||||||
|
menuBorder.style.transform = `translate3d(${left}, 0 , 0)`
|
||||||
|
}
|
||||||
|
|
||||||
|
if(show){
|
||||||
|
this.clickReceiveFile()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
clickFile : function(show = true){
|
||||||
|
this.currentMenu = 3;
|
||||||
|
|
||||||
|
let body = document.body;
|
||||||
|
let menuBorder = document.querySelector(".menu__border");
|
||||||
|
let active = this.$refs['btnFile'];
|
||||||
|
let box = active.getBoundingClientRect();
|
||||||
|
|
||||||
|
body.style.backgroundColor = active.style.getPropertyValue("--bgColorBody");
|
||||||
|
offsetMenuBorder (box, menuBorder);
|
||||||
|
|
||||||
|
function offsetMenuBorder(box, menuBorder) {
|
||||||
|
let left = Math.floor(box.left - menuBorder.closest("menu").offsetLeft - (menuBorder.offsetWidth - box.width) / 2) + "px";
|
||||||
|
menuBorder.style.transform = `translate3d(${left}, 0 , 0)`
|
||||||
|
}
|
||||||
|
|
||||||
|
if(show){
|
||||||
|
this.clickReceiveFile()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
//生成下载标签
|
||||||
|
genDownUrlTableDom : function(id, domA, fileType, fileSize){
|
||||||
|
let tr = document.createElement("tr");
|
||||||
|
let thDom_id = document.createElement("td");
|
||||||
|
let span_id = document.createElement("span");
|
||||||
|
span_id.innerText = id;
|
||||||
|
thDom_id.appendChild(span_id);
|
||||||
|
|
||||||
|
let thDom_fileName = document.createElement("td");
|
||||||
|
let span_fileName = document.createElement("span");
|
||||||
|
span_fileName.appendChild(domA);
|
||||||
|
thDom_fileName.appendChild(span_fileName);
|
||||||
|
|
||||||
|
let thDom_fileType = document.createElement("td");
|
||||||
|
let span_fileType = document.createElement("span");
|
||||||
|
span_fileType.innerText = fileType;
|
||||||
|
thDom_fileType.appendChild(span_fileType);
|
||||||
|
|
||||||
|
let thDom_fileSize = document.createElement("td");
|
||||||
|
let span_fileSize = document.createElement("span");
|
||||||
|
span_fileSize.innerText = this.getFileSizeStr(fileSize);
|
||||||
|
thDom_fileSize.appendChild(span_fileSize);
|
||||||
|
|
||||||
|
tr.appendChild(thDom_id);
|
||||||
|
tr.appendChild(thDom_fileName);
|
||||||
|
tr.appendChild(thDom_fileType);
|
||||||
|
tr.appendChild(thDom_fileSize);
|
||||||
|
this.$refs['files'].appendChild(tr);
|
||||||
|
},
|
||||||
|
//文件大小
|
||||||
|
getFileSizeStr : function (size){
|
||||||
|
let sizeStr = (size/1048576).toString();
|
||||||
|
let head = sizeStr.split(".")[0];
|
||||||
|
let tail = "";
|
||||||
|
if(sizeStr.split(".")[1]){
|
||||||
|
tail = sizeStr.split(".")[1].substr(0,3);
|
||||||
|
}
|
||||||
|
return head + '.' + tail + "M";
|
||||||
|
},
|
||||||
|
//文件进度
|
||||||
|
getFileSizePercent : function (offset, size){
|
||||||
|
if(size === 0) return 0;
|
||||||
|
let percent = (offset / size) * 100;
|
||||||
|
let percentStr = percent.toString();
|
||||||
|
let head = percentStr.split(".")[0];
|
||||||
|
return head;
|
||||||
|
},
|
||||||
|
//点击下载文件
|
||||||
|
clickReceiveFile : function(){
|
||||||
|
this.showReceiveFile = !this.showReceiveFile;
|
||||||
|
if(this.showReceiveFile){
|
||||||
|
this.numReceiveFile = 50;
|
||||||
|
}else{
|
||||||
|
this.numReceiveFile = 150;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
//点击发送文件
|
||||||
|
clickSendFile : function(){
|
||||||
|
this.showSendFile = !this.showSendFile;
|
||||||
|
if(this.showSendFile){
|
||||||
|
this.numSendFile = 50;
|
||||||
|
}else{
|
||||||
|
this.numSendFile = 150;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
//点击查看日志
|
||||||
|
clickLogs : function(){
|
||||||
|
this.showLogs = !this.showLogs;
|
||||||
|
if(this.showLogs){
|
||||||
|
this.numLogs = 50;
|
||||||
|
}else{
|
||||||
|
this.numLogs = 150;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
//点击更换背景色
|
||||||
|
clickToolBg : function(){
|
||||||
|
this.tool_bg = !this.tool_bg;
|
||||||
|
},
|
||||||
|
//点击切换tab
|
||||||
|
changeMenu : function(index){
|
||||||
|
this.menu = index;
|
||||||
|
},
|
||||||
|
//创建房间
|
||||||
|
createRoom : function () {
|
||||||
|
if(this.fileName != null){
|
||||||
|
alert("请先加入房间再选文件")
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.roomId) {
|
||||||
|
if(this.roomId.toString().length > 15){
|
||||||
|
alert("房间号太长啦");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.socket.emit('createAndJoin', { room: this.roomId });
|
||||||
|
this.isJoined = true;
|
||||||
|
// this.nickName = this.genNickName();
|
||||||
|
this.addPopup("你进入了房间"+this.roomId);
|
||||||
|
this.logs.push("你进入了房间"+this.roomId);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
//退出房间
|
||||||
|
exitRoom : function () {
|
||||||
|
if (this.roomId) {
|
||||||
|
this.socket.emit('exit', {
|
||||||
|
from : this.socketId,
|
||||||
|
room: this.roomId
|
||||||
|
});
|
||||||
|
}
|
||||||
|
for (let i in this.rtcConns) {
|
||||||
|
let rtcConnect = this.rtcConns[i];
|
||||||
|
rtcConnect.close();
|
||||||
|
rtcConnect = null;
|
||||||
|
}
|
||||||
|
this.resetData();
|
||||||
|
},
|
||||||
|
//推出后重置数据
|
||||||
|
resetData : function(){
|
||||||
|
this.isJoined = false;
|
||||||
|
this.showReceiveFile = false;
|
||||||
|
this.showSendFile = false;
|
||||||
|
this.numSendFile= 150;
|
||||||
|
this.numReceiveFile = 150;
|
||||||
|
this.tool_bg = false;
|
||||||
|
this.currentMenu = 1;
|
||||||
|
|
||||||
|
this.nickName = "";
|
||||||
|
this.socketId = 0; //本人的id
|
||||||
|
this.roomId = "10086"; //房间号
|
||||||
|
this.fileReader = null;
|
||||||
|
this.rtcConns = {}; //远程连接
|
||||||
|
this.remoteMap = {}; //远程连接map
|
||||||
|
|
||||||
|
this.chunkSize = 16 * 1024; //一块16kb字节
|
||||||
|
this.offset = 0;
|
||||||
|
this.fileName = null;
|
||||||
|
this.allSended = false;//当前文件是否全部发送给房间内所有用户
|
||||||
|
this.allReceiveSize = 0;
|
||||||
|
this.currentSendingId = ""; //当前正在发送的文件
|
||||||
|
this.chooseFile = null; //选择的文件
|
||||||
|
this.sendFileList = [];
|
||||||
|
},
|
||||||
|
//获取rtc缓存连接
|
||||||
|
getRtcConnect : function(id){
|
||||||
|
return this.rtcConns[id];
|
||||||
|
},
|
||||||
|
//创立链接
|
||||||
|
createRtcConnect : function (id) {
|
||||||
|
if(id === undefined){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let that = this;
|
||||||
|
let rtcConnect = new RTCPeerConnection(this.config);
|
||||||
|
|
||||||
|
rtcConnect.onicecandidate = (e) => {
|
||||||
|
that.iceCandidate(rtcConnect, id, e)
|
||||||
|
};
|
||||||
|
|
||||||
|
//保存peer连接
|
||||||
|
this.rtcConns[id] = rtcConnect;
|
||||||
|
if (!this.remoteMap[id]) {
|
||||||
|
Vue.set(this.remoteMap,id,{id : id})
|
||||||
|
}
|
||||||
|
|
||||||
|
//数据通道
|
||||||
|
this.initSendDataChannel(id);
|
||||||
|
|
||||||
|
rtcConnect.onremovestream = (e) => {
|
||||||
|
that.removeStream(rtcConnect, id, e)
|
||||||
|
};
|
||||||
|
|
||||||
|
return rtcConnect;
|
||||||
|
},
|
||||||
|
//获取本地与远程连接
|
||||||
|
getOrCreateRtcConnect : function(id){
|
||||||
|
let rtcConnect = this.getRtcConnect(id);
|
||||||
|
if (typeof (rtcConnect) == 'undefined'){
|
||||||
|
rtcConnect = this.createRtcConnect(id);
|
||||||
|
}
|
||||||
|
return rtcConnect;
|
||||||
|
},
|
||||||
|
//连接创立时建立 send/receive Channel链接
|
||||||
|
initSendDataChannel : function (id) {
|
||||||
|
let that = this;
|
||||||
|
|
||||||
|
let sendChannel = this.rtcConns[id].createDataChannel('sendDataChannel');
|
||||||
|
sendChannel.binaryType = 'arraybuffer';
|
||||||
|
|
||||||
|
sendChannel.addEventListener('open', ()=>{
|
||||||
|
if (sendChannel.readyState === 'open') {
|
||||||
|
that.logs.push("建立连接 : channel open")
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
sendChannel.addEventListener('close', ()=>{
|
||||||
|
if (sendChannel.readyState === 'close') {
|
||||||
|
that.logs.push("连接关闭 : channel close")
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
sendChannel.addEventListener('error', (error) => {
|
||||||
|
that.handlerSendChannelError(error)
|
||||||
|
});
|
||||||
|
|
||||||
|
this.rtcConns[id].addEventListener('datachannel', (event)=>{
|
||||||
|
that.initReceiveDataChannel(event,id);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.setRemoteInfo(id,{sendChannel : sendChannel});
|
||||||
|
},
|
||||||
|
//处理发送过程中的错误情况
|
||||||
|
handlerSendChannelError : function(error){
|
||||||
|
console.error(error.error)
|
||||||
|
this.logs.push("连接断开 : "+error)
|
||||||
|
},
|
||||||
|
//上传文件
|
||||||
|
submitChooseFile : function(){
|
||||||
|
this.initSendData();
|
||||||
|
},
|
||||||
|
//创建发送文件事件
|
||||||
|
initSendData : function () {
|
||||||
|
let that = this;
|
||||||
|
if(this.chooseFile == undefined || this.chooseFile == null){
|
||||||
|
this.logs.push("请先选择文件")
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.fileReader = new FileReader();
|
||||||
|
this.fileReader.addEventListener('error', error => {
|
||||||
|
that.logs.push("读取文件错误 : "+error);
|
||||||
|
});
|
||||||
|
this.fileReader.addEventListener('abort', event => {
|
||||||
|
that.logs.push("读取文件中断 : "+event);
|
||||||
|
});
|
||||||
|
this.fileReader.addEventListener('load', this.sendData);
|
||||||
|
this.readSlice(0);
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 发送文件
|
||||||
|
* 0 : 未发送
|
||||||
|
* 1 : 发送中
|
||||||
|
* 2 : 已发送
|
||||||
|
* @param {*} event
|
||||||
|
*/
|
||||||
|
sendData : function(event){
|
||||||
|
let needSendingId = "";
|
||||||
|
|
||||||
|
let hasSending = false;
|
||||||
|
for(let id in this.remoteMap){
|
||||||
|
let remote = this.remoteMap[id];
|
||||||
|
let status = remote.status || 0;
|
||||||
|
if(status === 1){ //有正在发送中的
|
||||||
|
hasSending = true;
|
||||||
|
needSendingId = id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let that = this;
|
||||||
|
// hasSending = Object.keys(Object.keys(remoteMap).filter((id)=>{
|
||||||
|
// return that.remoteMap[id].status === 1;
|
||||||
|
// }));
|
||||||
|
|
||||||
|
if(!hasSending){ //没有正在发送中的, 取出对应的还没发送的文件
|
||||||
|
let hasAllSended = true;
|
||||||
|
for(let id in this.remoteMap){
|
||||||
|
let remote = this.remoteMap[id];
|
||||||
|
let status = remote.status || 0;
|
||||||
|
if(status === 0 || status === 1){
|
||||||
|
hasAllSended = false;
|
||||||
|
this.allSended = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(hasAllSended){ //全部发送完毕
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for(let id in this.remoteMap){ //还有还没发送的
|
||||||
|
let remote = this.remoteMap[id];
|
||||||
|
let status = remote.status || 0;
|
||||||
|
if(status === 0){
|
||||||
|
needSendingId = id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.setRemoteInfo(needSendingId,{status : 1}) //发送给下一个用户时更新状态
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if(needSendingId != ''){
|
||||||
|
let remote = this.remoteMap[needSendingId];
|
||||||
|
let status = remote.status || 0;
|
||||||
|
if(status === 1){ //保证同一时间只能发送房间内对应的一个用户
|
||||||
|
let sendChannel = remote.sendChannel;
|
||||||
|
if(!sendChannel || sendChannel.readyState !== 'open'){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this.offset === 0){
|
||||||
|
this.addPopup("正在发送给"+needSendingId.substr(0,4)+",0%。");
|
||||||
|
this.logs.push("正在发送给"+needSendingId.substr(0,4)+",0%。")
|
||||||
|
}
|
||||||
|
|
||||||
|
sendChannel.send(event.target.result);
|
||||||
|
this.offset += event.target.result.byteLength;
|
||||||
|
|
||||||
|
//接受完一份重置相关数据 并且开启下一个
|
||||||
|
if(this.offset === this.chooseFile.size){
|
||||||
|
console.log(needSendingId+"发送完毕");
|
||||||
|
this.addPopup("正在发送给"+needSendingId.substr(0,4)+",100%。");
|
||||||
|
this.logs.push("正在发送给"+needSendingId.substr(0,4)+",100%。")
|
||||||
|
this.offset = 0;
|
||||||
|
this.setRemoteInfo(needSendingId,{status : 2})
|
||||||
|
this.submitChooseFile();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
//文件分片 -- 点击发送时首次自动,后续就是收到ack回执后自动
|
||||||
|
readSlice : function (offset) {
|
||||||
|
const slice = this.chooseFile.slice(this.offset, offset + this.chunkSize);
|
||||||
|
this.fileReader.readAsArrayBuffer(slice);
|
||||||
|
},
|
||||||
|
//分片发送反馈ack
|
||||||
|
receivedAck : function (socketId, receivedSize) {
|
||||||
|
this.socket.emit('message', {
|
||||||
|
emitType : "receivedAck",
|
||||||
|
room : this.roomId,
|
||||||
|
from : this.socketId,
|
||||||
|
offset : receivedSize,
|
||||||
|
chunkSize : this.chunkSize,
|
||||||
|
to : socketId
|
||||||
|
});
|
||||||
|
},
|
||||||
|
//创建接收文件事件
|
||||||
|
initReceiveDataChannel : function(event, id){
|
||||||
|
if(!id || !event){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let currentRtc = this.getRemoteInfo(id);
|
||||||
|
if(currentRtc){
|
||||||
|
let receiveChannel = event.channel;
|
||||||
|
receiveChannel.binaryType = 'arraybuffer';
|
||||||
|
receiveChannel.onmessage = (env)=>{
|
||||||
|
this.receiveData(env,id);
|
||||||
|
};
|
||||||
|
receiveChannel.onopen = ()=>{
|
||||||
|
const readyState = receiveChannel.readyState;
|
||||||
|
if (readyState === 'open') {
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
receiveChannel.onclose = ()=>{
|
||||||
|
const readyState = receiveChannel.readyState;
|
||||||
|
if (readyState === 'open') {
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.setRemoteInfo(id, {receiveChannel : receiveChannel});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
//接收文件
|
||||||
|
receiveData :function(event, id) {
|
||||||
|
if(!event || !id){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let currentRtc = this.getRemoteInfo(id);
|
||||||
|
let receiveFiles = currentRtc.receiveFiles || {};
|
||||||
|
let name = receiveFiles.name;
|
||||||
|
let size = receiveFiles.size;
|
||||||
|
let type = receiveFiles.type;
|
||||||
|
|
||||||
|
//获取数据存下本地
|
||||||
|
let receiveBuffer = currentRtc.receiveBuffer || [];
|
||||||
|
let receivedSize = currentRtc.receivedSize || 0;
|
||||||
|
receiveBuffer.push(event.data);
|
||||||
|
receivedSize += event.data.byteLength;
|
||||||
|
this.$refs['receiveProgress'].value = receivedSize;
|
||||||
|
|
||||||
|
this.setRemoteInfo(id,{receiveBuffer : receiveBuffer,receivedSize : receivedSize})
|
||||||
|
this.allReceiveSize += event.data.byteLength;
|
||||||
|
|
||||||
|
//收到分片后反馈ack
|
||||||
|
this.receivedAck(id,receivedSize);
|
||||||
|
|
||||||
|
if(receivedSize === size){
|
||||||
|
console.log("接收完毕");
|
||||||
|
this.logs.push("接收完毕...");
|
||||||
|
this.$refs['receiveProgress'].value = 0;
|
||||||
|
this.addPopup("文件[ "+name+" ]接收完毕,可点击右下角查看。");
|
||||||
|
const received = new Blob(receiveBuffer);
|
||||||
|
//下载标签
|
||||||
|
let a = document.createElement("a");
|
||||||
|
a.href = URL.createObjectURL(received,{type: type});
|
||||||
|
a.download = name;
|
||||||
|
a.innerHTML = name;
|
||||||
|
a.style="color: #ff5722;text-decoration: underline;";
|
||||||
|
this.genDownUrlTableDom(id, a, type, size);
|
||||||
|
//清除接收的数据缓存
|
||||||
|
this.setRemoteInfo(id,{receiveBuffer : [], receivedSize : 0})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
//关闭连接
|
||||||
|
closeDataChannels : function () {
|
||||||
|
for(let remote in this.remoteMap){
|
||||||
|
let id = remote.id;
|
||||||
|
let sendChannel = remote.sendChannel;
|
||||||
|
let receiveChannel = remote.receiveChannel;
|
||||||
|
if(!id || !sendChannel || !receiveChannel){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
sendChannel.close();
|
||||||
|
receiveChannel.close();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
//设置rtc缓存远程连接数据
|
||||||
|
setRemoteInfo(id,data){
|
||||||
|
if(!id || !data){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let oldData = this.remoteMap[id];
|
||||||
|
if(oldData){
|
||||||
|
Object.assign(oldData,data);
|
||||||
|
Vue.set(this.remoteMap,id,oldData);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
//获取rtc缓存远程连接数据
|
||||||
|
getRemoteInfo(id){
|
||||||
|
if(!id){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return this.remoteMap[id];
|
||||||
|
},
|
||||||
|
//移除rtc连接
|
||||||
|
removeStream : function(rtcConnect,id,event){
|
||||||
|
this.getOrCreateRtcConnect(id).close;
|
||||||
|
delete this.rtcConns[id];
|
||||||
|
delete this.remoteMap[id];
|
||||||
|
},
|
||||||
|
iceCandidate : function (rtcConnect,id,event) {
|
||||||
|
if (event.candidate != null) {
|
||||||
|
let message = {
|
||||||
|
from : this.socketId,
|
||||||
|
to : id,
|
||||||
|
room : this.roomId,
|
||||||
|
sdpMid : event.candidate.sdpMid,
|
||||||
|
sdpMLineIndex : event.candidate.sdpMLineIndex,
|
||||||
|
sdp : event.candidate.candidate
|
||||||
|
};
|
||||||
|
this.socket.emit('candidate', message);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
offerSuccess : function (rtcConnect,id,offer) {
|
||||||
|
rtcConnect.setLocalDescription(offer).then(r => {})
|
||||||
|
let message = {
|
||||||
|
from : this.socketId,
|
||||||
|
to : id,
|
||||||
|
room : this.roomId,
|
||||||
|
sdp : offer.sdp
|
||||||
|
};
|
||||||
|
this.socket.emit('offer', message);
|
||||||
|
},
|
||||||
|
offerFailed : function (rtcConnect,id,error) {
|
||||||
|
this.logs.push("offer失败,"+error);
|
||||||
|
},
|
||||||
|
answerSuccess : function (rtcConnect,id,offer) {
|
||||||
|
rtcConnect.setLocalDescription(offer).then(r => {});
|
||||||
|
let message = {
|
||||||
|
from : this.socketId,
|
||||||
|
to : id,
|
||||||
|
room : this.roomId,
|
||||||
|
sdp : offer.sdp
|
||||||
|
};
|
||||||
|
this.socket.emit('answer', message);
|
||||||
|
},
|
||||||
|
answerFailed : function (rtcConnect,id,error) {
|
||||||
|
this.logs.push("answer失败,"+error);
|
||||||
|
},
|
||||||
|
addIceCandidateSuccess : function(res){
|
||||||
|
this.logs.push("addIceCandidateSuccess成功,"+res);
|
||||||
|
},
|
||||||
|
addIceCandidateFailed : function(err){
|
||||||
|
this.logs.push("addIceCandidate失败,"+err);
|
||||||
|
},
|
||||||
|
socketListener : function () {
|
||||||
|
let that = this;
|
||||||
|
this.socket.on('created', async function (data) {
|
||||||
|
that.logs.push("创建房间,"+JSON.stringify(data));
|
||||||
|
that.socketId = data.id;
|
||||||
|
that.roomId = data.room;
|
||||||
|
for (let i = 0; i < data['peers'].length; i++) {
|
||||||
|
let otherSocketId = data['peers'][i].id;
|
||||||
|
let rtcConnect = that.getOrCreateRtcConnect(otherSocketId);
|
||||||
|
rtcConnect.createOffer(that.options).then(offer => {
|
||||||
|
that.offerSuccess(rtcConnect, otherSocketId, offer);
|
||||||
|
}, error => {
|
||||||
|
that.offerFailed(rtcConnect, otherSocketId, error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
that.touchResize();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.socket.on('joined', function (data) {
|
||||||
|
that.logs.push("加入房间,"+JSON.stringify(data));
|
||||||
|
that.getOrCreateRtcConnect(data.from);
|
||||||
|
that.addPopup(data.id+"加入了房间。");
|
||||||
|
that.touchResize();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.socket.on('offer', function (data) {
|
||||||
|
that.logs.push("offer,"+JSON.stringify(data));
|
||||||
|
let rtcConnect = that.getOrCreateRtcConnect(data.from);
|
||||||
|
let rtcDescription = { type: 'offer', sdp: data.sdp };
|
||||||
|
rtcConnect.setRemoteDescription(new RTCSessionDescription(rtcDescription)).then(r => {});
|
||||||
|
rtcConnect.createAnswer(that.options).then((offer) => {
|
||||||
|
that.answerSuccess(rtcConnect, data.from, offer)
|
||||||
|
}).catch((error) => {
|
||||||
|
that.answerFailed(rtcConnect, data.from, error)
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
this.socket.on('answer', function (data) {
|
||||||
|
that.logs.push("answer,"+JSON.stringify(data));
|
||||||
|
let rtcConnect = that.getOrCreateRtcConnect(data.from);
|
||||||
|
let rtcDescription = { type: 'answer', sdp: data.sdp };
|
||||||
|
rtcConnect.setRemoteDescription(new RTCSessionDescription(rtcDescription)).then(r => {});
|
||||||
|
});
|
||||||
|
|
||||||
|
this.socket.on('candidate', function (data) {
|
||||||
|
that.logs.push("candidate,"+JSON.stringify(data));
|
||||||
|
let rtcConnect = that.getOrCreateRtcConnect(data.from);
|
||||||
|
let rtcIceCandidate = new RTCIceCandidate({
|
||||||
|
candidate: data.sdp,
|
||||||
|
sdpMid: data.sdpMid,
|
||||||
|
sdpMLineIndex: data.sdpMLineIndex
|
||||||
|
});
|
||||||
|
rtcConnect.addIceCandidate(rtcIceCandidate).then(res => {
|
||||||
|
that.addIceCandidateSuccess(res);
|
||||||
|
}).catch(error => {
|
||||||
|
that.addIceCandidateFailed(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
this.socket.on('exit', function (data) {
|
||||||
|
var rtcConnect = that.rtcConns[data.from];
|
||||||
|
if (typeof (rtcConnect) == 'undefined') {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
that.addPopup(data.from+"退出了房间。");
|
||||||
|
that.logs.push("退出房间,"+JSON.stringify(data));
|
||||||
|
that.getOrCreateRtcConnect(data.from).close;
|
||||||
|
delete that.rtcConns[data.from];
|
||||||
|
Vue.delete(that.remoteMap,data.from);
|
||||||
|
}
|
||||||
|
that.touchResize();
|
||||||
|
})
|
||||||
|
|
||||||
|
//选中文件时发送给接收方
|
||||||
|
this.socket.on('sendFileInfo', function (data) {
|
||||||
|
console.log('sendFileInfo ',data)
|
||||||
|
let fromId = data.from;
|
||||||
|
that.setRemoteInfo(fromId,{receiveFiles : data});
|
||||||
|
that.addPopup(data.from+"选择了文件 [ "+data.name+" ],即将发送。");
|
||||||
|
that.logs.push(data.from+"选择了文件 [ "+data.name+" ],即将发送。");
|
||||||
|
that.$refs['receiveProgress'].max = data.size;
|
||||||
|
});
|
||||||
|
|
||||||
|
//收到文件回传ack,继续分片回传
|
||||||
|
this.socket.on('receivedAck', function (data) {
|
||||||
|
let to = data.to;
|
||||||
|
if(to === that.socketId){
|
||||||
|
if(that.offset < that.chooseFile.size){
|
||||||
|
that.readSlice(that.offset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
initCss : function(e){
|
||||||
|
if(!e) return;
|
||||||
|
if(this.currentMenu === 1){
|
||||||
|
this.clickHome(false);
|
||||||
|
}else if(this.currentMenu === 2){
|
||||||
|
this.clickRoom(false);
|
||||||
|
}else if(this.currentMenu === 3){
|
||||||
|
this.clickFile(false);
|
||||||
|
}
|
||||||
|
//re caculate size
|
||||||
|
this.reCaculateSwiperSize();
|
||||||
|
|
||||||
|
this.logsHeight = document.documentElement.clientHeight-55;
|
||||||
|
},
|
||||||
|
loadJS : function( url, callback ){
|
||||||
|
var script = document.createElement('script'),
|
||||||
|
fn = callback || function(){};
|
||||||
|
script.type = 'text/javascript';
|
||||||
|
//IE
|
||||||
|
if(script.readyState){
|
||||||
|
script.onreadystatechange = function(){
|
||||||
|
if( script.readyState == 'loaded' || script.readyState == 'complete' ){
|
||||||
|
script.onreadystatechange = null;
|
||||||
|
fn();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}else{
|
||||||
|
//其他浏览器
|
||||||
|
script.onload = function(){
|
||||||
|
fn();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
script.src = url;
|
||||||
|
document.getElementsByTagName('head')[0].appendChild(script);
|
||||||
|
},
|
||||||
|
reCaculateSwiperSize : function () {
|
||||||
|
let clientWidth = document.body.clientWidth;
|
||||||
|
let slidesPerView = parseInt((clientWidth / 100))-1;
|
||||||
|
window.swiper.params.slidesPerView = slidesPerView;
|
||||||
|
},
|
||||||
|
touchResize : function() {
|
||||||
|
let that = this;
|
||||||
|
setTimeout(()=>{
|
||||||
|
var myEvent = new Event('resize');
|
||||||
|
window.dispatchEvent(myEvent);
|
||||||
|
that.reCaculateSwiperSize();
|
||||||
|
},100)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created : function () {
|
||||||
|
let that = this;
|
||||||
|
if(window.location.search && window.location.search.includes("debug")){
|
||||||
|
this.loadJS('js/vconsole.min.js',function(){
|
||||||
|
that.loadJS('js/vconsole.js',function(){
|
||||||
|
console.log("load vconsole success")
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted : function () {
|
||||||
|
this.$nextTick(()=>{
|
||||||
|
this.logs.push("socket 初始化中...");
|
||||||
|
this.socketListener();
|
||||||
|
this.logs.push("socket 初始化成功");
|
||||||
|
})
|
||||||
|
this.clickHome(false);
|
||||||
|
|
||||||
|
window.onresize = this.initCss;
|
||||||
|
},
|
||||||
|
destroyed : function () {
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
|
5
res/js/index.min.js
vendored
Normal file
5
res/js/index.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
res/js/scroxt.min.js
vendored
Normal file
1
res/js/scroxt.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
3
res/js/socket.io.js
Normal file
3
res/js/socket.io.js
Normal file
File diff suppressed because one or more lines are too long
14
res/js/swiper-bundle.min.js
vendored
Normal file
14
res/js/swiper-bundle.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
6
res/js/vue.min.js
vendored
Normal file
6
res/js/vue.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
55
res/vertical.html
Normal file
55
res/vertical.html
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||||
|
<title>vertical</title>
|
||||||
|
<script type="text/javascript" src="js/vue.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<style>
|
||||||
|
.vertical{
|
||||||
|
height: 30px;
|
||||||
|
font-size: 13px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
[v-cloak]{
|
||||||
|
visibility: hidden !important; /*防止被覆盖*/
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div class="vertical" v-cloak></div>
|
||||||
|
|
||||||
|
|
||||||
|
<script src="js/scroxt.min.js"></script>
|
||||||
|
<script>
|
||||||
|
let verticalVue = new Vue({
|
||||||
|
el : ".vertical",
|
||||||
|
data : {
|
||||||
|
popupMap : {}, //弹幕事件数组
|
||||||
|
scroxtVertical : null,//弹幕滑动对象
|
||||||
|
},
|
||||||
|
methods : {
|
||||||
|
addPopup : function(msg) {
|
||||||
|
this.scroxtVertical.addBarrage(msg)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted : function () {
|
||||||
|
let that = this;
|
||||||
|
setTimeout(()=>{
|
||||||
|
that.scroxtVertical = new scroxt.Live({target: ".vertical"});
|
||||||
|
window.parent.Bus.$on("addPopup",(data)=>{
|
||||||
|
that.addPopup(data);
|
||||||
|
})
|
||||||
|
window.parent.Bus.$on("cleanPopUp",(data)=>{
|
||||||
|
that.popupMap = {};
|
||||||
|
})
|
||||||
|
},1000)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
25
server.js
Normal file
25
server.js
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
const express = require("express"); //express
|
||||||
|
const conf = require("./conf/cfg"); //conf
|
||||||
|
const fileApiRouters = require("./src/router")(conf); //file routers
|
||||||
|
let resRouter = conf.router.res;
|
||||||
|
const fs = require('fs'); // fs
|
||||||
|
const https = require('https'); // http
|
||||||
|
|
||||||
|
let app = express();
|
||||||
|
console.log("resource including...")
|
||||||
|
|
||||||
|
//res
|
||||||
|
for(let key in resRouter) app.use(key,express.static(resRouter[key]));
|
||||||
|
|
||||||
|
//file api
|
||||||
|
for(let key in fileApiRouters) app.use(key,fileApiRouters[key])
|
||||||
|
|
||||||
|
let options = {
|
||||||
|
key: fs.readFileSync('./conf/keys/server.key'),
|
||||||
|
cert: fs.readFileSync('./conf/keys/server.crt')
|
||||||
|
}
|
||||||
|
https.createServer(options,app).listen(conf.node.port);
|
||||||
|
console.log("express init...")
|
||||||
|
|
||||||
|
|
||||||
|
console.log("server runing on ",conf.node.port," successful");
|
46
serversocket.js
Normal file
46
serversocket.js
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
const https = require('https'); // http
|
||||||
|
const socketIO = require('socket.io'); //socket
|
||||||
|
const app = require("express")(); //express
|
||||||
|
const fs = require('fs'); // fs
|
||||||
|
const db = require("./src/tables/db"); //db
|
||||||
|
const conf = require("./conf/cfg"); //conf
|
||||||
|
const utils = require("./utils/request"); //utils
|
||||||
|
const socket = require("./src/socket/index") //socket handler
|
||||||
|
|
||||||
|
if(conf.db.open){
|
||||||
|
// db init
|
||||||
|
let {tables,sql,Sql} = db.excute(conf);
|
||||||
|
app.use(async function (req,res,next) {
|
||||||
|
req.ctx = {};
|
||||||
|
req.ctx.tables = tables;
|
||||||
|
req.ctx.sql = sql;
|
||||||
|
req.ctx.Sql = Sql;
|
||||||
|
await next();
|
||||||
|
})
|
||||||
|
console.log("db init...")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//log flow init --日志流水初始
|
||||||
|
app.use(async function (req,res,next) {
|
||||||
|
res.tl = {};
|
||||||
|
res.tl.flowId = utils.genFlow();
|
||||||
|
await next();
|
||||||
|
})
|
||||||
|
console.log("flow init...")
|
||||||
|
|
||||||
|
|
||||||
|
//Socket连接监听
|
||||||
|
let options = {
|
||||||
|
key: fs.readFileSync('./conf/keys/server.key'),
|
||||||
|
cert: fs.readFileSync('./conf/keys/server.crt')
|
||||||
|
}
|
||||||
|
let io = socketIO.listen(
|
||||||
|
https.createServer(options).listen(conf.ws.ssl_port)
|
||||||
|
);
|
||||||
|
conf.ws.io = io;
|
||||||
|
socket.excute(tables,conf);
|
||||||
|
console.log("socket init...")
|
||||||
|
|
||||||
|
|
||||||
|
console.log("socket listen on ",conf.ws.ssl_port," successful");
|
23
src/comm/comm.js
Normal file
23
src/comm/comm.js
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
const utils = require("../../utils/request");
|
||||||
|
const conf = require("../../conf/cfg");
|
||||||
|
const wsConf = conf.ws;
|
||||||
|
const webrtcConf = conf.webrtc;
|
||||||
|
|
||||||
|
//获取ip地址,初始化等相关配置
|
||||||
|
function initData(req,res) {
|
||||||
|
let localIp = utils.getLocalIP();
|
||||||
|
let clientIp = utils.getClientIP(req);
|
||||||
|
let isLocal = clientIp === '1';
|
||||||
|
|
||||||
|
let data = {
|
||||||
|
wsHost : isLocal ? "ws://"+localIp+":"+wsConf.port : wsConf.ws_online,
|
||||||
|
rtcConfig : {iceServers : webrtcConf.iceServers} ,
|
||||||
|
options : webrtcConf.options
|
||||||
|
};
|
||||||
|
|
||||||
|
res.json(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
initData
|
||||||
|
}
|
11
src/comm/index.js
Normal file
11
src/comm/index.js
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
const express = require('express');
|
||||||
|
const comm = require("./comm");
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = function () {
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
router.get("/initData",comm.initData);
|
||||||
|
|
||||||
|
return router;
|
||||||
|
}
|
14
src/module/index.js
Normal file
14
src/module/index.js
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
const express = require('express');
|
||||||
|
const webModule = require("./webModule");
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = function () {
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
router.get("/add",webModule.add);
|
||||||
|
router.get("/set",webModule.set);
|
||||||
|
router.get("/get",webModule.get);
|
||||||
|
router.get("/del",webModule.del);
|
||||||
|
|
||||||
|
return router;
|
||||||
|
}
|
118
src/module/webModule.js
Normal file
118
src/module/webModule.js
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
|
||||||
|
/**
|
||||||
|
* 添加模块数据
|
||||||
|
* @param {*} req
|
||||||
|
* @param {*} res
|
||||||
|
* @param {*} next
|
||||||
|
*/
|
||||||
|
async function add(req,res,next) {
|
||||||
|
let ctx = req.ctx || {};
|
||||||
|
let params = req.params || {};
|
||||||
|
let info = params.info || {};
|
||||||
|
|
||||||
|
let addInfo = {};
|
||||||
|
Object.assign(addInfo,info.id)
|
||||||
|
Object.assign(addInfo,info.uname)
|
||||||
|
Object.assign(addInfo,info.name)
|
||||||
|
Object.assign(addInfo,info.flag)
|
||||||
|
Object.assign(addInfo,info.other)
|
||||||
|
Object.assign(addInfo,info.content)
|
||||||
|
|
||||||
|
let data = await ctx.tables.Module.create(addInfo);
|
||||||
|
|
||||||
|
if(res){
|
||||||
|
res.json(data)
|
||||||
|
}else {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新模块数据
|
||||||
|
* @param {*} req
|
||||||
|
* @param {*} res
|
||||||
|
* @param {*} next
|
||||||
|
*/
|
||||||
|
async function set(req,res,next) {
|
||||||
|
let ctx = req.ctx || {};
|
||||||
|
let params = req.params || {};
|
||||||
|
let info = params.info || {};
|
||||||
|
let matcher = params.matcher || {};
|
||||||
|
|
||||||
|
let upInfo = {};
|
||||||
|
Object.assign(upInfo,info.id)
|
||||||
|
Object.assign(upInfo,info.uname)
|
||||||
|
Object.assign(upInfo,info.name)
|
||||||
|
Object.assign(upInfo,info.flag)
|
||||||
|
Object.assign(upInfo,info.other)
|
||||||
|
Object.assign(upInfo,info.content)
|
||||||
|
|
||||||
|
let matcherInfo = {};
|
||||||
|
Object.assign(matcherInfo,matcher.id)
|
||||||
|
|
||||||
|
let data = await ctx.tables.Module.update(upInfo,{
|
||||||
|
where : matcherInfo
|
||||||
|
});
|
||||||
|
|
||||||
|
if(res){
|
||||||
|
res.json(data)
|
||||||
|
}else {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取模块数据
|
||||||
|
* @param {*} req
|
||||||
|
* @param {*} res
|
||||||
|
* @param {*} next
|
||||||
|
*/
|
||||||
|
async function get(req,res,next) {
|
||||||
|
let ctx = req.ctx || {};
|
||||||
|
let params = req.params || {};
|
||||||
|
let matcher = params.matcher || {};
|
||||||
|
|
||||||
|
let matcherInfo = {};
|
||||||
|
Object.assign(matcherInfo,matcher.id)
|
||||||
|
|
||||||
|
let data = await ctx.tables.Module.findAll({
|
||||||
|
where : matcherInfo
|
||||||
|
});
|
||||||
|
|
||||||
|
if(res){
|
||||||
|
res.json(data)
|
||||||
|
}else {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除模块数据
|
||||||
|
* @param {*} req
|
||||||
|
* @param {*} res
|
||||||
|
* @param {*} next
|
||||||
|
*/
|
||||||
|
async function del(req,res,next) {
|
||||||
|
let ctx = req.ctx || {};
|
||||||
|
let params = req.params || {};
|
||||||
|
let matcher = params.matcher || {};
|
||||||
|
|
||||||
|
let matcherInfo = {};
|
||||||
|
Object.assign(matcherInfo,matcher.id)
|
||||||
|
|
||||||
|
let data = await ctx.tables.Module.destroy({
|
||||||
|
where : matcherInfo
|
||||||
|
});
|
||||||
|
|
||||||
|
if(res){
|
||||||
|
res.json(data)
|
||||||
|
}else {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
add,set,get,del
|
||||||
|
}
|
18
src/room/index.js
Normal file
18
src/room/index.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
const express = require('express');
|
||||||
|
const room = require("./room");
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = function () {
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
router.get("/:name",room.getRoomByName);
|
||||||
|
router.get("/o/:uid",room.getOwnerRoomByUid);
|
||||||
|
router.get("/j/:name",room.getJoinRoomByName);
|
||||||
|
|
||||||
|
router.get("/o/add",room.addOwnerRoom);
|
||||||
|
router.get("/j/add",room.addJoinRoom);
|
||||||
|
|
||||||
|
router.get("/j/set/:id",room.updateRoom);
|
||||||
|
|
||||||
|
return router;
|
||||||
|
}
|
203
src/room/room.js
Normal file
203
src/room/room.js
Normal file
@@ -0,0 +1,203 @@
|
|||||||
|
|
||||||
|
const utils = require("../../utils/request");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取房主信息 by uid rname
|
||||||
|
* @param {*} req
|
||||||
|
* @param {*} res
|
||||||
|
* @param {*} next
|
||||||
|
*/
|
||||||
|
async function getOwnerRoomByUid(req,res,next) {
|
||||||
|
let ctx = req.ctx || {};
|
||||||
|
let params = req.params || {};
|
||||||
|
let data = {};
|
||||||
|
|
||||||
|
//加入之前需要获取房间信息给客户端
|
||||||
|
data = await ctx.tables.Room.findOne({
|
||||||
|
where : {
|
||||||
|
uid : params.uid,
|
||||||
|
rname : params.rname,
|
||||||
|
status : 0 //有开启的房间
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if(res){
|
||||||
|
res.json(data)
|
||||||
|
}else {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取房间成员信息 by rname
|
||||||
|
* @param {*} req
|
||||||
|
* @param {*} res
|
||||||
|
* @param {*} next
|
||||||
|
*/
|
||||||
|
async function getJoinRoomByName(req,res,next) {
|
||||||
|
let ctx = req.ctx || {};
|
||||||
|
let params = req.params || {};
|
||||||
|
let data = {};
|
||||||
|
|
||||||
|
//加入之前需要获取房间信息给客户端
|
||||||
|
data = await ctx.tables.Room.findOne({
|
||||||
|
where : {
|
||||||
|
rname : params.rname,
|
||||||
|
status : 0 //有开启的房间
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if(res){
|
||||||
|
res.json(data)
|
||||||
|
}else {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取房间信息 by rname
|
||||||
|
* @param {*} req
|
||||||
|
* @param {*} res
|
||||||
|
* @param {*} next
|
||||||
|
*/
|
||||||
|
async function getRoomByName(req,res,next) {
|
||||||
|
let ctx = req.ctx || {};
|
||||||
|
let params = req.params || {};
|
||||||
|
let data = {};
|
||||||
|
|
||||||
|
data = await ctx.tables.Room.findAll({
|
||||||
|
where : {
|
||||||
|
rname : params.rname,
|
||||||
|
status : 0
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if(res){
|
||||||
|
res.json(data)
|
||||||
|
}else {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建房间
|
||||||
|
* @param {*} req
|
||||||
|
* @param {*} res
|
||||||
|
* @param {*} next
|
||||||
|
*/
|
||||||
|
async function addOwnerRoom(req,res,next) {
|
||||||
|
let ctx = req.ctx || {};
|
||||||
|
let params = req.params || {};
|
||||||
|
let data = {};
|
||||||
|
|
||||||
|
data = await ctx.tables.Room.create({
|
||||||
|
uid : params.uid,
|
||||||
|
uname : params.uname,
|
||||||
|
rcode : utils.genRoom(),
|
||||||
|
rname : params.rname,
|
||||||
|
sid : params.sid,
|
||||||
|
ip : params.ip,
|
||||||
|
device : params.device,
|
||||||
|
url : params.url,
|
||||||
|
content : params.content
|
||||||
|
});
|
||||||
|
|
||||||
|
if(res){
|
||||||
|
res.json(data)
|
||||||
|
}else {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加入房间
|
||||||
|
* @param {*} req
|
||||||
|
* @param {*} res
|
||||||
|
* @param {*} next
|
||||||
|
*/
|
||||||
|
async function addJoinRoom(req,res,next) {
|
||||||
|
let ctx = req.ctx || {};
|
||||||
|
let params = req.params || {};
|
||||||
|
|
||||||
|
let data = await ctx.tables.Room.create({
|
||||||
|
rcode : utils.genRoom(),
|
||||||
|
rname : params.rname,
|
||||||
|
sid : params.sid,
|
||||||
|
ip : params.ip,
|
||||||
|
device : params.device,
|
||||||
|
url : params.url,
|
||||||
|
content : params.content
|
||||||
|
});
|
||||||
|
|
||||||
|
if(res){
|
||||||
|
res.json(data)
|
||||||
|
}else {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新房间
|
||||||
|
* @param {*} req
|
||||||
|
* @param {*} res
|
||||||
|
* @param {*} next
|
||||||
|
*/
|
||||||
|
async function updateRoom(req,res,next) {
|
||||||
|
let ctx = req.ctx || {};
|
||||||
|
let params = req.params || {};
|
||||||
|
|
||||||
|
let data = await ctx.tables.Room.update({
|
||||||
|
status : 1
|
||||||
|
},{
|
||||||
|
where : {
|
||||||
|
id : params.id
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if(res){
|
||||||
|
res.json(data)
|
||||||
|
}else {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新房间
|
||||||
|
* @param {*} req
|
||||||
|
* @param {*} res
|
||||||
|
* @param {*} next
|
||||||
|
*/
|
||||||
|
async function updateRoomBySid(req,res,next) {
|
||||||
|
let ctx = req.ctx || {};
|
||||||
|
let params = req.params || {};
|
||||||
|
|
||||||
|
let data = await ctx.tables.Room.update({
|
||||||
|
status : 1
|
||||||
|
},{
|
||||||
|
where : {
|
||||||
|
sid : params.sid
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if(res){
|
||||||
|
res.json(data)
|
||||||
|
}else {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
getRoomByName,
|
||||||
|
getOwnerRoomByUid,
|
||||||
|
getJoinRoomByName,
|
||||||
|
addOwnerRoom,
|
||||||
|
addJoinRoom,
|
||||||
|
updateRoom,
|
||||||
|
updateRoomBySid
|
||||||
|
}
|
24
src/router.js
Normal file
24
src/router.js
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
module.exports = (conf) => {
|
||||||
|
let routers = {};
|
||||||
|
let handlerConf = conf.router.filter;
|
||||||
|
|
||||||
|
//file模块
|
||||||
|
let dirs = fs.readdirSync(__dirname);
|
||||||
|
for (let file of dirs) {
|
||||||
|
//过滤文件夹和文件
|
||||||
|
if(handlerConf.whiteDir.includes(file) || handlerConf.whiteFile.includes(file)) continue;
|
||||||
|
try {
|
||||||
|
//添加router
|
||||||
|
let stat = fs.statSync(path.join(__dirname, file, 'index.js'));
|
||||||
|
if (stat && stat.isFile()) {
|
||||||
|
routers["/api/"+file] = require("./" + file + '/index')();
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return routers;
|
||||||
|
}
|
241
src/socket/index.js
Normal file
241
src/socket/index.js
Normal file
@@ -0,0 +1,241 @@
|
|||||||
|
const room = require("../room/room");
|
||||||
|
const template = require("./template");
|
||||||
|
const SocketHandler = template.SocketHandler;
|
||||||
|
const Worker = require("./../worker/worker").Worker; //worker
|
||||||
|
|
||||||
|
|
||||||
|
let tables = {};
|
||||||
|
let exitSocketList = []; //退房的socket
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 开个定时器跑,清除已经离开房间的数据
|
||||||
|
*/
|
||||||
|
function cleanExitSocketData(){
|
||||||
|
async function removeRoomSokect(){
|
||||||
|
if(exitSocketList == null || !(exitSocketList instanceof Array)){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(exitSocketList.length <= 0){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for(let i = 0; i < exitSocketList.length; i++){
|
||||||
|
await exitRoom({sid : exitSocketList.shift()})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let cusWorker = new Worker(24 * 60 * 60 * 1000,removeRoomSokect);
|
||||||
|
cusWorker.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行器
|
||||||
|
* @param {*} tabs
|
||||||
|
* @param {*} config
|
||||||
|
*/
|
||||||
|
function excute(tabs = {}, config = {}) {
|
||||||
|
init(config);
|
||||||
|
tables = tabs;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 参数初始化
|
||||||
|
* @param {*} config
|
||||||
|
*/
|
||||||
|
function init(config) {
|
||||||
|
let io = config.ws.io;
|
||||||
|
if (io === undefined || io === null) {
|
||||||
|
console.error("init socket error ");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//前置执行函数
|
||||||
|
if (config.ws.beforeInit) {
|
||||||
|
config.ws.beforeInit();
|
||||||
|
}
|
||||||
|
|
||||||
|
listen(io);
|
||||||
|
cleanExitSocketData();
|
||||||
|
|
||||||
|
//后置执行函数
|
||||||
|
if (config.ws.afterInit) {
|
||||||
|
config.ws.afterInit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 监听函数
|
||||||
|
* @param {*} io
|
||||||
|
*/
|
||||||
|
function listen(io) {
|
||||||
|
io.sockets.on('connection', function (socket) {
|
||||||
|
|
||||||
|
var handler = new SocketHandler(io.sockets,socket);
|
||||||
|
|
||||||
|
socket.on('disconnect',async function (reason) {
|
||||||
|
handler._disconnect({});
|
||||||
|
// exitSocketList.push(socket.id);
|
||||||
|
// if(exitSocketList.length > 1000){
|
||||||
|
// new Worker(24 * 60 * 60 * 1000,removeRoomSokect);
|
||||||
|
// cusWorker.run();
|
||||||
|
// }
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
socket.on('createAndJoin', async function (message) {
|
||||||
|
let room = message.room;
|
||||||
|
let recoderId = 1;
|
||||||
|
|
||||||
|
handler._createAndJoin(message,{
|
||||||
|
created : {recoderId:recoderId},
|
||||||
|
joined : {recoderId:recoderId}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
socket.on('offer', function (message) {
|
||||||
|
handler._offer(message,{})
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('answer', function (message) {
|
||||||
|
handler._answer(message,{})
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
socket.on('candidate', function (message) {
|
||||||
|
handler._candidate(message,{})
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
socket.on('exit', async function (message) {
|
||||||
|
let recoderId = message.recoderId;
|
||||||
|
handler._exit(message,{})
|
||||||
|
if(recoderId != undefined){
|
||||||
|
exitSocketList.push(socket.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('exist', async function (message) {
|
||||||
|
let room = message.room;
|
||||||
|
|
||||||
|
let data = {};
|
||||||
|
data.success = true;
|
||||||
|
data.msg = "ok";
|
||||||
|
data.room = room;
|
||||||
|
|
||||||
|
socket.emit('exist', data);
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
socket.on('catchError', function (err) {
|
||||||
|
console.log('err : ', err)
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
socket.on('message', function (message) {
|
||||||
|
handler._message(message,{})
|
||||||
|
})
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 房间是否存在
|
||||||
|
* @param {*} data
|
||||||
|
*/
|
||||||
|
async function isRoomExist(data) {
|
||||||
|
let req = {
|
||||||
|
ctx: {
|
||||||
|
tables: tables
|
||||||
|
},
|
||||||
|
params: {
|
||||||
|
rname: data.roomName,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let rooms = await room.getRoomByName(req, null);
|
||||||
|
return rooms.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 首次创房数据入库
|
||||||
|
* @param {*} data
|
||||||
|
*/
|
||||||
|
async function createRoom(data) {
|
||||||
|
let req = {
|
||||||
|
ctx: {
|
||||||
|
tables: tables
|
||||||
|
},
|
||||||
|
params: {
|
||||||
|
uid: data.uid,
|
||||||
|
uname: data.uname,
|
||||||
|
rname: data.roomName,
|
||||||
|
sid: data.socketId,
|
||||||
|
ip: data.ip,
|
||||||
|
device: data.device,
|
||||||
|
url: data.url,
|
||||||
|
content: JSON.stringify(data.content)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let res = await room.addOwnerRoom(req, null);
|
||||||
|
return res.dataValues.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加入房间
|
||||||
|
* @param {*} data
|
||||||
|
*/
|
||||||
|
async function joinRoom(data) {
|
||||||
|
let req = {
|
||||||
|
ctx: {
|
||||||
|
tables: tables
|
||||||
|
},
|
||||||
|
params: {
|
||||||
|
uid: data.uid,
|
||||||
|
uname: data.uname,
|
||||||
|
rname: data.roomName,
|
||||||
|
sid: data.socketId,
|
||||||
|
ip: data.ip,
|
||||||
|
device: data.device,
|
||||||
|
url: data.url,
|
||||||
|
content: JSON.stringify(data.content)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let res = await room.addJoinRoom(req, null);
|
||||||
|
return res.dataValues.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 退出房间
|
||||||
|
* @param {*} data
|
||||||
|
*/
|
||||||
|
async function exitRoom(data) {
|
||||||
|
let req = {
|
||||||
|
ctx: {
|
||||||
|
tables: tables
|
||||||
|
},
|
||||||
|
params: {
|
||||||
|
sid: data.sid
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let res = await room.updateRoomBySid(req, null);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function addWorker(){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
excute : excute
|
||||||
|
}
|
163
src/socket/template.js
Normal file
163
src/socket/template.js
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
/**
|
||||||
|
* 封装socket连接公共方法
|
||||||
|
*/
|
||||||
|
class SocketHandler {
|
||||||
|
constructor(sockets,socket){
|
||||||
|
this.sockets = sockets;
|
||||||
|
this.socket = socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 链接断开
|
||||||
|
* @param {*} message
|
||||||
|
* @param {*} params
|
||||||
|
*/
|
||||||
|
_disconnect(message, params){
|
||||||
|
let socketId = this.socket.id;
|
||||||
|
let data = {
|
||||||
|
from : socketId,
|
||||||
|
};
|
||||||
|
this.socket.broadcast.emit('exit',data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建并加入
|
||||||
|
* @param {*} message
|
||||||
|
* @param {*} params
|
||||||
|
*/
|
||||||
|
_createAndJoin(message, params){
|
||||||
|
let room = message.room;
|
||||||
|
|
||||||
|
let clientsInRoom = this.sockets.adapter.rooms[room];
|
||||||
|
let numClients = clientsInRoom ? Object.keys(clientsInRoom.sockets).length : 0;
|
||||||
|
|
||||||
|
if (numClients === 0) {
|
||||||
|
this.socket.join(room);
|
||||||
|
let createdData = {
|
||||||
|
id: this.socket.id,
|
||||||
|
room: room,
|
||||||
|
peers: [],
|
||||||
|
};
|
||||||
|
Object.assign(createdData,params['created'])
|
||||||
|
this.socket.emit('created', createdData);
|
||||||
|
|
||||||
|
}else {
|
||||||
|
let joinedData = {
|
||||||
|
id: this.socket.id,
|
||||||
|
room: room,
|
||||||
|
};
|
||||||
|
Object.assign(joinedData,params['joined'])
|
||||||
|
this.sockets.in(room).emit('joined',joinedData);
|
||||||
|
|
||||||
|
let peers = new Array();
|
||||||
|
let otherSocketIds = Object.keys(clientsInRoom.sockets);
|
||||||
|
for (let i = 0; i < otherSocketIds.length; i++) {
|
||||||
|
peers.push({
|
||||||
|
id: otherSocketIds[i]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.socket.join(room);
|
||||||
|
|
||||||
|
let createdData = {
|
||||||
|
id: this.socket.id,
|
||||||
|
room: room,
|
||||||
|
peers: peers,
|
||||||
|
};
|
||||||
|
Object.assign(createdData,params['created'])
|
||||||
|
this.socket.emit('created', createdData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 【offer】转发offer消息至room其他客户端 [from,to,room,sdp]
|
||||||
|
* @param {*} message
|
||||||
|
* @param {*} params
|
||||||
|
*/
|
||||||
|
_offer(message, params){
|
||||||
|
let otherClient = this.sockets.connected[message.to];
|
||||||
|
if (!otherClient) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
otherClient.emit('offer', message);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 【answer】转发answer消息至room其他客户端 [from,to,room,sdp]
|
||||||
|
* @param {*} message
|
||||||
|
* @param {*} params
|
||||||
|
*/
|
||||||
|
_answer(message, params){
|
||||||
|
let otherClient = this.sockets.connected[message.to];
|
||||||
|
if (!otherClient) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
otherClient.emit('answer', message);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 【candidate】转发candidate消息至room其他客户端 [from,to,room,candidate[sdpMid,sdpMLineIndex,sdp]]
|
||||||
|
* @param {*} message
|
||||||
|
* @param {*} params
|
||||||
|
*/
|
||||||
|
_candidate(message, params){
|
||||||
|
let otherClient = this.sockets.connected[message.to];
|
||||||
|
if (!otherClient){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
otherClient.emit('candidate', message);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 【exit】关闭连接转发exit消息至room其他客户端 [from,room]
|
||||||
|
* @param {*} message
|
||||||
|
* @param {*} params
|
||||||
|
*/
|
||||||
|
_exit(message, params){
|
||||||
|
let room = message.room;
|
||||||
|
|
||||||
|
this.socket.leave(room);
|
||||||
|
let clientsInRoom = this.sockets.adapter.rooms[room];
|
||||||
|
if (clientsInRoom) {
|
||||||
|
let otherSocketIds = Object.keys(clientsInRoom.sockets);
|
||||||
|
for (let i = 0; i < otherSocketIds.length; i++) {
|
||||||
|
let otherSocket = this.sockets.connected[otherSocketIds[i]];
|
||||||
|
otherSocket.emit('exit', message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_message(message){
|
||||||
|
let room = message.room;
|
||||||
|
let emitType = message.emitType;
|
||||||
|
let from = message.from;
|
||||||
|
let to = message.to;
|
||||||
|
let clientsInRoom = this.sockets.adapter.rooms[room];
|
||||||
|
if (clientsInRoom) {
|
||||||
|
let otherSocketIds = Object.keys(clientsInRoom.sockets);
|
||||||
|
for (let i = 0; i < otherSocketIds.length; i++) {
|
||||||
|
let otherSocket = this.sockets.connected[otherSocketIds[i]];
|
||||||
|
if(to && to === otherSocket.id){ //有指定发送id,不用走广播
|
||||||
|
otherSocket.emit(emitType, message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(from != otherSocket.id ){
|
||||||
|
otherSocket.emit(emitType, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
SocketHandler : SocketHandler
|
||||||
|
}
|
83
src/tables/db.js
Normal file
83
src/tables/db.js
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
const Sql = require('sequelize');
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
let sql = null;
|
||||||
|
let models = {};
|
||||||
|
|
||||||
|
|
||||||
|
//export outside
|
||||||
|
function excute(config) {
|
||||||
|
init(config);
|
||||||
|
|
||||||
|
return {
|
||||||
|
tables : models,
|
||||||
|
sql : sql,
|
||||||
|
Sql : Sql
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//main
|
||||||
|
function init(config) {
|
||||||
|
if(config.db.beforeInit){
|
||||||
|
config.db.beforeInit();
|
||||||
|
}
|
||||||
|
|
||||||
|
let dbConf = config.db.mysql;
|
||||||
|
sql = new Sql(dbConf.dbName, dbConf.user, dbConf.password, dbConf.other.sequelize);
|
||||||
|
connectDb(sql);
|
||||||
|
initTable(sql);
|
||||||
|
syncDb(sql);
|
||||||
|
|
||||||
|
if(config.db.afterInit){
|
||||||
|
config.db.afterInit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//connect to db
|
||||||
|
async function connectDb(sql){
|
||||||
|
try {
|
||||||
|
let connect = await sql.authenticate();
|
||||||
|
console.log('连接成功');
|
||||||
|
} catch (e) {
|
||||||
|
console.log('连接失败');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//init table
|
||||||
|
async function initTable() {
|
||||||
|
let files = fs.readdirSync(__dirname);
|
||||||
|
for (let f of files) {
|
||||||
|
if (f[0] == '.' || f == 'db.js') continue;
|
||||||
|
try {
|
||||||
|
let fn = require('./' + f);
|
||||||
|
if (typeof fn == 'function') {
|
||||||
|
let ms = fn(sql, Sql);
|
||||||
|
for (let k in ms) {
|
||||||
|
models[k] = ms[k];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// sync db tables
|
||||||
|
async function syncDb(sql) {
|
||||||
|
try{
|
||||||
|
let res = sql.sync({
|
||||||
|
force: false
|
||||||
|
});
|
||||||
|
console.log("同步成功");
|
||||||
|
}catch(e){
|
||||||
|
console.log("同步失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//other init methods ...
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
excute
|
||||||
|
}
|
56
src/tables/dog.js
Normal file
56
src/tables/dog.js
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
// dog
|
||||||
|
module.exports = (sequelize, DataTypes) => {
|
||||||
|
let Dog = sequelize.define('dog', {
|
||||||
|
id : {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
comment: '功能id',
|
||||||
|
primaryKey: true,
|
||||||
|
autoIncrement : true
|
||||||
|
},
|
||||||
|
name : {
|
||||||
|
type: DataTypes.STRING(20),
|
||||||
|
comment: '操作功能id'
|
||||||
|
},
|
||||||
|
module : {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
comment: '所属的模块'
|
||||||
|
},
|
||||||
|
uid : {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
comment: '添加功能的用户,属于的用户唯一id,11位,暂不处理',
|
||||||
|
unique: true
|
||||||
|
},
|
||||||
|
uname : {
|
||||||
|
type: DataTypes.STRING(20),
|
||||||
|
comment: '姓名'
|
||||||
|
},
|
||||||
|
ip : {
|
||||||
|
type: DataTypes.STRING(32),
|
||||||
|
comment: 'ip'
|
||||||
|
},
|
||||||
|
device : {
|
||||||
|
type: DataTypes.STRING(256),
|
||||||
|
comment: '设备'
|
||||||
|
},
|
||||||
|
localtion : {
|
||||||
|
type: DataTypes.STRING(256),
|
||||||
|
comment: '地理位置'
|
||||||
|
},
|
||||||
|
flag : {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
comment: '标志位',
|
||||||
|
defaultValue: 0,
|
||||||
|
},
|
||||||
|
content : {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
comment: '详细信息'
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
timestamps: true,
|
||||||
|
comment: '功能记录表'
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
Dog
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
46
src/tables/module.js
Normal file
46
src/tables/module.js
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
// module
|
||||||
|
module.exports = (sequelize, DataTypes) => {
|
||||||
|
let Module = sequelize.define('module', {
|
||||||
|
id : {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
comment: 'id',
|
||||||
|
primaryKey: true,
|
||||||
|
autoIncrement : true
|
||||||
|
},
|
||||||
|
name : {
|
||||||
|
type: DataTypes.STRING(20),
|
||||||
|
comment: '模块名称',
|
||||||
|
defaultValue: "",
|
||||||
|
},
|
||||||
|
uid : {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
comment: '添加功能的用户,属于的用户唯一id,11位,暂不处理',
|
||||||
|
unique: true,
|
||||||
|
defaultValue: 0,
|
||||||
|
},
|
||||||
|
uname : {
|
||||||
|
type: DataTypes.STRING(20),
|
||||||
|
comment: '姓名',
|
||||||
|
defaultValue: "",
|
||||||
|
},
|
||||||
|
flag : {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
comment: '标志位',
|
||||||
|
defaultValue: 0,
|
||||||
|
},
|
||||||
|
content : {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
comment: '数据content'
|
||||||
|
},
|
||||||
|
other : {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
comment: '其他数据'
|
||||||
|
},
|
||||||
|
},{
|
||||||
|
timestamps: true,
|
||||||
|
comment: '功能模块表'
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
Module
|
||||||
|
};
|
||||||
|
}
|
73
src/tables/room.js
Normal file
73
src/tables/room.js
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
// room
|
||||||
|
module.exports = (sequelize, DataTypes) => {
|
||||||
|
let Room = sequelize.define('room', {
|
||||||
|
id : {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
primaryKey: true,
|
||||||
|
autoIncrement : true,
|
||||||
|
unique: true,
|
||||||
|
comment: '数据id',
|
||||||
|
},
|
||||||
|
rcode : {
|
||||||
|
type: DataTypes.STRING(30),
|
||||||
|
comment: '房间编号'
|
||||||
|
},
|
||||||
|
rname : {
|
||||||
|
type: DataTypes.STRING(32),
|
||||||
|
comment: '房间名称-唯一'
|
||||||
|
},
|
||||||
|
uid : {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
comment: '属于的用户唯一id,11位,暂不处理',
|
||||||
|
},
|
||||||
|
uname : {
|
||||||
|
type: DataTypes.STRING(20),
|
||||||
|
comment: '姓名'
|
||||||
|
},
|
||||||
|
sid : {
|
||||||
|
type: DataTypes.STRING(30),
|
||||||
|
comment: '进入房间时的sessionId'
|
||||||
|
},
|
||||||
|
pwd: {
|
||||||
|
type: DataTypes.STRING(6),
|
||||||
|
comment: '房间密码'
|
||||||
|
},
|
||||||
|
status : {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
comment: '房间状态',
|
||||||
|
defaultValue : 0
|
||||||
|
},
|
||||||
|
ip : {
|
||||||
|
type: DataTypes.STRING(32),
|
||||||
|
comment: 'ip'
|
||||||
|
},
|
||||||
|
device : {
|
||||||
|
type: DataTypes.STRING(256),
|
||||||
|
comment: '设备'
|
||||||
|
},
|
||||||
|
localtion : {
|
||||||
|
type: DataTypes.STRING(256),
|
||||||
|
comment: '地理位置'
|
||||||
|
},
|
||||||
|
url : {
|
||||||
|
type: DataTypes.STRING(256),
|
||||||
|
comment: '请求url'
|
||||||
|
},
|
||||||
|
flag : {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
comment: '标志位',
|
||||||
|
defaultValue: 0,
|
||||||
|
},
|
||||||
|
content : {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
comment: '详细信息'
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
timestamps: true,
|
||||||
|
comment: '房主表--首个创建房间的用户和房间信息'
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
Room
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
48
src/tables/user.js
Normal file
48
src/tables/user.js
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
// user
|
||||||
|
module.exports = (sequelize, DataTypes) => {
|
||||||
|
let User = sequelize.define('user', {
|
||||||
|
id : {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
comment: '属于的用户唯一id,11位,暂不处理',
|
||||||
|
primaryKey: true,
|
||||||
|
autoIncrement : true
|
||||||
|
},
|
||||||
|
uid : {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
comment: '属于的用户唯一id,11位,暂不处理',
|
||||||
|
unique: true
|
||||||
|
},
|
||||||
|
uname : {
|
||||||
|
type: DataTypes.STRING(20),
|
||||||
|
comment: '姓名'
|
||||||
|
},
|
||||||
|
ip : {
|
||||||
|
type: DataTypes.STRING(32),
|
||||||
|
comment: 'ip'
|
||||||
|
},
|
||||||
|
device : {
|
||||||
|
type: DataTypes.STRING(256),
|
||||||
|
comment: '设备'
|
||||||
|
},
|
||||||
|
localtion : {
|
||||||
|
type: DataTypes.STRING(256),
|
||||||
|
comment: '地理位置'
|
||||||
|
},
|
||||||
|
flag : {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
comment: '标志位',
|
||||||
|
defaultValue: 0,
|
||||||
|
},
|
||||||
|
content : {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
comment: '详细信息'
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
timestamps: true,
|
||||||
|
comment: '用户表'
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
User
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
10
src/user/index.js
Normal file
10
src/user/index.js
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
const express = require('express');
|
||||||
|
const user = require("./user");
|
||||||
|
|
||||||
|
module.exports = function(){
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
router.get("/add",user.add);
|
||||||
|
|
||||||
|
return router;
|
||||||
|
}
|
10
src/user/user.js
Normal file
10
src/user/user.js
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
|
||||||
|
async function add(req,res,next) {
|
||||||
|
console.log("req : user");
|
||||||
|
res.send("user")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
add
|
||||||
|
}
|
22
src/worker/index.js
Normal file
22
src/worker/index.js
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
const Worker = require("./worker").Worker;
|
||||||
|
|
||||||
|
async function removeRoomSokect(list){
|
||||||
|
if(list == null || typeof list != 'array'){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(list.length <= 0){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let cusWorker = new Worker(5000,()=>{
|
||||||
|
for(let i = 0; i < list.length; i++){
|
||||||
|
console.log('i : ',list[i]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
cusWorker.run()
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
removeRoomSokect
|
||||||
|
}
|
104
src/worker/worker.js
Normal file
104
src/worker/worker.js
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
class Master {
|
||||||
|
constructor(maxCount = 4){
|
||||||
|
this.workers = {}; //worker数量
|
||||||
|
this.maxCount = maxCount; //最大worker数
|
||||||
|
console.log("执行master")
|
||||||
|
}
|
||||||
|
|
||||||
|
get(){
|
||||||
|
let data = {
|
||||||
|
workers : this.workers,
|
||||||
|
maxCount : this.maxCount,
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
add(type = "", worker = null){
|
||||||
|
if(Object.keys(this.workers) >= this.maxCount){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(!worker || !type){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(this.workers[type] != undefined){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.workers[type] = worker;
|
||||||
|
}
|
||||||
|
|
||||||
|
del(type = ""){
|
||||||
|
delete this.workers[type];
|
||||||
|
if(this.maxCount > 0){
|
||||||
|
this.maxCount--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
clear(){
|
||||||
|
this.workers = {};
|
||||||
|
this.maxCount = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Worker {
|
||||||
|
constructor(time = 2000, handler = null){
|
||||||
|
this.isRunning = false;
|
||||||
|
this.excuteCount = 0;
|
||||||
|
this.errorCount = 0;
|
||||||
|
|
||||||
|
this.time = time;
|
||||||
|
this.handler = handler;
|
||||||
|
this.id = 0;
|
||||||
|
console.log("执行worker")
|
||||||
|
}
|
||||||
|
|
||||||
|
get(){
|
||||||
|
let data = {
|
||||||
|
isRunning : this.isRunning,
|
||||||
|
excuteCount : this.errorCount,
|
||||||
|
errorCount : this.errorCount,
|
||||||
|
time : this.time,
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
run(){
|
||||||
|
if(!this.time || this.time <= 0){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(!this.handler){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(this.errorCount > 5){
|
||||||
|
console.log("errorCount > 5",this.errorCount)
|
||||||
|
this.stop();
|
||||||
|
}
|
||||||
|
let that = this;
|
||||||
|
function excute(){
|
||||||
|
try{
|
||||||
|
that.handler();
|
||||||
|
that.excuteCount++;
|
||||||
|
that.isRunning = true;
|
||||||
|
}catch(err){
|
||||||
|
that.errorCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.id = setInterval(excute,this.time);
|
||||||
|
}
|
||||||
|
|
||||||
|
stop(){
|
||||||
|
clearInterval(this.id);
|
||||||
|
this.id = 0;
|
||||||
|
this.time = 0;
|
||||||
|
this.handler = null;
|
||||||
|
this.isRunning = false;
|
||||||
|
this.excuteCount = 0;
|
||||||
|
this.errorCount = 0;
|
||||||
|
console.log("停止worker handler")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
Worker : Worker,
|
||||||
|
Master : Master
|
||||||
|
}
|
65
utils/request.js
Normal file
65
utils/request.js
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
const os = require('os');
|
||||||
|
|
||||||
|
function getLocalIP() {
|
||||||
|
const osType = os.type(); //系统类型
|
||||||
|
const netInfo = os.networkInterfaces(); //网络信息
|
||||||
|
let ip = '';
|
||||||
|
if (osType === 'Windows_NT') {
|
||||||
|
for (let dev in netInfo) {
|
||||||
|
//win7的网络信息中显示为本地连接,win10显示为以太网
|
||||||
|
if (dev === '本地连接' || dev === '以太网') {
|
||||||
|
for (let j = 0; j < netInfo[dev].length; j++) {
|
||||||
|
if (netInfo[dev][j].family === 'IPv4') {
|
||||||
|
ip = netInfo[dev][j].address;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (osType === 'Linux') {
|
||||||
|
for (var dev in netInfo) {
|
||||||
|
var iface = netInfo[dev];
|
||||||
|
for (var i = 0; i < iface.length; i++) {
|
||||||
|
var alias = iface[i];
|
||||||
|
if (alias.family === 'IPv4' && alias.address !== '127.0.0.1' && !alias.internal) {
|
||||||
|
return alias.address;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ip;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getClientIP(request){
|
||||||
|
var ip = request.headers['x-forwarded-for'] ||
|
||||||
|
request.ip ||
|
||||||
|
request.connection.remoteAddress ||
|
||||||
|
request.socket.remoteAddress ||
|
||||||
|
request.connection.socket.remoteAddress;
|
||||||
|
|
||||||
|
if(ip.split(',').length > 0){
|
||||||
|
ip = ip.split(',')[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
ip = ip.substr(ip.lastIndexOf(':')+1,ip.length);
|
||||||
|
return ip;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function genFlow(req) {
|
||||||
|
return num = Math.floor(Math.random(100000000)*100000000+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function genRoom(req) {
|
||||||
|
return num = Math.floor(Math.random(100000000)*100000000+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
getLocalIP,
|
||||||
|
getClientIP,
|
||||||
|
genFlow,
|
||||||
|
genRoom
|
||||||
|
}
|
Reference in New Issue
Block a user