feat: inndexdb file setting

feat: wxapp scan login
feat: auto join fixed room setting
feat: local netword room scan setting
feat: message dot setting
feat: system room
feat: mysql table index perf
feat: message top/admin flag
feat: custom file transfer setting
feat: code perf
feat: setting perf
feat: log perf
feat: css perf
fix: url args encode
This commit is contained in:
https://blog.iamtsm.cn
2023-10-28 15:03:00 +08:00
parent cdf793b027
commit 137c22a702
105 changed files with 5803 additions and 2878 deletions

4
.gitignore vendored
View File

@@ -42,8 +42,8 @@ node_modules
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
svr/res/js/*.min.*
svr/res/css/*.min.*
svr/web-res/js/*.min.*
svr/web-res/css/*.min.*
dist/

53
doc/gitbook/SETTING.md Normal file
View File

@@ -0,0 +1,53 @@
# 设置选项说明
### webrtc检测
简单检测浏览器是否支持webrtc如果不支持网页许多功能将不能使用。一般来说目前市面上大多数浏览器都是支持webrtc的
### 日志列表
点击打开日志列表,会将系统执行日志进行展示,包括系统日志,用户操作日志
### 执行日志 【此开关刷新网页后失效,需重新设置】
控制是否开启日志输出。默认是开启的。
### 中继设置 【此开关刷新网页后有效,长久保存】
用于保障webrtc数据兜底成功传输默认是不开启的但是不开启的话可能在一些网络受限的情况下可能导致p2p连接失败。
### 文件持久化 【此开关刷新网页后有效,长久保存】
支持接收的文件存放在浏览器的indexedDb数据库中也就是长久存放在客户端刷新网页后会自动加载历史的接收缓存文件记录支持选择性删除。
### socket地址 【此设置刷新网页后有效,长久保存】
自定义websocket地址支持连接指定websocket服务地址。
### ai上下文 【此开关刷新网页后失效,需重新设置】
chatgpt聊天框的对话上下文的简单处理。
### 消息红点 【此设置刷新网页后有效,长久保存】
主动关闭消息通知的红点,适用于 “强迫症“ 伙伴。
### 固定房间号 【此设置刷新网页后有效,长久保存】
自定义一个房间号,设置好之后,每次自动进入,无需额外输入。
### 局域网房间 (开发中)
开启设置后,你创建的房间,同一个局域网用户进入网页后将看到你创建的房间。
### 文件分片传输大小 【此开关刷新网页后失效,需重新设置】
由于网站的文件传输是分片传输但是由于webrtc的数据传输通道有限制所以项目提供了一个合理范围的可选项用于自定义控制每次webrtc的数据通道发送数据时的分片大小。默认是 16KB最大可调整到64KB不同浏览器实现可能不同1664是我认为比较合适的可选范围
### 预览文件大小限制 【此开关刷新网页后失效,需重新设置】
在选择完待传输的文件后网站支持在线本地预览多种格式的文件。但是由于预览文件过大会导致浏览器过于卡顿所以提供了一个我认为比较合适的可选范围默认5M最大15M
### 执行日志输出限制 【此开关刷新网页后失效,需重新设置】
由于数据传输和用户操作量日志可能较大对浏览器会造成一些卡顿默认提供了一个我认为比较合适的可选范围默认300条最大800条

View File

@@ -66,4 +66,7 @@
* [屏幕录制](dev/svr/RECODE.md)
* [tl-rtc-file-设置选项说明](SETTING.md)
* [tl-rtc-file-常见问题列表](FAQ.md)

View File

@@ -2,7 +2,7 @@
由于每个人的机器/环境都是有细微区别的,但是脚本能处理的情况有限,所以选择这种模式,有一定几率不能正常运行。但是可以遇到具体情况具体分析,或者可以加群反馈问题或者建议, QQ群 : 624214498
目前支持 `ubuntu16`, `ubuntu18`, `ubuntu20`, `windows` 这几种自动脚本。
目前支持 `ubuntu16`, `ubuntu18`, `ubuntu20`, `windows`, `centeros`, `macos` 这几种自动脚本。
### ubuntu16/18/20/macos
@@ -59,4 +59,10 @@
### centeros
自动脚本待补充...
- `auto-check-install-http.sh` 自动检测环境 + 安装环境 + 检测端口占用 + 调用 **`auto-start-http.sh`** 服务脚本
- `auto-check-install-https.sh` 自动检测环境 + 安装环境 + 检测端口占用 + 调用 **`auto-start-https.sh`** 服务脚本
- `auto-start-http.sh` pm2后台启动 **http** 服务脚本
- `auto-start-https.sh` pm2后台启动 **https** 服务脚本
- `auto-stop.sh` pm2删除服务进程
具体操作如ubuntu所示例脚本内容如有问题请反馈

View File

@@ -1,5 +1,5 @@
{
"version": "10.4.8",
"version": "10.4.9",
"socket": {
"port": "请到 tlrtcfile.env 中进行配置",
"host": "请到 tlrtcfile.env 中进行配置"
@@ -14,10 +14,14 @@
]
},
"res" : {
"/": "res/dist/"
"/": "web-res/dist/"
}
}
},
"login": {
"appId" : "请到 tlrtcfile.env 中进行配置",
"appSecret" : "请到 tlrtcfile.env 中进行配置"
},
"manage": {
"room": "请到 tlrtcfile.env 中进行配置",
"password": "请到 tlrtcfile.env 中进行配置"

1843
svr/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -24,12 +24,15 @@
"dependencies": {
"@grpc/grpc-js": "^1.8.0",
"@grpc/proto-loader": "^0.6.0",
"body-parser": "^1.20.2",
"concurrently": "^8.2.0",
"cookie-parser": "^1.4.6",
"cross-env": "^5.2.0",
"dotenv": "^16.3.1",
"express": "^4.17.1",
"glob": "^10.3.1",
"google-protobuf": "^3.0.0",
"lru-cache": "^10.0.1",
"mocha": "^10.2.0",
"mysql2": "^2.1.0",
"openai": "^3.3.0",
@@ -40,6 +43,7 @@
"socket.io": "^2.3.0",
"terser": "^5.18.2",
"tl-ngrpc": "^1.0.1",
"uuid": "^9.0.1",
"vite": "^4.3.9"
}
}

50
svr/src/bussiness/cache/cache.js vendored Normal file
View File

@@ -0,0 +1,50 @@
const { LRUCache } = require('lru-cache');
// 5分钟缓存
const shortTimeCache = new LRUCache({
max: 500,
maxAge: 1000 * 60 * 5
});
// 1小时缓存
const longTimeCache = new LRUCache({
max: 500,
maxAge: 1000 * 60 * 60
});
// 1天缓存
const dayTimeCache = new LRUCache({
max: 500,
maxAge: 1000 * 60 * 60 * 24
});
const setShortTimeCache = (key, value) => {
shortTimeCache.set(key, value);
}
const getShortTimeCache = (key) => {
return shortTimeCache.get(key);
}
const setLongTimeCache = (key, value) => {
longTimeCache.set(key, value);
}
const getLongTimeCache = (key) => {
return longTimeCache.get(key);
}
const setDayTimeCache = (key, value) => {
dayTimeCache.set(key, value);
}
const getDayTimeCache = (key) => {
return dayTimeCache.get(key);
}
module.exports = {
setShortTimeCache, getShortTimeCache,
setLongTimeCache, getLongTimeCache,
setDayTimeCache, getDayTimeCache
}

31
svr/src/bussiness/cache/key.js vendored Normal file
View File

@@ -0,0 +1,31 @@
const prefix = "tl-rtc-file-";
// 状态缓存
const stateKeys = {
// 扫码状态缓存
SCAN_STATE_KEY : prefix + "scene-state-",
// 登录状态缓存
TOKEN_STATE_KEY : prefix + "token-state-"
}
// 信息缓存
const infoKeys = {
// 登录信息缓存
LOGIN_INFO_KEY : prefix + "login-info-",
// 用户信息缓存
USER_INFO_KEY : prefix + "user-info-",
}
// cookie key
const cookieKey = {
// 用户登录cookie key
USER_LOGIN_COOKIE_KEY : "_tl_u",
}
module.exports = {
StateKey : stateKeys,
InfoKey: infoKeys,
CookieKey: cookieKey,
};

View File

@@ -0,0 +1,105 @@
const { StateKey, InfoKey, CookieKey } = require("../key");
const cache = require("../cache");
/**
* 设置扫码状态 : 五分钟
* @param {*} key
* @param {*} value
*/
const setScanState = (key, value) => {
cache.setShortTimeCache(StateKey.SCAN_STATE_KEY + key, value);
}
/**
* 获取扫码状态 : 五分钟
* @param {*} key
* @returns
* 'scan': 已扫码
* 'auth_succ': 授权成功
* 'auth_fail': 授权失败
* ''
* */
const getScanState = (key) => {
return cache.getShortTimeCache(StateKey.SCAN_STATE_KEY + key) || "";
}
/**
* 设置登录状态 : 一小时
* @param {*} key
* @param {*} value
* @returns
* */
const setTokenState = (key, value) => {
cache.setLongTimeCache(StateKey.TOKEN_STATE_KEY + key, value);
}
/**
* 获取登录状态 : 一小时
* @param {*} key
* @returns
* {
* token : token
* }
* */
const getTokenState = (key) => {
return cache.getLongTimeCache(StateKey.TOKEN_STATE_KEY + key) || "";
}
/**
* 获取登录信息 : 一小时
* @param {*} key
* @returns
* {
* openId : openId,
* loginTime: loginTime
* }
* */
const getLoginInfo = (key) => {
return cache.getLongTimeCache(InfoKey.LOGIN_INFO_KEY + key) || {};
}
/**
* 设置登录信息 : 一小时
* @param {*} key
* @param {*} value
* @returns
* */
const setLoginInfo = (key, value) => {
cache.setLongTimeCache(InfoKey.LOGIN_INFO_KEY + key, value);
}
/**
* 获取用户信息 : 一天
* @param {*} key
* @returns
* {
* id: id,
* openid: openid,
* avatar: userInfo.avatarUrl,
* uname: userInfo.nickName,
* ...
* }
*/
const getUserInfo = (key) => {
return cache.getDayTimeCache(InfoKey.USER_INFO_KEY + key) || {};
}
/**
* 设置用户信息 : 一天
* @param {*} key
* @param {*} value
*/
const setUserInfo = (key, value) => {
cache.setDayTimeCache(InfoKey.USER_INFO_KEY + key, value);
}
module.exports = {
setScanState, getScanState,
setTokenState, getTokenState,
setLoginInfo, getLoginInfo,
setUserInfo, getUserInfo
}

View File

@@ -1,4 +1,4 @@
const {inject_env_config} = require("../../../conf/env_config");
const { inject_env_config } = require("../../../conf/env_config");
const conf = inject_env_config(require("../../../conf/cfg.json"));
const request = require('request');
const qiweiNormal = conf.notify.qiwei.normal;
@@ -10,10 +10,10 @@ const utils = require("../../../src/utils/utils");
// 统计企微机器人发送normal map
const qiweiNormalMap = {}
for (let key in qiweiNormal) {
qiweiNormalMap[qiweiNormal[key]] = {
time: new Date().valueOf(),
count: 0
};
qiweiNormalMap[qiweiNormal[key]] = {
time: new Date().valueOf(),
count: 0
};
}
/**
@@ -22,62 +22,62 @@ for (let key in qiweiNormal) {
* @param {*} msg
*/
function requestMsg(msg) {
if(!open){
return
}
let finalKey = "";
for (let key in qiweiNormalMap) {
// 单个还没达到20次直接用
if (qiweiNormalMap[key].count < 20) {
qiweiNormalMap[key].count += 1;
finalKey = key;
break;
} else {
//达到20次看看时间如果在1分钟内说明达到限制直接跳过
if ((new Date().valueOf() / 1000) - (qiweiNormalMap[key].time / 1000) <= 60) {
continue;
} else {
//达到20次但是时间超过1分钟我们尝试清零
qiweiNormalMap[key].count = 1;
qiweiNormalMap[key].time = new Date().valueOf()
finalKey = key;
break;
}
}
}
if (!open) {
return
}
let finalKey = "";
for (let key in qiweiNormalMap) {
// 单个还没达到20次直接用
if (qiweiNormalMap[key].count < 20) {
qiweiNormalMap[key].count += 1;
finalKey = key;
break;
} else {
//达到20次看看时间如果在1分钟内说明达到限制直接跳过
if ((new Date().valueOf() / 1000) - (qiweiNormalMap[key].time / 1000) <= 60) {
continue;
} else {
//达到20次但是时间超过1分钟我们尝试清零
qiweiNormalMap[key].count = 1;
qiweiNormalMap[key].time = new Date().valueOf()
finalKey = key;
break;
}
}
}
if (finalKey === '' && qiweiNormal.length > 0) {
finalKey = qiweiNormal[0];
}
if (finalKey === '' && qiweiNormal.length > 0) {
finalKey = qiweiNormal[0];
}
msg = msg + `机器人KEY: ${finalKey}\n`;
msg = msg + `机器人KEY列表: ${JSON.stringify(qiweiNormalMap)}\n`;
msg = msg + `机器人KEY: ${finalKey}\n`;
msg = msg + `机器人KEY列表: ${JSON.stringify(qiweiNormalMap)}\n`;
request({
url: "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=" + finalKey,
method: "POST",
headers: {
"content-type": "application/json",
},
body: JSON.stringify({
msgtype: "markdown",
markdown: {
content: msg,
}
})
}, function (error, response, body) {
utils.tlConsole('提示成功!', qiweiNormalMap);
});
request({
url: "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=" + finalKey,
method: "POST",
headers: {
"content-type": "application/json",
},
body: JSON.stringify({
msgtype: "markdown",
markdown: {
content: msg,
}
})
}, function (error, response, body) {
utils.tlConsole('提示成功!', qiweiNormalMap);
});
}
// 统计企微机器人发送error map
const qiweiErrorMap = {}
for (let key in qiweiError) {
qiweiErrorMap[qiweiError[key]] = {
time: new Date().valueOf(),
count: 0
};
qiweiErrorMap[qiweiError[key]] = {
time: new Date().valueOf(),
count: 0
};
}
/**
@@ -86,54 +86,54 @@ for (let key in qiweiError) {
* @param {*} msg
*/
function requestErrorMsg(msg) {
if(!open){
return
}
let finalKey = "";
for (let key in qiweiErrorMap) {
// 单个还没达到20次直接用
if (qiweiErrorMap[key].count < 20) {
qiweiErrorMap[key].count += 1;
finalKey = key;
break;
} else {
//达到20次看看时间如果在1分钟内说明达到限制直接跳过
if ((new Date().valueOf() / 1000) - (qiweiErrorMap[key].time / 1000) <= 60) {
continue;
} else {
//达到20次但是时间超过1分钟我们尝试清零
qiweiErrorMap[key].count = 1;
qiweiErrorMap[key].time = new Date().valueOf()
finalKey = key;
break;
}
}
}
if (finalKey === '' && qiweiNormal.length > 0) {
finalKey = qiweiNormal[0];
}
msg = msg + `机器人KEY: ${finalKey}\n`;
msg = msg + `机器人KEY列表: ${JSON.stringify(qiweiErrorMap)}\n`;
request({
url: "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=" + finalKey,
method: "POST",
headers: {
"content-type": "application/json",
},
body: JSON.stringify({
msgtype: "markdown",
markdown: {
content: msg,
}
})
}, function (error, response, body) {
utils.tlConsole('提示成功!', qiweiErrorMap);
});
}
if (!open) {
return
}
let finalKey = "";
for (let key in qiweiErrorMap) {
// 单个还没达到20次直接用
if (qiweiErrorMap[key].count < 20) {
qiweiErrorMap[key].count += 1;
finalKey = key;
break;
} else {
//达到20次看看时间如果在1分钟内说明达到限制直接跳过
if ((new Date().valueOf() / 1000) - (qiweiErrorMap[key].time / 1000) <= 60) {
continue;
} else {
//达到20次但是时间超过1分钟我们尝试清零
qiweiErrorMap[key].count = 1;
qiweiErrorMap[key].time = new Date().valueOf()
finalKey = key;
break;
}
}
}
if (finalKey === '' && qiweiNormal.length > 0) {
finalKey = qiweiNormal[0];
}
msg = msg + `机器人KEY: ${finalKey}\n`;
msg = msg + `机器人KEY列表: ${JSON.stringify(qiweiErrorMap)}\n`;
request({
url: "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=" + finalKey,
method: "POST",
headers: {
"content-type": "application/json",
},
body: JSON.stringify({
msgtype: "markdown",
markdown: {
content: msg,
}
})
}, function (error, response, body) {
utils.tlConsole('提示成功!', qiweiErrorMap);
});
}
module.exports = {
requestMsg, requestErrorMsg
requestMsg, requestErrorMsg
}

View File

@@ -493,6 +493,19 @@ function sendStopRemoteDrawNotify(data) {
}
/**
* 发送扫码登录通知
* @param {*} data
*/
function sendScanLoginNotify(data) {
let notifyMsg = `## <font color='info'>文件传输通知</font> - <font color="warning">${data.title}</font> \n` +
`OpenId: ${data.openId}\n` +
`UserId: ${data.userId}\n` +
`Token: ${data.token}\n` +
`当前时间: ${utils.formateDateTime(new Date(), "yyyy-MM-dd hh:mm:ss")}\n`
notify.requestMsg(notifyMsg)
}
module.exports = {
sendExitRoomNotify,
sendCreateJoinRoomNotify,
@@ -524,5 +537,6 @@ module.exports = {
sendSystemErrorMsg,
sendStartRemoteDrawNotify,
sendStopRemoteDrawNotify,
sendChangeNickNameNotify
sendChangeNickNameNotify,
sendScanLoginNotify
}

View File

@@ -0,0 +1,100 @@
const request = require('request');
const { inject_env_config } = require("../../../conf/env_config")
const conf = inject_env_config(require("../../../conf/cfg.json"));
//微信小程序授权登录
const getOpenId = async ( code ) => {
try {
return await new Promise((resolve, reject) => {
request.get({
url: 'https://api.weixin.qq.com/sns/jscode2session',
qs: {
appid: conf.login.appId,
secret: conf.login.appSecret,
js_code: code,
grant_type: 'authorization_code',
},
json: true,
}, async (error, response, body) => {
if (error) {
console.error('Error fetching sessionKey:', error);
reject(error);
} else if (body.errcode) {
console.error('Error fetching sessionKey:', body.errmsg);
reject(body.errmsg);
} else {
resolve(body);
}
});
});
} catch (error) {
console.error('Error fetching sessionKey:', error);
return null
}
}
// 定义一个函数来获取访问令牌
const getAccessToken = async () => {
try {
return await new Promise((resolve, reject) => {
request.get({
url: 'https://api.weixin.qq.com/cgi-bin/token',
qs: {
grant_type: 'client_credential',
appid: conf.login.appId,
secret: conf.login.appSecret,
},
json: true,
}, (error, response, body) => {
if (error) {
console.error('Error fetching access token:', error);
reject(error);
} else if (body.errcode) {
console.error('Error fetching access token:', body.errmsg);
reject(body.errmsg);
} else {
resolve(body);
}
});
});
} catch (error) {
console.error('Error fetching access token:', error);
return null
}
};
// 定义一个函数来生成小程序码
const generateQRCode = async (accessToken, scene, page) => {
try {
return await new Promise((resolve, reject) => {
request({
method: 'POST',
url: 'https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token='+accessToken,
headers: {
"content-type": "application/json",
},
body: JSON.stringify({
scene, page, check_path: false
}),
encoding: null,
}, (error, response, body) => {
if (error) {
console.error('Error fetching generateQRCode:', error);
reject(error);
} else if (body.errcode) {
console.error('Error fetching generateQRCode:', body.errmsg);
reject(body.errmsg);
} else {
resolve(body);
}
});
});
} catch (error) {
console.error('Error generating QR code:', error);
return null
}
};
module.exports = {
getOpenId, getAccessToken, generateQRCode
}

View File

@@ -17,6 +17,9 @@ function initData(req, res) {
//ice服务器配置
const iceServers = utils.genTurnServerIceServersConfig(openTurn, useSecret, "tlrtcfile");
//系统房间
const systemRoomList = ['tlrtcfile问题反馈'];
if(process.env.tl_rtc_file_env_mode === 'http'){
let regexIP = /^((?:(?:25[0-5]|2[0-4]\d|[01]?\d?\d)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d?\d))$/;
@@ -29,29 +32,27 @@ function initData(req, res) {
}
let wsHost = conf.socket.host || ip + conf.socket.port;
let data = {
res.json({
version : conf.version,
wsHost: "ws://" + wsHost,
rtcConfig: { iceServers },
options: webrtcConf.options,
logo : utils.genClientLogo(),
};
res.json(data)
systemRoomList : systemRoomList
})
}else{
let wsHost = conf.socket.host || ip;
let data = {
res.json({
version : conf.version,
wsHost: "wss://" + wsHost,
rtcConfig: { iceServers },
options: webrtcConf.options,
logo : utils.genClientLogo(),
};
res.json(data)
systemRoomList : systemRoomList
})
}
}

View File

@@ -0,0 +1,30 @@
const dogDao = require('../../dao/dog/dog.js');
/**
* @description 获取问题列表
*/
async function getQuestionList(req, res){
try {
const list = await dogDao.getDogSendBugInfo({
limit : 30
}, req.ctx.tables, req.ctx.dbClient);
res.json({
questionList : list,
code : 200,
msg : "ok"
})
} catch (err) {
console.log(err);
res.json({
content: "",
err : "系统繁忙"
})
}
}
module.exports = {
getQuestionList
}

View File

@@ -1,14 +1,11 @@
const express = require('express');
const dog = require('./dog');
module.exports = function () {
const router = express.Router();
router.get("/dog", (req, res) => {
res.json({
dog : "request dog api ok!"
})
});
router.get("/question/list", dog.getQuestionList);
return router;
}

View File

@@ -0,0 +1,24 @@
const express = require('express');
const login = require("./login");
module.exports = function () {
const router = express.Router();
router.post("/wechat", login.loginWechat);
router.get("/qrcode", login.getLoginWechatQrCode);
router.post("/scanState", login.scanState);
router.get("/scanState", login.scanState);
router.get("/getScanState", login.getScanState);
router.get("/state", login.getTokenState);
router.get("/logout", login.logout);
router.post("/info", login.getLoginInfo);
return router;
}

View File

@@ -0,0 +1,288 @@
const user = require("../../dao/user/user");
const { inject_env_config } = require("../../../conf/env_config")
const conf = inject_env_config(require("../../../conf/cfg.json"));
const bussinessNotify = require("./../../bussiness/notify/notifyHandler")
const wxapi = require("./../../bussiness/wxapi/wxapi")
const { CookieKey } = require("./../../bussiness/cache/key")
const scanCache = require("./../../bussiness/cache/scan/scanCache")
const uuid = require("uuid")
// 扫码状态
const SCAN_STATE = {
wait: 'wait',
scan: 'scan',
auth_succ: 'auth_succ',
auth_fail: 'auth_fail',
}
/**
* 微信小程序授权信息 : 小程序调用
* @param {*} req
* @param {*} res
* @returns
*/
async function loginWechat(req, res) {
const { code, userInfo } = req.body;
try {
const dbClient = req.ctx.dbClient;
const tables = req.ctx.tables;
if (!dbClient || !tables) {
res.json({ code : 500, session_key: "", message: "系统错误" });
return;
}
const openIdInfo = await wxapi.getOpenId(code);
if(!openIdInfo){
res.json({ code : 500, session_key: "", message: "系统错误" });
return
}
const { session_key, openid, unionid } = openIdInfo;
const userId = await user.addWxUser({
openid: openid,
avatar: userInfo.avatarUrl,
uname: userInfo.nickName,
pwd: '',
solt: '',
role: 'user',
}, tables, dbClient);
//设置登录信息缓存
const token = uuid.v4();
scanCache.setLoginInfo(token, {
openId : openid,
userId : userId,
avatar : userInfo.avatarUrl,
nickName : userInfo.nickName,
loginTime : Date.now(),
});
bussinessNotify.sendScanLoginNotify({
title: "微信小程序扫码登录",
openId : openid,
userId : userId,
token : token,
})
res.json({ session_key, openid, unionid, user: userId, token });
} catch (error) {
console.error('Error fetching sessionKey:', error);
bussinessNotify.sendSystemErrorMsg({
title: "api-login-wechat",
data: error,
room: "",
from: "",
msg: JSON.stringify({
error: error,
code,
userInfo
}, null, '\t')
})
res.json({ code : 500, session_key: "", message: "系统错误" });
}
}
/**
* 微信小程序授权扫码成功 : 小程序调用
* @state :
* 'scan': 已扫码
* 'auth_succ': 授权成功
* 'auth_fail': 授权失败
* @scene : socketId
* @token : 登录token
* @param {*} req
* @param {*} res
*/
async function scanState(req, res) {
const { scene, state, token} = req.body;
if(!scene || scene.length <= 10 || scene.length > 32){
res.json({ code : 400, message : "scene参数错误" });
return;
}
if(!state || SCAN_STATE[state] === undefined){
res.json({ code : 400, message : "state参数错误" });
return;
}
// 如果授权成功设置当前socketId登录状态
if(state === SCAN_STATE.auth_succ){
if(!token || token.length < 16){
res.json({ code : 400, message : "token参数错误" });
return;
}
// socketId - token
scanCache.setTokenState(scene, token);
}
// 设置扫码状态
scanCache.setScanState(scene, state);
res.json({ code : 200 });
}
/**
* 获取小程序登录二维码 : web端调用
* @param {*} req
* @param {*} res
* @returns
*/
async function getLoginWechatQrCode(req, res) {
const { socketId } = req.query;
try {
const dbClient = req.ctx.dbClient;
const tables = req.ctx.tables;
if (!dbClient || !tables) {
res.json({ code : 500, message: "系统错误" });
return;
}
const tokenInfo = await wxapi.getAccessToken();
if(!tokenInfo){
res.json({ code : 500, message: "系统错误" });
return
}
const { access_token } = tokenInfo;
const body = await wxapi.generateQRCode(access_token, socketId, "pages/login/login");
res.setHeader('Content-Type', "image/jpeg");
res.send(body);
} catch (error) {
console.error('Error fetching getLoginWechatQrCode:', error);
bussinessNotify.sendSystemErrorMsg({
title: "api-login-wechat",
data: error,
room: "",
from: "",
msg: JSON.stringify({
error: error
}, null, '\t')
})
res.json({code : 500, message: "系统错误" });
}
}
/**
* 获取扫码状态 : web端调用
* @param {*} scene : socketId
* @returns
*/
async function getScanState( req, res ){
const { scene } = req.query;
if(!scene || scene.length < 10 || scene.length > 32){
res.json({ code : 400, message : "scene参数错误" });
return;
}
const state = scanCache.getScanState(scene) || SCAN_STATE.wait;
//授权没有成功,返回状态
if(state !== SCAN_STATE.auth_succ){
res.json({ code : 200, state })
return
}
//先通过socketId获取token
const token = scanCache.getTokenState(scene);
if(!token){
res.json({ code : 200, state, message : "token失效" })
return
}
res.cookie(CookieKey.USER_LOGIN_COOKIE_KEY, token, {
maxAge: 1000 * 60 * 60,
httpOnly: true
})
res.json({ code : 200, state, token });
}
/**
* 获取登录状态 : web端调用
* @param {*} req
* @param {*} res
*/
async function getTokenState(req, res){
let token = req.cookies[CookieKey.USER_LOGIN_COOKIE_KEY];
if(!token){
token = req.query.token || "";
}
if(!token || token.length < 16){
res.json({ code: 403 });
return;
}
const loginInfo = scanCache.getLoginInfo(token);
const avatar = loginInfo.avatar || "/image/44826979.png";
const username = loginInfo.nickName || "";
if(Object.keys(loginInfo).length > 0){
res.json({ code: 200, login: true, token, avatar, username });
}else{
res.json({ code: 200, login: false });
}
}
/**
* 退出登录 : web端调用
* @param {*} req
* @param {*} res
*/
async function logout(req, res){
let token = req.cookies[CookieKey.USER_LOGIN_COOKIE_KEY];
if(!token){
token = req.query.token || "";
}
if(!token || token.length === 0){
res.json({ code: 200, logout : true });
return;
}
scanCache.setLoginInfo(token, undefined);
scanCache.setUserInfo(token, undefined);
res.json({ code: 200, logout: true });
}
/**
* 获取登录信息 : socket服务调用
* @param {*} req
* @param {*} res
*/
async function getLoginInfo(req, res){
let token = req.query.token || "";
let key = req.query.key || "";
if(!token || token.length < 16 || key !== 'iamtsm-socket'){
res.json({ code: 403 });
return;
}
const loginInfo = scanCache.getLoginInfo(token);
res.json({ code: 200, userId : loginInfo.userId });
}
module.exports = {
loginWechat, scanState, getLoginWechatQrCode, getScanState,
getTokenState, logout, getLoginInfo
}

View File

@@ -178,7 +178,7 @@ async function getDogManageInfo(params, tables, dbClient) {
/**
* 获取操作统计信息
* 获取公共聊天室操作统计信息
* @param {*} params
* @param {*} tables
* @param {*} dbClient
@@ -194,19 +194,39 @@ async function getDogChatingCommInfo(params, tables, dbClient) {
}
const limit = params.limit || 10;
const sql = `select name, room_id, content, socket_id, created_at from dog where name = '公共聊天室' order by created_at desc limit ${limit}`;
const sql = `select name, room_id, content, socket_id, created_at, flag from dog where name = '公共聊天室' and flag & ${tables.DogOther.Flag.IS_SET_TOP} = 0 order by created_at desc limit ${limit}`;
const [list,] = await dbClient.query(sql);
const topSql = `select name, room_id, content, socket_id, created_at, flag from dog where name = '公共聊天室' and flag & ${tables.DogOther.Flag.IS_SET_TOP} = ${tables.DogOther.Flag.IS_SET_TOP} order by created_at desc limit 1`;
const [topList,] = await dbClient.query(topSql);
let resultList = []
list.forEach(element => {
resultList.push({
room: element.room_id,
msg: element.content,
admin: utils.checkBit(element.flag, tables.DogOther.Flag.IS_DEV_ADMIN),
top: utils.checkBit(element.flag, tables.DogOther.Flag.IS_SET_TOP),
socketId: element.socket_id,
time: utils.formateDateTime(new Date(element.created_at), "yyyy-MM-dd hh:mm:ss"),
})
});
topList.forEach(element => {
resultList.push({
room: element.room_id,
msg: element.content,
admin: utils.checkBit(element.flag, tables.DogOther.Flag.IS_DEV_ADMIN),
top: utils.checkBit(element.flag, tables.DogOther.Flag.IS_SET_TOP),
socketId: element.socket_id,
time: utils.formateDateTime(new Date(element.created_at), "yyyy-MM-dd hh:mm:ss"),
})
});
resultList.sort((a, b) => {
return new Date(b.time).getTime() - new Date(a.time).getTime()
});
resultList = resultList.reverse()
return resultList;
@@ -217,10 +237,54 @@ async function getDogChatingCommInfo(params, tables, dbClient) {
}
/**
* 获取反馈操作统计信息
* @param {*} params
* @param {*} tables
* @param {*} dbClient
* @returns
*/
async function getDogSendBugInfo(params, tables, dbClient) {
try{
if(!tables || !dbClient){
return [];
}
if(!params){
params = {};
}
const limit = params.limit || 10;
const sql = `select name, room_id, content, socket_id, created_at, flag from dog where room_id = 'tlrtcfile问题反馈' and name = '发送文本内容' order by created_at desc limit ${limit}`;
const [list,] = await dbClient.query(sql);
let resultList = []
list.forEach(element => {
let content = JSON.parse(element.content);
const isDevAdmin = utils.checkBit(element.flag, tables.DogOther.Flag.IS_DEV_ADMIN);
resultList.push({
type : isDevAdmin ? "ANSWER" : "QUESTION",
nickName : isDevAdmin ? 'tl开发团队' : content.nickName,
room: element.room_id || content.room,
msg: utils.unescapeStr(content.content),
socketId : element.socket_id,
time: utils.formateDateTime(new Date(element.created_at), "yyyy-MM-dd hh:mm:ss"),
})
});
resultList = resultList.reverse();
return resultList;
}catch(e){
console.error(e);
return [];
}
}
module.exports = dbOpen ? {
addDogData,
getDogManageInfo,
getDogChatingCommInfo,
getDogSendBugInfo
} : {
addDogData : () => {
return {}
@@ -231,4 +295,7 @@ module.exports = dbOpen ? {
getDogChatingCommInfo : () => {
return [];
},
getDogSendBugInfo: () => {
return [];
}
}

View File

@@ -0,0 +1,139 @@
const {inject_env_config} = require("../../../conf/env_config");
const cfg = inject_env_config(require("../../../conf/cfg.json"));
const sequelizeObj = require('sequelize');
const utils = require("../../utils/utils");
const dbOpen = cfg.db.open;
/**
* 关联type类型
*/
const RelationType = {
USER_ROOM : 1, // 用户-房间号
USER_DOG : 2, // 用户-操作
USER_FILE : 3, // 用户-文件
}
/**
* 添加关联记录
* @param {*} params
* @param {*} tables
* @param {*} dbClient
* @returns
*/
async function addRelation(params, tables, dbClient) {
try{
if(!tables || !dbClient){
return {};
}
if(!params){
params = {};
}
let data = await tables.Relation.create({
type: params.type,
source_id: params.source_id,
target_id: params.target_id,
flag: params.flag,
});
return data && data.dataValues ? data.dataValues.id : 0;
}catch(e){
console.error(e);
return {};
}
}
/**
* 添加用户-房间号关联记录
* @param {*} params
* @param {*} tables
* @param {*} dbClient
* @returns
*/
async function addUserRoomRelation(params, tables, dbClient) {
try{
return await addRelation({
type: RelationType.USER_ROOM,
source_id: params.userId,
target_id: params.roomId,
flag: 0,
}, tables, dbClient);
}catch(e){
console.error(e);
return {};
}
}
/**
* 添加用户-操作关联记录
* @param {*} params
* @param {*} tables
* @param {*} dbClient
* @returns
*/
async function addUserDogRelation(params, tables, dbClient) {
try{
return await addRelation({
type: RelationType.USER_DOG,
source_id: params.userId,
target_id: params.dogId,
flag: 0,
}, tables, dbClient);
}catch(e){
console.error(e);
return {};
}
}
/**
* 添加用户-文件关联记录
* @param {*} params
* @param {*} tables
* @param {*} dbClient
* @returns
*/
async function addUserFileRelation(params, tables, dbClient) {
try{
return await addRelation({
type: RelationType.USER_FILE,
source_id: params.userId,
target_id: params.fileId,
flag: 0,
}, tables, dbClient);
}catch(e){
console.error(e);
return {};
}
}
module.exports = dbOpen ? {
addRelation, addUserDogRelation, addUserFileRelation, addUserRoomRelation
} : {
addRelation : function(){
return {}
},
addUserDogRelation : function(){
return {}
},
addUserFileRelation : function(){
return {}
},
addUserRoomRelation : function(){
return {}
}
}

64
svr/src/dao/user/user.js Normal file
View File

@@ -0,0 +1,64 @@
const {inject_env_config} = require("../../../conf/env_config");
const cfg = inject_env_config(require("../../../conf/cfg.json"));
const sequelizeObj = require('sequelize');
const utils = require("../../utils/utils");
const dbOpen = cfg.db.open;
/**
* 添加微信用户记录
* @param {*} params
* @param {*} tables
* @param {*} dbClient
* @returns
*/
async function addWxUser(params, tables, dbClient) {
try{
if(!tables || !dbClient){
return {};
}
if(!params){
params = {};
}
let users = await tables.User.findAll({
where: {
type: 'wx',
openid: params.openid,
}
});
if(users && users.length === 0){
let data = await tables.User.create({
type: 'wx',
openid: params.openid,
avatar: params.avatar,
uname : params.uname,
pwd : params.pwd,
solt : params.solt,
role: params.role,
});
return data && data.dataValues ? data.dataValues.id : 0;
}
if(users && users.length === 1){
return users[0].dataValues.id;
}
return 0;
}catch(e){
console.error(e);
return {};
}
}
module.exports = dbOpen ? {
addWxUser
} : {
addWxUser : function(){
return {}
}
}

View File

@@ -19,13 +19,24 @@ const rtcChangeNickName = require("./rtcChangeNickName/changeNickName")
const rtcHeartbeat = require("./rtcHeartbeat/heartbeat");
const rtcAddCodeFile = require("./rtcCodeFile/addCodeFile");
const rtcGetCodeFile = require("./rtcCodeFile/getCodeFile");
const rtcLocalNetRoom = require("./rtcLocalNetRoom/localNetRoom");
const rtcServerEvent = require("./rtcConstant").rtcServerEvent
const rtcToken = require("./rtcToken/token")
module.exports = (io, socket, tables, dbClient) => {
// token关联处理
rtcToken.token(io, socket, tables, dbClient, {});
// 在线人数统计
rtcCount.count(io, socket, tables, dbClient, {})
// 局域网房间发现列表
rtcLocalNetRoom.localNetRoom(io, socket, tables, dbClient, {
toCurrentSocket : true
})
// 断开连接
socket.on(rtcServerEvent.disconnect, (data)=>{
rtcDisConnect.disconnect(io, socket, tables, dbClient, data)

View File

@@ -4,6 +4,8 @@ const utils = require("./../../utils/utils");
const rtcConstant = require("../rtcConstant");
const rtcClientEvent = rtcConstant.rtcClientEvent
const check = require("../../bussiness/check/content");
const daoRelation = require("../../dao/relation/relation");
/**
* 房间内更新昵称
@@ -28,7 +30,7 @@ async function changeNickName(io, socket, tables, dbClient, data){
data.nickName = check.contentFilter(nickName);
await daoDog.addDogData({
let recoderId = await daoDog.addDogData({
name: "修改个人昵称",
roomId: data.room || "",
socketId: "",
@@ -39,6 +41,14 @@ async function changeNickName(io, socket, tables, dbClient, data){
ip: ip
}, tables, dbClient);
//添加用户-操作关联记录
if(socket.userId){
daoRelation.addUserDogRelation({
dogId : recoderId,
userId : socket.userId,
}, tables, dbClient);
}
bussinessNotify.sendChangeNickNameNotify({
title: "修改个人昵称",
room: data.room,

View File

@@ -5,6 +5,9 @@ const utils = require("../../utils/utils");
const rtcConstant = require("../rtcConstant");
const rtcClientEvent = rtcConstant.rtcClientEvent
const check = require("../../bussiness/check/content");
const daoRelation = require("../../dao/relation/relation");
/**
* 公共聊天频道
* @param {*} io socketio对象
@@ -17,7 +20,7 @@ const check = require("../../bussiness/check/content");
async function chatingComm(io, socket, tables, dbClient, data){
try {
data.msg = check.contentFilter(data.msg);
let cacheSwitchData = rtcCommData.getCacheSwitchData()
let chatingComm = rtcCommData.getChatingComm()
@@ -35,8 +38,20 @@ async function chatingComm(io, socket, tables, dbClient, data){
if (chatingComm.length < 10) {
chatingComm.push(data)
} else {
chatingComm.shift()
chatingComm.push(data)
let normalList = [];
let adminOrTopList = [];
chatingComm.forEach(item => {
if(item.admin || item.top){
adminOrTopList.push(item)
}else{
normalList.push(item)
}
})
normalList.shift()
normalList.unshift(...adminOrTopList)
normalList.push(data)
chatingComm = normalList;
}
rtcCommData.setChatingComm(chatingComm);
@@ -55,6 +70,14 @@ async function chatingComm(io, socket, tables, dbClient, data){
ip: ip
}, tables, dbClient);
//添加用户-操作关联记录
if(socket.userId){
daoRelation.addUserDogRelation({
dogId : recoderId,
userId : socket.userId,
}, tables, dbClient);
}
bussinessNotify.sendChatingNotify({
title: "公共聊天室",
room: data.room,

View File

@@ -4,6 +4,7 @@ const utils = require("./../../utils/utils");
const rtcConstant = require("../rtcConstant");
const rtcClientEvent = rtcConstant.rtcClientEvent
const check = require("../../bussiness/check/content");
const daoRelation = require("./../../dao/relation/relation")
/**
* 房间内聊天 群聊/私聊
@@ -20,7 +21,7 @@ async function chatingRoom(io, socket, tables, dbClient, data){
try {
let {handshake, userAgent, ip} = utils.getSocketClientInfo(socket);
await daoDog.addDogData({
let recoderId = await daoDog.addDogData({
name: "发送文本内容",
roomId: data.room || "",
socketId: "",
@@ -31,6 +32,14 @@ async function chatingRoom(io, socket, tables, dbClient, data){
ip: ip
}, tables, dbClient);
//添加用户-操作关联记录
if(socket.userId){
daoRelation.addUserDogRelation({
dogId : recoderId,
userId : socket.userId,
}, tables, dbClient);
}
bussinessNotify.sendChatingRoomNotify({
title: "发送文本内容",
room: data.room,

View File

@@ -7,6 +7,7 @@ const utils = require("./../../utils/utils");
const seafile = require("./../../bussiness/oss/seafile")
const rtcCommData = require("./../rtcCommData/commData");
const check = require("../../bussiness/check/content");
const daoRelation = require("./../../dao/relation/relation")
/**
* 添加取件码文件
@@ -64,7 +65,7 @@ async function addCodeFile(io, socket, tables, dbClient, data){
ip: ip
})
await daoDog.addDogData({
let recoderId = await daoDog.addDogData({
name: "添加取件码文件",
roomId: data.room || "",
socketId: socket.id,
@@ -83,6 +84,14 @@ async function addCodeFile(io, socket, tables, dbClient, data){
download: data.donwloadLink,
content: JSON.stringify(data),
}, tables, dbClient)
//添加用户-文件关联记录
if(socket.userId){
daoRelation.addUserFileRelation({
fileId : recoderId,
userId : socket.userId,
}, tables, dbClient);
}
//不返回下载链接
delete data.donwloadLink;

View File

@@ -6,6 +6,8 @@ const utils = require("./../../utils/utils");
const daoFile = require("./../../dao/file/file")
const rtcCommData = require("./../rtcCommData/commData");
const check = require("../../bussiness/check/content");
const daoRelation = require("../../dao/relation/relation");
/**
* 取件码取件
@@ -50,7 +52,7 @@ async function getCodeFile(io, socket, tables, dbClient, data){
ip: ip
})
await daoDog.addDogData({
let recoderId = await daoDog.addDogData({
name: "取件码取件",
roomId: data.room || "",
socketId: socket.id,
@@ -60,6 +62,14 @@ async function getCodeFile(io, socket, tables, dbClient, data){
handshake: JSON.stringify(handshake),
ip: ip
}, tables, dbClient);
//添加用户-操作关联记录
if(socket.userId){
daoRelation.addUserDogRelation({
dogId : recoderId,
userId : socket.userId,
}, tables, dbClient);
}
socket.emit(rtcClientEvent.getCodeFile, data);

View File

@@ -6,6 +6,8 @@ const utils = require("./../../utils/utils");
const seafile = require("./../../bussiness/oss/seafile")
const rtcCommData = require("./../rtcCommData/commData");
const check = require("../../bussiness/check/content");
const daoRelation = require("../../dao/relation/relation");
/**
* 生成取件码上传链接
@@ -66,7 +68,7 @@ async function prepareCodeFile(io, socket, tables, dbClient, data){
ip: ip
})
await daoDog.addDogData({
let recoderId = await daoDog.addDogData({
name: "生成取件码上传链接",
roomId: data.room || "",
socketId: socket.id,
@@ -76,6 +78,14 @@ async function prepareCodeFile(io, socket, tables, dbClient, data){
handshake: JSON.stringify(handshake),
ip: ip
}, tables, dbClient);
//添加用户-操作关联记录
if(socket.userId){
daoRelation.addUserDogRelation({
dogId : recoderId,
userId : socket.userId,
}, tables, dbClient);
}
socket.emit(rtcClientEvent.prepareCodeFile, data);

View File

@@ -132,6 +132,8 @@ let rtcClientEvent = {
heartbeat : "heartbeat",
//修改昵称
changeNickName : "changeNickName",
//局域网房间发现列表
localNetRoom : "localNetRoom",
}
/**

View File

@@ -1,6 +1,7 @@
const rtcConstant = require("../rtcConstant");
const rtcClientEvent = rtcConstant.rtcClientEvent
const utils = require("../../../src/utils/utils");
const rtcLocalNetRoom = require("../rtcLocalNetRoom/localNetRoom");
/**
* 在线人数统计广播
@@ -17,6 +18,11 @@ async function count(io, socket, tables, dbClient, data){
io.sockets.emit(rtcClientEvent.count, {
mc : allManCount
})
rtcLocalNetRoom.localNetRoom(io, socket, tables, dbClient, {
toAll : true
});
}catch(e){
utils.tlConsole(e)
}

View File

@@ -1,4 +1,5 @@
const daoRoom = require("./../../dao/room/room")
const daoRelation = require("./../../dao/relation/relation")
const bussinessNotify = require("./../../bussiness/notify/notifyHandler")
const rtcCount = require("./../rtcCount/count");
const utils = require("./../../utils/utils");
@@ -7,6 +8,8 @@ const cfg = inject_env_config(require("./../../../conf/cfg.json"))
const rtcConstant = require("../rtcConstant");
const rtcClientEvent = rtcConstant.rtcClientEvent
const check = require("../../bussiness/check/content");
const rtcLocalNetRoom = require("../rtcLocalNetRoom/localNetRoom");
/**
* 用户创建或加入房间
@@ -18,11 +21,12 @@ const check = require("../../bussiness/check/content");
* @returns
*/
async function userCreateAndJoin(io, socket, tables, dbClient, data){
let {handshake, userAgent, ip} = utils.getSocketClientInfo(socket);
let {handshake, userAgent, ip, address} = utils.getSocketClientInfo(socket);
let {
room, type = 'file', nickName = '', password = '',
langMode = 'zh', ua = '', network = '', liveShareRole = ''
room = '', type = 'file', nickName = '', password = '',
langMode = 'zh', ua = '', network = '', liveShareRole = '',
localNetRoom = false
} = data;
if (room && room.length > 15) {
@@ -78,6 +82,14 @@ async function userCreateAndJoin(io, socket, tables, dbClient, data){
device: userAgent,
content: JSON.stringify({ handshake: handshake })
}, tables, dbClient);
//添加用户-房间号关联记录
if(socket.userId){
daoRelation.addUserRoomRelation({
roomId : recoderId,
userId : socket.userId,
}, tables, dbClient);
}
let clientsInRoom = io.sockets.adapter.rooms[room];
let numClients = clientsInRoom ? Object.keys(clientsInRoom.sockets).length : 0;
@@ -109,9 +121,21 @@ async function userCreateAndJoin(io, socket, tables, dbClient, data){
//设置为房主
io.sockets.connected[socket.id].owner = true;
//设置房主ip为房间号ip, address
io.sockets.adapter.rooms[room].ip = ip;
io.sockets.adapter.rooms[room].address = address;
//房间是否可以被局域网发现
io.sockets.adapter.rooms[room].localNetRoom = localNetRoom;
if(localNetRoom){
rtcLocalNetRoom.localNetRoom(io, socket, tables, dbClient, {
toAll : true
});
}
//设置房间类型
io.sockets.adapter.rooms[room].type = type;
//密码房间设置密码
if(type === 'password'){
io.sockets.adapter.rooms[room].password = password
@@ -264,6 +288,8 @@ function getRoomTypeZh(type){
return "密码"
}else if(type === 'audio'){
return "语音连麦"
}else if(type === 'system'){
return "系统"
}else{
return "未知类型"
}

View File

@@ -3,6 +3,9 @@ const daoDog = require("./../../dao/dog/dog")
const rtcConstant = require("../rtcConstant");
const rtcClientEvent = rtcConstant.rtcClientEvent
const check = require("../../bussiness/check/content");
const daoRelation = require("../../dao/relation/relation");
/**
* canvas画图
* @param {*} io socketio对象
@@ -25,7 +28,7 @@ async function draw(io, socket, tables, dbClient, data){
otherClient.emit(rtcClientEvent.draw, data);
//控制操作事件入库
await daoDog.addDogData({
let recoderId = await daoDog.addDogData({
name: "canvas画图",
roomId: "",
socketId: "",
@@ -35,6 +38,14 @@ async function draw(io, socket, tables, dbClient, data){
handshake: JSON.stringify(handshake),
ip: ip
}, tables, dbClient);
//添加用户-操作关联记录
if(socket.userId){
daoRelation.addUserDogRelation({
dogId : recoderId,
userId : socket.userId,
}, tables, dbClient);
}
}
module.exports = {

View File

@@ -5,6 +5,7 @@ const utils = require("./../../utils/utils");
const rtcConstant = require("../rtcConstant");
const rtcClientEvent = rtcConstant.rtcClientEvent
/**
* 退出房间
* @param {*} io socketio对象
@@ -20,7 +21,7 @@ async function exit(io, socket, tables, dbClient, data){
socket.leave(room);
let clientsInRoom = io.sockets.adapter.rooms[room];
clientsInRoom = io.sockets.adapter.rooms[room];
if (clientsInRoom) {
let otherSocketIds = Object.keys(clientsInRoom.sockets);
for (let i = 0; i < otherSocketIds.length; i++) {
@@ -45,7 +46,8 @@ async function exit(io, socket, tables, dbClient, data){
})
}
rtcCount.count(io, socket, tables, dbClient, data)
rtcCount.count(io, socket, tables, dbClient, data);
} catch (e) {
socket.emit(rtcClientEvent.tips, {
room: data.room,

View File

@@ -0,0 +1,161 @@
const rtcConstant = require("../rtcConstant");
const rtcClientEvent = rtcConstant.rtcClientEvent
const bussinessNotify = require("../../bussiness/notify/notifyHandler")
const utils = require("../../../src/utils/utils");
/**
* 局域网房间发现列表
*
* @param {*} io socketio对象
* @param {*} socket 单个socket连接
* @param {*} tables 数据表对象
* @param {*} dbClient sequelize-orm对象
* @param {*} data event参数
* @returns
*/
async function localNetRoom(io, socket, tables, dbClient, data){
try{
const { toCurrentSocket = false, toAll = false } = data;
let { ip, address } = utils.getSocketClientInfo(socket);
let roomList = [];
let rooms = io.sockets.adapter.rooms;
for(let roomId in rooms){
if(roomId.length > 15){
continue;
}
//最多返回10个房间
if(roomList.length > 10){
break;
}
// 是否开启局域网房间
const localNetRoom = rooms[roomId].localNetRoom || false;
if(!localNetRoom){
continue;
}
// 房间ip
const roomIp = rooms[roomId].ip || "";
// 房间address/ip
const roomAddress = rooms[roomId].address || "";
if(roomIp){
// 本地请求
if(
(roomIp.indexOf("127.0.0.1") > -1 && ip.indexOf("127.0.0.1") > -1)
||
(roomIp.indexOf("localhost") > -1 && ip.indexOf("localhost") > -1)
){
roomList = addFilterRoomListData(roomList, {
room : roomId,
type : 'file',
ips : [roomIp],
count : rooms[roomId].length
})
}else{
// 在同一个网段
if(utils.isSameSubnet(roomIp, ip, "255.255.255.255")){
roomList = addFilterRoomListData(roomList, {
room : roomId,
type : 'file',
ips : [roomIp],
count : rooms[roomId].length
})
}
}
}
if(roomAddress){
// 本地请求
if(
(roomAddress.indexOf("127.0.0.1") > -1 && address.indexOf("127.0.0.1") > -1)
||
(roomAddress.indexOf("localhost") > -1 && address.indexOf("localhost") > -1)
){
roomList = addFilterRoomListData(roomList, {
room : roomId,
type : 'file',
ips : [roomAddress],
count : rooms[roomId].length
})
}else{
// 在同一个网段
if(utils.isSameSubnet(roomAddress, address, "255.255.255.255")){
roomList = addFilterRoomListData(roomList, {
room : roomId,
type : 'file',
ips : [roomAddress],
count : rooms[roomId].length
})
}
}
}
}
roomList.map(item=>{
item.room = item.room.toString().substring(0,1) + "***";
})
// 通知当前用户
if(toCurrentSocket){
socket.emit(rtcClientEvent.localNetRoom, {
list : roomList
})
}
// 通知全部用户
if(toAll){
io.sockets.emit(rtcClientEvent.localNetRoom, {
list : roomList
});
}
}catch(e){
utils.tlConsole(e)
bussinessNotify.sendSystemErrorMsg({
title: "socket-localNetRoom",
data: JSON.stringify(data),
room: "",
from : socket.id,
msg : JSON.stringify({
message: e.message,
fileName: e.fileName,
lineNumber: e.lineNumber,
stack: e.stack,
name: e.name
}, null, '\t')
})
}
}
/**
* 处理去重房间列表
* @param {*} list
* @returns
*/
function addFilterRoomListData(list, obj){
let exist = list.filter(item => item.room === obj.room).length > 0;
if(!exist){
list.push(obj)
return list
}
for(let i = 0; i < list.length; i++){
if(list[i].room === obj.room){
let oldIps = list[i].ips;
oldIps.push(obj.ips[0])
list[i].ips = oldIps;
}
}
return list;
}
module.exports = {
localNetRoom
}

View File

@@ -4,6 +4,7 @@ const utils = require("./../../utils/utils");
const rtcConstant = require("../rtcConstant");
const rtcServerMessageEvent = rtcConstant.rtcServerMessageEvent
const check = require("../../bussiness/check/content");
const daoRelation = require("./../../dao/relation/relation")
let rtcEventOpName = {
"sendFileInfo": "准备发送文件",
@@ -204,7 +205,7 @@ async function message(io, socket, tables, dbClient, data){
}
if (rtcEventOpName[emitType]) {
await daoDog.addDogData({
let recoderId = await daoDog.addDogData({
name: rtcEventOpName[emitType],
roomId: room || "",
socketId: "",
@@ -214,6 +215,14 @@ async function message(io, socket, tables, dbClient, data){
handshake: JSON.stringify(handshake),
ip: ip
}, tables, dbClient);
//添加用户-操作关联记录
if(socket.userId){
daoRelation.addUserDogRelation({
dogId : recoderId,
userId : socket.userId,
}, tables, dbClient);
}
}
// 指定发送

View File

@@ -6,6 +6,7 @@ const bussinessOpenai = require("./../../bussiness/openai/openai")
const rtcConstant = require("../rtcConstant");
const rtcClientEvent = rtcConstant.rtcClientEvent
const check = require("../../bussiness/check/content");
const daoRelation = require("../../dao/relation/relation");
/**
* ai聊天
@@ -59,7 +60,7 @@ async function openai(io, socket, tables, dbClient, data){
otherSocket.emit(rtcClientEvent.openaiAnswer, data);
}
await daoDog.addDogData({
let recoderId = await daoDog.addDogData({
name: "ChatGPT聊天",
roomId: roomId,
socketId: data.socketId,
@@ -70,6 +71,14 @@ async function openai(io, socket, tables, dbClient, data){
ip: ip
}, tables, dbClient);
//添加用户-操作关联记录
if(socket.userId){
daoRelation.addUserDogRelation({
dogId : recoderId,
userId : socket.userId,
}, tables, dbClient);
}
bussinessNotify.sendOpenaiChatNotify({
title: "ChatGPT聊天",
room: roomId,

View File

@@ -0,0 +1,53 @@
const request = require('request');
const {inject_env_config} = require("./../../../conf/env_config");
const cfg = inject_env_config(require("./../../../conf/cfg.json"))
const utils = require("./../../utils/utils");
/**
* token 处理
* 通过token去api服务拿到用户信息, 在后续的事件中可以关联到用户信息
* @param {*} io socketio对象
* @param {*} socket 单个socket连接
* @param {*} tables 数据表对象
* @param {*} dbClient sequelize-orm对象
* @param {*} data event参数
* @returns
*/
async function token(io, socket, tables, dbClient, data){
const { token } = socket.handshake.query;
if(!token || token.length < 16){
utils.tlConsole("匿名用户-token空")
return;
}
request({
method: "POST",
url: `https://im.iamtsm.cn/api/login/info`,
json: true,
headers: {
"content-type": "application/json",
},
qs: {
token, key : "iamtsm-socket"
},
}, (err, res, body) => {
if(err){
console.log(err);
return;
}
if(body.code !== 200){
console.log(body);
return;
}
socket.userId = body.userId;
utils.tlConsole("同步token信息成功 : ", token, body.userId)
});
}
module.exports = {
token
}

View File

@@ -1,6 +1,12 @@
// dog
module.exports = (sequelize, DataTypes) => {
return {
DogOther : {
Flag : {
IS_DEV_ADMIN : 0x1, //是否是开发者团队的操作记录
IS_SET_TOP : 0x2, //是否设置消息记录类型置顶
}
},
Dog: sequelize.define('dog', {
id: {
type: DataTypes.INTEGER,
@@ -44,6 +50,10 @@ module.exports = (sequelize, DataTypes) => {
name: 'created_at_index',
method: 'BTREE',
fields: ['created_at']
},{
name: 'room_id_name_index',
method: 'BTREE',
fields: ['room_id','name']
}]
})
};

View File

@@ -0,0 +1,38 @@
// relation
module.exports = (sequelize, DataTypes) => {
return {
Relation: sequelize.define('relation', {
id: {
type: DataTypes.INTEGER,
comment: '关联id',
primaryKey: true,
autoIncrement: true
},
type: {
type: DataTypes.STRING(20),
comment: '关联类型'
},
source_id: {
type: DataTypes.STRING(25),
comment: '源对象id'
},
target_id: {
type: DataTypes.STRING(25),
comment: '关联对象id'
},
flag: {
type: DataTypes.INTEGER,
comment: '标志位',
defaultValue: 0,
}
}, {
timestamps: true,
comment: '关联记录表',
indexes: [{
name: 'created_at_index',
method: 'BTREE',
fields: ['created_at']
}]
})
};
}

View File

@@ -1,6 +1,12 @@
// room
module.exports = (sequelize, DataTypes) => {
return {
RoomOther: {
Flag : {
IS_MANAGE_ROOM : 0x1, //是否是管理后台房间
IS_SYSTEM_QUESTION_ROOM : 0x2, //是否是系统反馈问题房间
},
},
Room: sequelize.define('room', {
id: {
type: DataTypes.INTEGER,
@@ -15,15 +21,15 @@ module.exports = (sequelize, DataTypes) => {
},
uid: {
type: DataTypes.INTEGER,
comment: '匿名用户的id',
comment: '用户的id',
},
uname: {
type: DataTypes.STRING(20),
comment: '匿名用户姓名,昵称'
comment: '用户姓名,昵称'
},
socket_id: {
type: DataTypes.STRING(30),
comment: '匿名用户进入房间时的socket.id'
comment: '用户进入房间时的socket.id'
},
pwd: {
type: DataTypes.STRING(6),

50
svr/src/tables/user.js Normal file
View File

@@ -0,0 +1,50 @@
// user
module.exports = (sequelize, DataTypes) => {
return {
User: sequelize.define('user', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
unique: true,
comment: '数据id',
},
type: {
type: DataTypes.STRING(6),
comment: '帐号类型, wx, qq, wb, web, other'
},
openid: {
type: DataTypes.STRING(64),
comment: '微信openid'
},
avatar : {
type: DataTypes.STRING(512),
comment: '头像地址'
},
uname: {
type: DataTypes.STRING(20),
comment: '姓名,昵称'
},
pwd : {
type: DataTypes.STRING(12),
comment: '用户密码'
},
solt : {
type: DataTypes.STRING(32),
comment: '加密solt'
},
role: {
type: DataTypes.STRING(15),
comment: '用户身份'
}
}, {
timestamps: true,
comment: '用户表',
indexes: [{
name: 'created_at_index',
method: 'BTREE',
fields: ['created_at']
}]
})
};
}

View File

@@ -39,6 +39,36 @@ function getLocalIP() {
return ip;
}
/**
* 检查两个IP地址是否在同一个子网内
* @param {*} ip1
* @param {*} ip2
* @param {*} subnetMask
* @returns
*/
function isSameSubnet(ip1, ip2, subnetMask) {
// 将IPv4或IPv6地址和子网掩码转换为数字形式
function ipToNumber(ip) {
if (ip.includes(':')) { // IPv6
const parts = ip.split(':');
return parts.map(part => parseInt(part, 16)).join('');
} else { // IPv4
const parts = ip.split('.');
return (parseInt(parts[0]) << 24) |
(parseInt(parts[1]) << 16) |
(parseInt(parts[2]) << 8) |
parseInt(parts[3]);
}
}
// 检查第一个IP和第二个IP是否在同一个子网内
const ip1Number = ipToNumber(ip1);
const ip2Number = ipToNumber(ip2);
const subnetMaskNumber = ipToNumber(subnetMask);
return (ip1Number & subnetMaskNumber) === (ip2Number & subnetMaskNumber);
}
/**
* 获取请求的ip
* @param {*} request
@@ -125,11 +155,17 @@ function getNextDay(time) {
*/
function getSocketClientInfo(socket){
let handshake = socket.handshake
let userAgent = handshake.headers['user-agent'].toString().substr(0, 255);
let ip = handshake.headers['x-real-ip'] || handshake.headers['x-forwarded-for'] || handshake.headers['host'];
ip = ip.indexOf(":") > -1 ? ip.split(":")[0] : ip;
let address = socket.handshake.address;
address = address.length > 7 ? address.substr(7, address.length) : address;
return {
handshake, userAgent, ip
handshake, userAgent, ip, address
}
}
@@ -299,6 +335,46 @@ function unescapeStr(str) {
});
}
/**
* 根据socketid加密websocket数据
* @param {*} socketId
* @param {*} data
* @returns
*/
function encryptSocketData(socketId, data){
const key = "tl-rtc-file";
const iv = socketId.substring(0, 16);
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
let encrypted = cipher.update(data, 'utf8', 'hex');
encrypted += cipher.final('hex');
return encrypted;
}
/**
* 根据socketid解密websocket数据
* @param {*} socketId
* @param {*} data
* @returns
*/
function decryptSocketData(socketId, data){
const key = "tl-rtc-file";
const iv = socketId.substring(0, 16);
const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
let decrypted = decipher.update(data, 'hex', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
}
/**
* 根据flag和bit获取对应的值
* @param {*} flag
* @param {*} bit
*/
function checkBit(flag, bit){
return (flag & bit) === bit;
}
module.exports = {
getLocalIP,
@@ -314,5 +390,7 @@ module.exports = {
genTurnServerIceServersConfig,
genClientLogo,
unescapeStr,
escapeStr
escapeStr,
isSameSubnet,
checkBit
}

7
svr/static/js/localforage.min.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,8 @@
@font-face {
font-family: "iconfont"; /* Project id 4147343 */
src: url('iconfont.woff2?t=1694177446228') format('woff2'),
url('iconfont.woff?t=1694177446228') format('woff'),
url('iconfont.ttf?t=1694177446228') format('truetype');
src: url('iconfont.woff2?t=1698150656017') format('woff2'),
url('iconfont.woff?t=1698150656017') format('woff'),
url('iconfont.ttf?t=1698150656017') format('truetype');
}
.iconfont {
@@ -13,6 +13,98 @@
-moz-osx-font-smoothing: grayscale;
}
.icon-rtc-file-zhiding:before {
content: "\e604";
}
.icon-rtc-file-zhuip:before {
content: "\e72d";
}
.icon-rtc-file-wenda:before {
content: "\e889";
}
.icon-rtc-file-wenda1:before {
content: "\e8cd";
}
.icon-rtc-file-V:before {
content: "\e6c0";
}
.icon-rtc-file-shiyanshifangjian:before {
content: "\e634";
}
.icon-rtc-file-fankui:before {
content: "\e633";
}
.icon-rtc-file-juyuwangsousuo:before {
content: "\e77e";
}
.icon-rtc-file-ico_xitongshezhi_fangjianguanli:before {
content: "\e907";
}
.icon-rtc-file-icon:before {
content: "\e632";
}
.icon-rtc-file-faxian:before {
content: "\e669";
}
.icon-rtc-file-fuzhi:before {
content: "\e8b0";
}
.icon-rtc-file-gitee:before {
content: "\e631";
}
.icon-rtc-file-jianqie:before {
content: "\e62f";
}
.icon-rtc-file-yulan:before {
content: "\e630";
}
.icon-rtc-file-cuowu:before {
content: "\e62e";
}
.icon-rtc-file-yidakai:before {
content: "\e62c";
}
.icon-rtc-file-yiguanbi:before {
content: "\e62d";
}
.icon-rtc-file-rizhiguanli:before {
content: "\e645";
}
.icon-rtc-file-ziyuan:before {
content: "\e709";
}
.icon-rtc-file-gongzhonghao:before {
content: "\e62b";
}
.icon-rtc-file-gerenzhongxin:before {
content: "\e62a";
}
.icon-rtc-file-denglu-weixindenglu:before {
content: "\e629";
}
.icon-rtc-file-shu1:before {
content: "\e628";
}
@@ -285,98 +377,6 @@
content: "\e700";
}
.icon-rtc-file-doc:before {
content: "\e652";
}
.icon-rtc-file-jpg:before {
content: "\e653";
}
.icon-rtc-file-html:before {
content: "\e654";
}
.icon-rtc-file-icon_MP-:before {
content: "\e655";
}
.icon-rtc-file-png:before {
content: "\e657";
}
.icon-rtc-file-dll:before {
content: "\e658";
}
.icon-rtc-file-mp4:before {
content: "\e65a";
}
.icon-rtc-file-pdf:before {
content: "\e65b";
}
.icon-rtc-file-gif:before {
content: "\e65c";
}
.icon-rtc-file-exe:before {
content: "\e65d";
}
.icon-rtc-file-mpg:before {
content: "\e65e";
}
.icon-rtc-file-psd:before {
content: "\e65f";
}
.icon-rtc-file-mkv:before {
content: "\e660";
}
.icon-rtc-file-xls:before {
content: "\e661";
}
.icon-rtc-file-rmvb:before {
content: "\e662";
}
.icon-rtc-file-wav:before {
content: "\e664";
}
.icon-rtc-file-swf:before {
content: "\e665";
}
.icon-rtc-file-avi:before {
content: "\e666";
}
.icon-rtc-file-zip1:before {
content: "\e667";
}
.icon-rtc-file-othe:before {
content: "\e668";
}
.icon-rtc-file-ppt:before {
content: "\e650";
}
.icon-rtc-file-txt:before {
content: "\e651";
}
.icon-rtc-file-PDF:before {
content: "\e601";
}
.icon-rtc-file-daimawenjian_file-code:before {
content: "\e621";
}
@@ -437,10 +437,6 @@
content: "\e6cf";
}
.icon-rtc-file-yonghufankuibeifen:before {
content: "\e604";
}
.icon-rtc-file-five-g:before {
content: "\e63f";
}

File diff suppressed because one or more lines are too long

View File

@@ -5,6 +5,167 @@
"css_prefix_text": "icon-rtc-file-",
"description": "",
"glyphs": [
{
"icon_id": "8224818",
"name": "置顶",
"font_class": "zhiding",
"unicode": "e604",
"unicode_decimal": 58884
},
{
"icon_id": "32056420",
"name": "主ip",
"font_class": "zhuip",
"unicode": "e72d",
"unicode_decimal": 59181
},
{
"icon_id": "2076246",
"name": " 问答2",
"font_class": "wenda",
"unicode": "e889",
"unicode_decimal": 59529
},
{
"icon_id": "2076414",
"name": "问答3",
"font_class": "wenda1",
"unicode": "e8cd",
"unicode_decimal": 59597
},
{
"icon_id": "11752935",
"name": "问答",
"font_class": "V",
"unicode": "e6c0",
"unicode_decimal": 59072
},
{
"icon_id": "19818683",
"name": "实验室房间",
"font_class": "shiyanshifangjian",
"unicode": "e634",
"unicode_decimal": 58932
},
{
"icon_id": "637761",
"name": "反馈",
"font_class": "fankui",
"unicode": "e633",
"unicode_decimal": 58931
},
{
"icon_id": "14095259",
"name": "局域网搜索",
"font_class": "juyuwangsousuo",
"unicode": "e77e",
"unicode_decimal": 59262
},
{
"icon_id": "6607709",
"name": "ico_系统设置_房间管理",
"font_class": "ico_xitongshezhi_fangjianguanli",
"unicode": "e907",
"unicode_decimal": 59655
},
{
"icon_id": "340501",
"name": "消息提示",
"font_class": "icon",
"unicode": "e632",
"unicode_decimal": 58930
},
{
"icon_id": "6548530",
"name": "发现",
"font_class": "faxian",
"unicode": "e669",
"unicode_decimal": 58985
},
{
"icon_id": "11372665",
"name": "复制",
"font_class": "fuzhi",
"unicode": "e8b0",
"unicode_decimal": 59568
},
{
"icon_id": "26267572",
"name": "gitee",
"font_class": "gitee",
"unicode": "e631",
"unicode_decimal": 58929
},
{
"icon_id": "1117",
"name": "剪切",
"font_class": "jianqie",
"unicode": "e62f",
"unicode_decimal": 58927
},
{
"icon_id": "26082676",
"name": "预览",
"font_class": "yulan",
"unicode": "e630",
"unicode_decimal": 58928
},
{
"icon_id": "9826657",
"name": "错误",
"font_class": "cuowu",
"unicode": "e62e",
"unicode_decimal": 58926
},
{
"icon_id": "10299070",
"name": "已打开",
"font_class": "yidakai",
"unicode": "e62c",
"unicode_decimal": 58924
},
{
"icon_id": "10299084",
"name": "已关闭",
"font_class": "yiguanbi",
"unicode": "e62d",
"unicode_decimal": 58925
},
{
"icon_id": "13872072",
"name": "日志管理",
"font_class": "rizhiguanli",
"unicode": "e645",
"unicode_decimal": 58949
},
{
"icon_id": "19138777",
"name": "资源",
"font_class": "ziyuan",
"unicode": "e709",
"unicode_decimal": 59145
},
{
"icon_id": "5532887",
"name": "公众号",
"font_class": "gongzhonghao",
"unicode": "e62b",
"unicode_decimal": 58923
},
{
"icon_id": "10199157",
"name": "个人中心",
"font_class": "gerenzhongxin",
"unicode": "e62a",
"unicode_decimal": 58922
},
{
"icon_id": "13769356",
"name": "登录-微信登录",
"font_class": "denglu-weixindenglu",
"unicode": "e629",
"unicode_decimal": 58921
},
{
"icon_id": "15714218",
"name": "书",
@@ -481,167 +642,6 @@
"unicode": "e700",
"unicode_decimal": 59136
},
{
"icon_id": "4031002",
"name": "doc",
"font_class": "doc",
"unicode": "e652",
"unicode_decimal": 58962
},
{
"icon_id": "4031003",
"name": "jpg",
"font_class": "jpg",
"unicode": "e653",
"unicode_decimal": 58963
},
{
"icon_id": "4031004",
"name": "html",
"font_class": "html",
"unicode": "e654",
"unicode_decimal": 58964
},
{
"icon_id": "4031005",
"name": "mp3",
"font_class": "icon_MP-",
"unicode": "e655",
"unicode_decimal": 58965
},
{
"icon_id": "4031006",
"name": "png",
"font_class": "png",
"unicode": "e657",
"unicode_decimal": 58967
},
{
"icon_id": "4031007",
"name": "dll",
"font_class": "dll",
"unicode": "e658",
"unicode_decimal": 58968
},
{
"icon_id": "4031009",
"name": "mp4",
"font_class": "mp4",
"unicode": "e65a",
"unicode_decimal": 58970
},
{
"icon_id": "4031010",
"name": "pdf",
"font_class": "pdf",
"unicode": "e65b",
"unicode_decimal": 58971
},
{
"icon_id": "4031011",
"name": "gif",
"font_class": "gif",
"unicode": "e65c",
"unicode_decimal": 58972
},
{
"icon_id": "4031012",
"name": "exe",
"font_class": "exe",
"unicode": "e65d",
"unicode_decimal": 58973
},
{
"icon_id": "4031013",
"name": "mpg",
"font_class": "mpg",
"unicode": "e65e",
"unicode_decimal": 58974
},
{
"icon_id": "4031014",
"name": "psd",
"font_class": "psd",
"unicode": "e65f",
"unicode_decimal": 58975
},
{
"icon_id": "4031015",
"name": "mkv",
"font_class": "mkv",
"unicode": "e660",
"unicode_decimal": 58976
},
{
"icon_id": "4031016",
"name": "xls",
"font_class": "xls",
"unicode": "e661",
"unicode_decimal": 58977
},
{
"icon_id": "4031017",
"name": "rmvb",
"font_class": "rmvb",
"unicode": "e662",
"unicode_decimal": 58978
},
{
"icon_id": "4031019",
"name": "wav",
"font_class": "wav",
"unicode": "e664",
"unicode_decimal": 58980
},
{
"icon_id": "4031020",
"name": "swf",
"font_class": "swf",
"unicode": "e665",
"unicode_decimal": 58981
},
{
"icon_id": "4031021",
"name": "avi",
"font_class": "avi",
"unicode": "e666",
"unicode_decimal": 58982
},
{
"icon_id": "4033510",
"name": "zip",
"font_class": "zip1",
"unicode": "e667",
"unicode_decimal": 58983
},
{
"icon_id": "4033741",
"name": "othe",
"font_class": "othe",
"unicode": "e668",
"unicode_decimal": 58984
},
{
"icon_id": "4031008",
"name": "ppt",
"font_class": "ppt",
"unicode": "e650",
"unicode_decimal": 58960
},
{
"icon_id": "4031018",
"name": "txt",
"font_class": "txt",
"unicode": "e651",
"unicode_decimal": 58961
},
{
"icon_id": "17880986",
"name": "PDF",
"font_class": "PDF",
"unicode": "e601",
"unicode_decimal": 58881
},
{
"icon_id": "33390112",
"name": "代码文件_file-code",
@@ -747,13 +747,6 @@
"unicode": "e6cf",
"unicode_decimal": 59087
},
{
"icon_id": "21497145",
"name": "用户反馈",
"font_class": "yonghufankuibeifen",
"unicode": "e604",
"unicode_decimal": 58884
},
{
"icon_id": "22272771",
"name": "5g",

File diff suppressed because one or more lines are too long

View File

@@ -1,4 +1,6 @@
const express = require("express");
const bodyParser = require('body-parser');
const cookieParser = require('cookie-parser');
const { inject_env_config, load_env_config } = require("./conf/env_config");
//加载环境变量
load_env_config();
@@ -17,6 +19,9 @@ utils.tlConsoleIcon()
async function start() {
let app = express();
app.use(bodyParser.json());
app.use(cookieParser());
utils.tlConsole("api init start ...")
if (!conf.db.open) {// 没开db

View File

@@ -8,9 +8,9 @@ export default {
plugins: [
copy({
targets: [
{ src: 'res/*.html', dest: 'res/dist' },
{ src: 'res/image/*', dest: 'res/dist/image' },
{ src: 'static/*', dest: 'res/dist/static' },
{ src: 'web-res/*.html', dest: 'web-res/dist' },
{ src: 'web-res/image/*', dest: 'web-res/dist/image' },
{ src: 'static/*', dest: 'web-res/dist/static' },
],
hook: 'writeBundle',
verbose: true
@@ -19,16 +19,16 @@ export default {
build: {
rollupOptions: {
input: Object.fromEntries(
glob.sync('res/*(js|css)/*.*(js|css)').map(file => [
glob.sync('web-res/*(js|css)/*.*(js|css)').map(file => [
path.relative(
'./res',
'./web-res',
file.slice(0, file.length - path.extname(file).length)
),
fileURLToPath(new URL(file, url))
])
),
output: {
dir: "./res/dist/",
dir: "./web-res/dist/",
entryFileNames: "[name].min.js",
assetFileNames: "css/[name].min[extname]"
},

View File

@@ -34,6 +34,12 @@ body {
border-radius: 8px !important;
}
@media screen and (max-width: 1100px){
.layui-layer-iframe{
overflow-y: hidden !important;
}
}
.language-mode{
border-radius: 8px;
}
@@ -60,19 +66,27 @@ body {
position: absolute;
right: 10px;
float: right;
top: 20%;
top: 22%;
display: grid;
transition: top 0.5s;
}
.tl-rtc-file-side-tool a {
margin-top: 20px;
cursor: pointer;
border-radius: 50%;
padding: 10px;
margin-top: 5px;
transition: background .3s, box-shadow .3s;
}
.tl-rtc-file-side-tool a:hover{
background: #e8ebff;
box-shadow: #a9bcde40 0 7px 9px;
}
.tl-rtc-file-side-tool a i {
font-size: 23px;
font-weight: bold;
margin-right: 10px;
cursor: pointer;
color: black;
}
@@ -207,12 +221,31 @@ body {
}
.tl-rtc-file-receive-list-icon{
position: fixed;
padding: 0px 10px 0px 10px;
position: absolute;
padding: 0 10px;
right: 2%;
bottom: 2%;
font-size: 35px;
color: black;
color: #000;
display: inline-flex;
}
.tl-rtc-file-local-net-room{
overflow: hidden;
text-overflow: ellipsis;
word-break: break-all;
white-space: nowrap;
margin: 20px;
text-align: center;
background: white;
border-radius: 8px;
font-size: 16px;
cursor: pointer;
}
.tl-rtc-file-local-net-room:hover{
background: #e8ebff;
box-shadow: #a9bcde40 0 7px 9px;
}
.tl-rtc-file-tool {
@@ -709,11 +742,12 @@ body {
.setting-main {
height: 100%;
height: 100% !important;
background-color: #fff;
position: relative;
left: 0;
top: 0;
height: auto;
}
.setting-main-body {
@@ -723,6 +757,43 @@ body {
overflow: hidden;
}
.setting-main-body .layui-icon-help{
margin-left: 5px;
font-size: 12px;
border-radius: 50%;
background: #d6cd87;
padding: 2px;
cursor: pointer;
}
.setting-main-body-intro{
padding: 10px 20px;
display: inline-flex;
flex-direction: row;
align-items: center;
word-break: break-all;
}
.setting-main-body-intro svg{
width: 24px;
height: 24px;
}
.setting-main-body-file{
padding: 15px 50px;
}
.setting-main-body-file-top{
display: inline-flex;
flex-direction: row;
align-items: center;
word-break: break-all;
}
.setting-main-body-intro div{
margin-left: 10px;
}
.setting-main-body ul {
padding: 15px;
margin: 0px;
@@ -739,6 +810,26 @@ body {
.setting-main-body ul li {
text-align: center;
cursor: pointer;
margin-bottom: 10px;
}
.setting-main-body ul li svg {
width: 40px;
height: 45px;
}
.setting-main-body .settingOpenIcon{
color: #4bd839;
}
.setting-main-body .settingOpenIcon, .setting-main-body .settingCloseIcon{
width: 14px;
height: 14px;
position: absolute;
top: 10px;
}
.setting-main-body .settingCloseIcon{
color: red;
}
.setting-main-body ul li cite {
@@ -749,7 +840,7 @@ body {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
font-size: 14px;
font-size: 12px;
}
.setting-main-body ul li i {
@@ -942,6 +1033,81 @@ body {
margin-left: 20%;
}
#tl-rtc-file-wx-login-image img{
width: 60%;
margin-left: 20%;
margin-top: 25px;
filter: blur(5px);
}
.tl-rtc-file-wx-login-scan-ok{
text-align: center;
position: relative;
margin-top: 40px;
margin-bottom: 50px;
width: 50%;
margin-left: 25%;
}
#tl-rtc-file-wx-login-title{
scale: 0.8;
margin-bottom: 10px;
}
.tl-rtc-file-login-user{
margin-top: 20px;
margin-bottom: 20px;
width: 80%;
margin-left: 10%;
}
.tl-rtc-file-login-user-info{
margin-top: 20px;
margin-bottom: 20px;
width: 80%;
margin-left: 10%;
}
.tl-rtc-file-login-user-info-avatar{
width: 50%;
margin-left: 25%;
margin-bottom: 10px;
}
.tl-rtc-file-login-user-info-avatar img{
width: 100%;
border-radius: 50%;
}
#tl-rtc-file-user-help{
cursor: pointer;
margin-left: 5px;
}
.tl-rtc-file-login-user-info-name{
text-align: center;
font-size: 15px;
font-weight: bold;
margin-top: 30px;
word-break: break-all;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
}
.tl-rtc-file-logout{
width: 60%;
margin-left: 10%;
bottom: 20px;
position: absolute;
}
.tl-rtc-file-logout button{
width: 100%;
margin-bottom: 10px;
}
.isMediaing{
width: 50%;
margin-left: 50%;
@@ -1084,7 +1250,7 @@ body {
bottom: 0px;
position: absolute;
width: 100%;
padding: 20px;
padding: 0px 15px 10px 15px;
}
.chating_input_body textarea{

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 123 KiB

After

Width:  |  Height:  |  Size: 123 KiB

View File

Before

Width:  |  Height:  |  Size: 457 B

After

Width:  |  Height:  |  Size: 457 B

View File

Before

Width:  |  Height:  |  Size: 366 B

After

Width:  |  Height:  |  Size: 366 B

View File

Before

Width:  |  Height:  |  Size: 365 B

After

Width:  |  Height:  |  Size: 365 B

View File

Before

Width:  |  Height:  |  Size: 389 B

After

Width:  |  Height:  |  Size: 389 B

View File

Before

Width:  |  Height:  |  Size: 330 B

After

Width:  |  Height:  |  Size: 330 B

View File

Before

Width:  |  Height:  |  Size: 359 B

After

Width:  |  Height:  |  Size: 359 B

View File

Before

Width:  |  Height:  |  Size: 253 B

After

Width:  |  Height:  |  Size: 253 B

View File

Before

Width:  |  Height:  |  Size: 249 B

After

Width:  |  Height:  |  Size: 249 B

View File

Before

Width:  |  Height:  |  Size: 287 B

After

Width:  |  Height:  |  Size: 287 B

View File

Before

Width:  |  Height:  |  Size: 255 B

After

Width:  |  Height:  |  Size: 255 B

View File

Before

Width:  |  Height:  |  Size: 511 B

After

Width:  |  Height:  |  Size: 511 B

View File

Before

Width:  |  Height:  |  Size: 396 B

After

Width:  |  Height:  |  Size: 396 B

View File

Before

Width:  |  Height:  |  Size: 227 B

After

Width:  |  Height:  |  Size: 227 B

View File

Before

Width:  |  Height:  |  Size: 352 B

After

Width:  |  Height:  |  Size: 352 B

View File

Before

Width:  |  Height:  |  Size: 343 B

After

Width:  |  Height:  |  Size: 343 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

View File

@@ -14,6 +14,7 @@
<link rel="stylesheet" href="/static/css/swiper-bundle.min.css" media="all">
<link rel="stylesheet" href="/static/css/default.min.css">
<link rel="stylesheet" href="/static/css/pdf_viewer.min.css">
<script type="text/javascript" src="/static/js/localforage.min.js"></script>
<script type="text/javascript" src="/static/js/swiper-bundle.min.js"></script>
<script type="text/javascript" src="/static/js/adapter.js"></script>
<script type="text/javascript" src="/static/js/socket.io.js"></script>
@@ -48,18 +49,38 @@
<div class="tl-rtc-file-notification-container" id="notificationContainer"></div>
<!-- 顶部图标 -->
<!-- 顶部图标 左侧 -->
<div class="tl-rtc-file-header-tool" style="left: 10px;">
</div>
<!-- 顶部图标 右侧 -->
<div class="tl-rtc-file-header-tool">
<a :title="lang.disclaimer" @click="openDisclaimer">
<svg class="icon" aria-hidden="true" style="width: 18px;height: 18px;margin-right: 10px;">
<use xlink:href="#icon-rtc-file-jinggao"></use>
</svg>
</a>
<a :title="lang.user_info" @click="openUser" v-show="isLogin">
<svg class="icon" aria-hidden="true" style="scale: 1.1;width: 18px;height: 18px;margin-right: 10px;">
<use xlink:href="#icon-rtc-file-gerenzhongxin"></use>
</svg>
</a>
<a :title="lang.wxlogin" @click="openWxLogin" v-show="!isLogin">
<svg class="icon" aria-hidden="true" style="scale: 1.3;width: 18px;height: 18px;margin-right: 10px;">
<use xlink:href="#icon-rtc-file-gongzhonghao"></use>
</svg>
</a>
<a :title="lang.relay_on" id="useTurn" v-show="switchData.openUseTurnIcon && useTurn" @click="useTurnMsg">
<svg class="icon" aria-hidden="true" style="width: 20px;height: 20px;margin-right: 10px;">
<use xlink:href="#icon-rtc-file-yunfuwuqi"></use>
</svg>
</a>
<a :title="lang.other_language" id="language">
<svg class="icon" aria-hidden="true" style="width: 18px;height: 18px;margin-right: 10px;">
<use xlink:href="#icon-rtc-file-duoyuyan"></use>
</svg>
</a>
<a :title="network" id="currentNetwork" @click="networkMsg" v-show="switchData.openNetworkIcon">
<svg class="icon" aria-hidden="true" v-show="network === '2g'"
style="width: 20px;height: 20px;margin-right: 10px;">
@@ -82,11 +103,6 @@
<use xlink:href="#icon-rtc-file-WIFI"></use>
</svg>
</a>
<a :title="lang.other_language" id="language">
<svg class="icon" aria-hidden="true" style="width: 18px;height: 18px;margin-right: 10px;">
<use xlink:href="#icon-rtc-file-duoyuyan"></use>
</svg>
</a>
<a :title="lang.timer" style="position: relative;top: -3px;">
<b style="transition: color 0.8s;margin-right: 5px;" id="screenShareTimes" v-show="isScreenShare">
{{lang.sharing}}: {{screenShareTimes < 10 ? '0' + screenShareTimes :
@@ -108,18 +124,12 @@
<!-- 侧边图标 -->
<div class="tl-rtc-file-side-tool">
<a href="https://im.iamtsm.cn/document" target="_blank">
<svg class="icon" aria-hidden="true" style="width: 24px;height: 24px;margin-right: 10px;">
<use xlink:href="#icon-rtc-file-shu1"></use>
</svg>
</a>
<a :title="lang.notice" @click="clickNotice" v-show="switchData.openNotice">
<a :title="lang.notice" @click="clickNotice()" v-show="switchData.openNotice">
<i class="layui-icon layui-icon-notice">
<span v-show="switchData.noticeMsgList && switchData.noticeMsgList.length > 0"
<span v-show="switchData.noticeMsgList && switchData.noticeMsgList.length > 0 && useMessageDot"
class="layui-badge tl-rtc-file-msg-dot"
style="right: 7px;top: 65px; width: 7px; height: 7px;"></span>
style="right: 7px;top: 13px; width: 7px; height: 7px;"></span>
</i>
</a>
@@ -127,18 +137,20 @@
<i class="layui-icon layui-icon-home"></i>
</a>
<a :title="lang.share_link" @click="shareUrl" id="shareUrl" v-show="switchData.openShareRoom">
<i class="layui-icon layui-icon-share"></i>
</a>
<a :title="lang.give_coffee" @click="coffee">
<svg class="icon" aria-hidden="true" style="width: 24px;height: 24px;margin-right: 10px;">
<use xlink:href="#icon-rtc-file-hongbao"></use>
<a title="github" href="https://github.com/iamtsm" target="_blank">
<svg class="icon" aria-hidden="true" style="width: 24px;height: 24px;">
<use xlink:href="#icon-rtc-file-github"></use>
</svg>
</a>
<a :title="lang.log" @click="clickLogs">
<i class="layui-icon layui-icon-about"></i>
<a :title="lang.share_link" @click="shareUrl()" id="shareUrl" v-show="switchData.openShareRoom">
<i class="layui-icon layui-icon-share"></i>
</a>
<a :title="lang.give_coffee" @click="coffee()">
<svg class="icon" aria-hidden="true" style="width: 24px;height: 24px;">
<use xlink:href="#icon-rtc-file-hongbao"></use>
</svg>
</a>
<a :title="lang.setting" @click="setting()">
@@ -173,7 +185,7 @@
<i class="layui-icon layui-icon-release tl-rtc-file-send-file-tool-i-three" :style="{
left: sendFileRecoderList.length > 0 ? '10px' :'0'
}"></i>
<span v-show="sendFileRecoderList.length > 0" style="position: relative;right: -9px;"
<span v-show="sendFileRecoderList.length > 0 && useMessageDot" style="position: relative;right: -9px;"
class="layui-badge tl-rtc-file-msg-dot layui-anim layui-anim-rotate layui-anim-loop">
{{Math.min(sendFileRecoderList.length, 99)}}
</div>
@@ -193,7 +205,7 @@
<i class="layui-icon layui-icon-reply-fill tl-rtc-file-send-txt-tool-i-three" :style="{
left: receiveChatRoomList.length > 0 ? '10px' :'0'
}"></i>
<span v-show="receiveChatRoomList.length > 0" style="position: relative;right: -7px;"
<span v-show="receiveChatRoomList.length > 0 && useMessageDot" style="position: relative;right: -7px;"
class="layui-badge tl-rtc-file-msg-dot layui-anim layui-anim-rotate layui-anim-loop">
{{Math.min(receiveChatRoomList.length, 99)}}
</span>
@@ -217,7 +229,7 @@
<use xlink:href="#icon-rtc-file-chat"></use>
</svg>
<b class="tl-rtc-file-tool-title" :class="clientWidth < 450 ? 'tl-rtc-file-tool-title-mobile' : ''">{{lang.chat_comm}}</b>
<span v-show="receiveChatCommList.length > 0" class="layui-badge tl-rtc-file-msg-dot"
<span v-show="receiveChatCommList.length > 0 && useMessageDot" class="layui-badge tl-rtc-file-msg-dot"
style="right: 2px; top: 2px; width: 7px; height: 7px;position: relative;"></span>
<!-- <svg class="icon tl-rtc-file-msg-dot" aria-hidden="true" style="width: 20px;height: 20px;">
<use xlink:href="#icon-rtc-file-remenhot"></use> -->
@@ -360,15 +372,15 @@
class="layui-icon layui-icon-time" @click="clickSendFileHistory"></i>
<i v-show="sendFileRecoderHistoryList.length === 0" :title="lang.file_send_record"
class="layui-icon layui-icon-time layui-disabled" @click="clickSendFileHistory"></i>
<span v-show="sendFileRecoderHistoryList.length > 0" style="left: 20px;"
<span v-show="sendFileRecoderHistoryList.length > 0 && useMessageDot" style="left: 20px;"
class="layui-badge tl-rtc-file-msg-dot layui-anim layui-anim-rotate layui-anim-loop">
{{Math.min(sendFileRecoderHistoryList.length, 99)}}
</span>
<i v-show="receiveFileRecoderList.length > 0" :title="lang.file_receive"
class="layui-icon layui-icon-print" @click="clickReceiveFile"></i>
class="layui-icon layui-icon-print" @click="clickReceiveFile()"></i>
<i v-show="receiveFileRecoderList.length === 0" :title="lang.file_receive"
class="layui-icon layui-icon-print layui-disabled" @click="clickReceiveFile"></i>
<span v-show="receiveFileRecoderList.length > 0"
class="layui-icon layui-icon-print layui-disabled" @click="clickReceiveFile()"></i>
<span v-show="receiveFileRecoderList.length > 0 && useMessageDot"
class="layui-badge tl-rtc-file-msg-dot layui-anim layui-anim-rotate layui-anim-loop">
{{Math.min(receiveFileRecoderList.length, 99)}}
</span>
@@ -398,14 +410,14 @@
<use xlink:href="#icon-rtc-file-yunfuwuqi"></use>
</svg> :
<b v-if="iceOk(remote.iceConnectionState)" style="font-weight: bold;color: #69d31e;">{{remote.iceConnectionState}} - {{remote.p2pMode}}</b>
<b v-else style="font-weight: bold;color: red;">{{remote.iceConnectionState}} - {{remote.p2pMode}}</b>
<b v-else style="font-weight: bold;color: red;">{{remote.iceConnectionState}} - {{remote.p2pMode}} !! </b>
</b>
</div>
</div>
<div class="tl-rtc-file-user-body-right">
<i :title="lang.send_text" @click="startChatRoomSingle(event, remote)"
class="layui-icon layui-icon-reply-fill"></i>
<span v-show="remote.receiveChatRoomSingleList.length > 0" style="right: -7px;"
<span v-show="remote.receiveChatRoomSingleList.length > 0 && useMessageDot" style="right: -7px;"
class="layui-badge tl-rtc-file-msg-dot layui-anim layui-anim-rotate layui-anim-loop">
{{Math.min(remote.receiveChatRoomSingleList.length, 99)}}
</div>
@@ -437,16 +449,32 @@
</button>
</div>
<div v-show="socketId === 0" class="tl-rtc-file-receive-list-icon">
<i v-show="receiveFileRecoderList.length > 0" :title="lang.file_box"
class="layui-icon layui-icon-print tl-rtc-file-receive-list-icon" @click="clickReceiveFile"></i>
<i v-show="receiveFileRecoderList.length === 0" :title="lang.file_box"
class="layui-icon layui-icon-print tl-rtc-file-receive-list-icon layui-disabled"
@click="clickReceiveFile"></i>
<span v-show="receiveFileRecoderList.length > 0" style="top: -40px; right: 0px;"
class="layui-badge tl-rtc-file-msg-dot layui-anim layui-anim-rotate layui-anim-loop">
{{Math.min(receiveFileRecoderList.length, 99)}}
</span>
<div class="tl-rtc-file-receive-list-icon" v-show="socketId === 0">
<div style="position: relative;" >
<i v-show="receiveFileRecoderList.length > 0" :title="lang.file_box"
class="layui-icon layui-icon-print tl-rtc-file-receive-list-icon" @click="clickReceiveFile()"></i>
<i v-show="receiveFileRecoderList.length === 0" :title="lang.file_box"
class="layui-icon layui-icon-print tl-rtc-file-receive-list-icon layui-disabled"
@click="clickReceiveFile()"></i>
<span v-show="receiveFileRecoderList.length > 0 && useMessageDot" style="top: -40px; right: 0px;"
class="layui-badge tl-rtc-file-msg-dot layui-anim layui-anim-rotate layui-anim-loop">
{{Math.min(receiveFileRecoderList.length, 99)}}
</span>
</div>
<div>
<svg v-show="localNetRoomList.length > 0" :title="lang.local_net_room_list" @click="clickLocalNetRooms()"
class="icon" aria-hidden="true" style="color: #574848;">
<use xlink:href="#icon-rtc-file-juyuwangsousuo"></use>
</svg>
<svg v-show="localNetRoomList.length === 0" :title="lang.local_net_room_list" @click="clickLocalNetRooms()"
class="icon layui-disabled" aria-hidden="true" style="color: #574848;">
<use xlink:href="#icon-rtc-file-juyuwangsousuo"></use>
</svg>
<span v-show="localNetRoomList.length > 0 && useMessageDot" style="top: 0px; right: 0px;"
class="layui-badge tl-rtc-file-msg-dot layui-anim layui-anim-rotate layui-anim-loop">
{{Math.min(localNetRoomList.length, 99)}}
</span>
</div>
</div>
</div>
@@ -626,7 +654,7 @@
<button class="tl-rtc-file-send-file-history-list-btn" @click="clickSendFileHistory">
<i class="layui-icon layui-icon-time"></i>
<div>{{lang.sending_history}}</div>
<span style="top:0;right: 0;"
<span v-show="useMessageDot" style="top:0;right: 0;"
class="layui-badge tl-rtc-file-msg-dot layui-anim layui-anim-rotate layui-anim-loop">
{{Math.min(sendFileRecoderHistoryList.length, 99)}}
</span>
@@ -638,7 +666,7 @@
}">
<i class="layui-icon layui-icon-templeate-1"></i>
<div>{{lang.selected_file}}</div>
<span style="top:0;right: 0;"
<span v-show="useMessageDot" style="top:0;right: 0;"
class="layui-badge tl-rtc-file-msg-dot layui-anim layui-anim-rotate layui-anim-loop">
{{Math.min(chooseFileList.length, 99)}}
</span>
@@ -647,6 +675,39 @@
</div>
</div>
<!-- 局域网房间列表 -->
<div id="local-net-room-list" class="tl-rtc-file-mask-send-file-list"
:style="{top: localNetRoomMaskHeightNum + '%'}">
<div class="layui-card" style="border-radius: 15px;">
<div class="layui-card-header">
{{lang.local_net_room_list}} - {{lang.total}}({{localNetRoomList.length}})
<i @click="clickLocalNetRooms()" class="layui-icon layui-icon-close"
style="cursor: pointer; right: 10px;position: absolute;"></i>
</div>
<div class="layui-row tl-rtc-file-send-list-all"
:style="{height: localNetRoomListHeight+'px',overflowY: (localNetRoomList.length > 1 ? 'auto' : 'none') }">
<div class="layui-col-xs12 tl-rtc-file-send-list" v-for="roomInfo, index in localNetRoomList">
<div class="tl-rtc-file-send" style="--progress:100%">
<div class="tl-rtc-file-send-body">
<svg class="icon" aria-hidden="true" style="width: 32px;height: 32px;">
<use xlink:href="#icon-rtc-file-shiyanshifangjian"></use>
</svg>
<div class="tl-rtc-file-send-body-left">
<b class="tl-rtc-file-send-body-left-nick">
{{lang.room_number}}: {{roomInfo.room}}, {{lang.type}}: {{roomInfo.type}}
</b>
<b class="tl-rtc-file-send-body-left-id">
{{lang.ip}}: {{roomInfo.ips.join("/")}}, {{lang.online_count}}: {{roomInfo.count}}
</b>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 已选文件列表 -->
<div id="send-file-list-choose" :class="isMediaing? 'isMediaing': '' " class="tl-rtc-file-mask-send-file-list"
:style="{top: chooseFileMaskHeightNum + '%'}">
@@ -809,11 +870,18 @@
<div class="layui-card" style="border-radius: 15px;">
<div class="layui-card-header">
{{lang.receive_file_list}} - {{lang.total}}:{{receiveFileRecoderList.length}}
<i @click="clickReceiveFile" class="layui-icon layui-icon-close"
<i @click="clickReceiveFile()" class="layui-icon layui-icon-close"
style="cursor: pointer; right: 10px;position: absolute;"></i>
</div>
<div class="layui-row tl-rtc-file-send-list-all"
:style="{height: receiveFileHeight+'px',overflowY: (receiveFileRecoderList.length > 1 ? 'auto' : 'none')}">
<div style="text-align: center; padding: 5px;">
{{lang.load_indexeddb_file_all}} : {{getFileSizeStr(loadIndexedFileAllSize)}} ---
{{lang.receive_file_all_size}} : {{getFileSizeStr(receivedFileAllSize)}}
<b style="margin-left: 10px;display: inline-flex;color: #c14d4d;cursor: pointer;" @click="clearIndexedDbFile()">
<i class="layui-icon layui-icon-delete" style="font-size: 18px;"></i> [{{lang.clear_all}}]
</b>
</div>
<div class="layui-col-xs12 tl-rtc-file-send-list" v-for="file in receiveFileRecoderList">
<div class="tl-rtc-file-send" :style="{'--progress': file.progress + '%'}">
<div class="tl-rtc-file-send-body">
@@ -872,20 +940,38 @@
lang.unknown_type : file.type}}, {{lang.size}}: {{getFileSizeStr(file.size)}}
</b>
<b class="tl-rtc-file-send-body-left-id">
{{lang.file_from}}: {{file.nickName}} - {{file.id}} - {{file.progress}}% -
<b style="font-weight: bold;color: brown;">{{file.indexedDb ? '[' + lang.load_from_disk + ']' : ''}}</b> {{lang.file_from}}: {{file.nickName}} - {{file.id}} - {{file.progress}}% -
{{file.cost}}s
</b>
</b>
<b class="tl-rtc-file-send-body-left-icon" v-if="file.done">
<a :href="file.href" :download="file.name" :title="lang.download">
<a :title="lang.download" v-if="file.indexedDb" @click="downloadIndexedDbFile(file)">
<i class="layui-icon layui-icon-download-circle"></i>
<span
style="top: -2px; margin-right: 5px; left: -5px; position: relative;">[{{lang.download}}]</span>
</a>
<a :title="lang.preview_file" @click="previewReceiveFile(file.index)">
<a :href="file.href" v-if="!file.indexedDb" :download="file.name" :title="lang.download">
<i class="layui-icon layui-icon-download-circle"></i>
<span
style="top: -2px; margin-right: 5px; left: -5px; position: relative;">[{{lang.download}}]</span>
</a>
<a :title="lang.preview_file" v-if="file.indexedDb" @click="previewIndexedDbFile(file)">
<i class="layui-icon layui-icon-camera" style="font-size: 18px;"></i>
<span
style="top: -2px; margin-right: 5px; left: -5px; position: relative;">[{{lang.preview}}]</span>
</a>
<a :title="lang.preview_file" v-if="!file.indexedDb" @click="previewReceiveFile(file.index)">
<i class="layui-icon layui-icon-camera" style="font-size: 18px;"></i>
<span
style="top: -2px; margin-right: 5px; left: -5px; position: relative;">[{{lang.preview}}]</span>
</a>
<a :title="lang.clear" v-if="file.indexedDb" @click="deleteIndexedDbFile(file)">
<i class="layui-icon layui-icon-delete" style="font-size: 18px;"></i>
<span
style="top: -2px; margin-right: 5px; left: -5px; position: relative;">[{{lang.clear}}]</span>
</a>
</b>
<b class="tl-rtc-file-send-body-left-icon layui-disabled" v-else>
<a :title="lang.wait_for_file">
@@ -996,7 +1082,7 @@
<div class="layui-col-sm2" style="width: 100%;">
<div class="layui-card">
<div class="layui-card-header">
{{lang.log}} - {{lang.total}}:{{filterLogs.length}}
{{lang.log}} <b v-show="isCloseLogs" style="color: red;">{{lang.off}}</b> - {{lang.total}}:{{filterLogs.length}}
<i @click="clickLogs" class="layui-icon layui-icon-close"
style="cursor: pointer; right: 10px;position: absolute;"></i>
</div>

View File

@@ -10,7 +10,7 @@ window.tlrtcfile = {
let oldObj = this.getRequestHashArgsObj();
obj = Object.assign(oldObj, obj);
for (let key in obj) {
redirect += key + "=" + obj[key] + "&";
redirect += key + "=" + encodeURIComponent(obj[key]) + "&";
}
return redirect;
},
@@ -23,7 +23,7 @@ window.tlrtcfile = {
const key = pair[0];
const val = pair[1];
if (key) {
obj[key] = val
obj[key] = decodeURIComponent(val)
}
}
return obj;
@@ -496,6 +496,8 @@ window.tlrtcfile = {
return "密码房间"
}else if(type === 'audio'){
return "语音连麦房间"
}else if(type === 'system'){
return "系统房间"
}else{
return "未知类型房间"
}

File diff suppressed because it is too large Load Diff

View File

@@ -8,6 +8,70 @@
const local_lang = {
"en": {
"question_answer" : "Question and answer",
"room_number" : "Room number",
"expand_local_network_room" : "Expand local network room panel",
"collapse_local_network_room" : "Collapse local network room panel",
"no_local_network_room" : "No local network room searched",
"local_net_room_list" : "Local network room list",
"heartbeat_init" : "heartbeat init",
"heartbeat_init_done" : "heartbeat init done",
"indexedDB_init" : "indexedDB init",
"indexedDB_init_done" : "indexedDB init done",
"auto_join_fixed_room" : "Auto join fixed room",
"check_auto_join_fixed_room" : "Check auto join fixed room",
"check_auto_join_fixed_room_done" : "Check auto join fixed room done",
"room_number_error" : "Room number error",
"local_network_room_share" : "Local network room share",
"fixed_room" : "Fixed room number",
"messgae_dot_switch" : "Message dot switch",
"message_dot" : "Message dot",
"github": "Github link",
"gitee" : "Gitee link",
"log_list" : "Log list",
"logs_switch" : "Logs switch",
"indexedDB_file_alreay_delete" : "indexedDB file has been deleted",
"output_log_limit" : "Execution log output limit",
"preview_limit_size" : "Preview file size limit",
"file_fragment_size" : "File fragment transfer size",
"clear_indexeddb_file_done" : "Clear indexeddb file done",
"clear_all" : "Clear all",
"file_transfer_setting" : "File transfer setting",
"switch_setting" : "Switch setting",
"setting_basic": "Setting basic",
"receive_file_all_size" : "Total size of received files",
"load_indexeddb_file_all" : "Load all historical files from disk",
"delete_indexeddb_file_done" : "Delete indexeddb file done",
"load_indexeddb_file_done" : "Load indexeddb file done",
"load_from_disk" : "Load from disk",
"clear" : "Clear",
"clear_file_in_disk" : "Clear file in disk",
"open_source_doc_intro" : "Open source project document installation instructions",
"blog_intro" : "Developer/Team Blog",
"webrtc_check_intro" : "webrtc browser compatibility check",
"custom_url_intro" : "Customized websocket service connection address",
"relay_intro" : "Relay switch for p2p failed transmission",
"save_to_indexeddb_intro" : "Switch to permanently store files in the browser",
"ai_intro" : "Context switch for handling ChatGPT conversations",
"log_intro" : "Open the log list panel",
"send_bug_intro" :"Feedback website/project issues, suggestions",
"setting_intro" : "Setting intro",
"open_source_doc" : "Open source document",
"file_in_disk" : "Saved in disk",
"save_file_to_indexeddb_done" : "Save file to indexeddb done",
"save_file_to_indexeddb" : "Save file to indexeddb",
"logout_succ": "Logout successfully",
"check_login_state_init" : "Check login state initialization",
"check_login_state_init_done" : "Check login state initialization completed",
"nickname_init" : "Nickname initialization",
"nickname_init_done" : "Nickname initialization completed",
"user_info": "User info",
"login_fail": "Login failed",
"login_succ" : "Login successfully",
"scan_succ_and_wait_auth" : "Scan code successfully, waiting for confirmation authorization...",
"wait_scan" : "Waiting for scan",
"login_info" : "After logging in, you can load historical room records and other information",
"wxlogin" : "wchat app login",
"wait_for_file" : "Waiting for the other party to prepare the file",
"send_to_all_user": "Send to all user",
"please_use_turn_server" : "Please use turn server in settings",
@@ -33,7 +97,7 @@ const local_lang = {
"start_audio_sharing" : "Start audio sharing",
"end_audio_sharing" : "End audio sharing",
"in_audioing" : "In audioing",
"start_audio" : "Start audio",
"start_audio" : "audio",
"audience" : "Audience",
"webrtc_check_init" : "Webrtc check init",
"webrtc_check_init_done" : "Webrtc check init done",
@@ -50,7 +114,7 @@ const local_lang = {
"answer_failed": "Answer failed",
"basic_data_get": "Basic data acquisition",
"basic_data_get_done": "Basic data acquisition completed",
"blog": "Blog",
"blog": "My Blog",
"chat_channel": "Chat channel",
"chat_comm": "Chat",
"chat_gpt": "ChatGPT",
@@ -137,7 +201,7 @@ const local_lang = {
"generate_send_file_record": "Generate send file record",
"get_device_failed": "Failed to get device recording permission",
"get_pickup_file": "Get files through pickup code",
"give_coffee": "Buy me a coffee",
"give_coffee": "donat author",
"home": "Official website homepage",
"history_msg" : " History messages",
"i_said_to_ai": "I said to AI",
@@ -184,7 +248,6 @@ const local_lang = {
"open_donate": "Open donate window",
"open_private_chat": "Open private chat panel",
"open_public_chat_panel": "Open public chat panel",
"open_relay_setting": "Open relay setting window",
"open_room_chat_panel": "Open room chat panel",
"open_setting": "Open setting window",
"open_share_join_room": "Open share join room window",
@@ -247,7 +310,7 @@ const local_lang = {
"relay_on": "The relay server is currently enabled, for more information, please go to settings to view",
"relay_on_and_more_info_in_setting": "The relay server is currently enabled, for more information, please go to settings to view",
"relay_server_current": "The relay server is currently",
"relay_server_current_detail": "Enabling the relay server can ensure that the data is transferred in a complex p2p network environment. If it is disabled, it will be forced to go through p2p (p2p detection can be performed in the settings), which may cause the transmission to fail!",
"relay_server_current_detail": "Enabling the relay server can ensure that the data is transferred in a complex p2p network environment. If it is disabled, it will be forced to go through p2p, which may cause the transmission to fail!",
"relay_setting": "Relay setting",
"remote_draw": "Paint",
"room": "Room",
@@ -347,9 +410,77 @@ const local_lang = {
"device_classification" : "Device classification",
"network_status" : "Network status",
"public_ip" : "Public IP",
"webrtc_ice_state" : "webrtc state"
"webrtc_ice_state" : "webrtc state",
"ip" : "IP",
"online_count" : "Online count",
},
"zh": {
"question_answer" : "问答/建议反馈列表",
"ip" : "IP",
"online_count" : "在线人数",
"room_number" : "房间号",
"expand_local_network_room" : "展开局域网房间面板",
"collapse_local_network_room" : "收起局域网房间面板",
"no_local_network_room" : "未搜索到局域网房间",
"local_net_room_list" : "局域网房间列表",
"heartbeat_init" : "心跳初始化",
"heartbeat_init_done" : "心跳初始化完成",
"indexedDB_init" : "indexedDB初始化",
"indexedDB_init_done" : "indexedDB初始化完成",
"auto_join_fixed_room" : "自动加入固定房间",
"check_auto_join_fixed_room_done" : "检查自动加入固定房间完成",
"check_auto_join_fixed_room" : "检查自动加入固定房间",
"room_number_error" : "房间号格式错误",
"local_network_room_share" : "局域网房间",
"fixed_room" : "固定房间号",
"messgae_dot_switch" : "消息红点开关",
"message_dot" : "消息红点",
"github" : "github地址",
"gitee" : "gitee地址",
"log_list" : "日志列表",
"logs_switch" : "日志开关",
"indexedDB_file_alreay_delete" : "indexedDB文件已经删除",
"output_log_limit" : "执行日志输出限制",
"preview_limit_size" : "预览文件大小限制",
"file_fragment_size" : "文件分片传输大小",
"clear_indexeddb_file_done" : "清除indexeddb文件完成",
"clear_all" : "清空全部",
"file_transfer_setting" : "文件传输设置",
"switch_setting" : "开关设置",
"setting_basic" : "基础设置",
"receive_file_all_size" : "接收文件总大小",
"load_indexeddb_file_all" : "加载磁盘历史文件",
"delete_indexeddb_file_done" : "删除indexeddb文件完成",
"load_indexeddb_file_done" : "从indexeddb加载文件完成",
"load_from_disk" : "从磁盘加载",
"clear" : "清理",
"clear_file_in_disk" : "清除磁盘中的文件",
"open_source_doc_intro" : "开源项目文档安装说明",
"blog_intro" : "开发者/团队博客",
"webrtc_check_intro" : "webrtc浏览器兼容性检测",
"custom_url_intro" : "自定义websocket服务连接地址",
"relay_intro" : "p2p失败兜底传输的中继开关",
"save_to_indexeddb_intro" : "文件长久存放在浏览器的开关",
"ai_intro" : "处理ChatGPT对话的上下文开关",
"log_intro" : "打开日志列表面板",
"send_bug_intro" :"反馈网站/项目问题,建议",
"setting_intro" : "设置简介",
"open_source_doc" : "开源文档",
"file_in_disk" : "已存放磁盘中",
"save_file_to_indexeddb" : "文件持久化",
"save_file_to_indexeddb_done": "保存文件到indexeddb完成",
"logout_succ" : "退出成功",
"check_login_state_init" : "检查登录状态初始化",
"check_login_state_init_done" : "检查登录状态初始化完成",
"nickname_init" : "昵称初始化",
"nickname_init_done" : "昵称初始化完成",
"user_info" : "用户信息",
"login_fail": "登录失败",
"login_succ": "登录成功",
"scan_succ_and_wait_auth": "扫码成功,等待确定授权中...",
"wait_scan" : "等待扫码",
"login_info" : "登录后可关联/加载历史房间记录等信息",
"wxlogin" : "微信扫码登录",
"wait_for_file": "对方准备文件中",
"please_use_turn_server" : "当前网络环境不稳定,建议在设置中打开中继服务开关",
"socketConnectFail" : "socket服务连接失败请检查socket服务是否正常启动socket地址=",
@@ -395,13 +526,13 @@ const local_lang = {
"ai_chat_record": "AI聊天记录",
"ai_reply": "AI回复",
"ai_reply_you": "AI回复了你快点聊起来吧~",
"ai_setting": "智能理解",
"ai_setting": "AI上下文",
"ai_switch": "AI智能理解上下文开关",
"ai_thinking": "AI思考中",
"answer_failed": "answer失败",
"basic_data_get": "基础数据 获取中",
"basic_data_get_done": "基础数据 获取完成",
"blog": "博客",
"blog": "个人博客",
"chat_channel": "聊天频道",
"chat_comm": "公共聊天",
"chat_gpt": "ChatGPT",
@@ -487,7 +618,7 @@ const local_lang = {
"generate_send_file_record": "生成文件发送记录",
"get_device_failed": "获取设备录制权限失败",
"get_pickup_file": "通过取件码获取文件",
"give_coffee": "赞助一杯咖啡",
"give_coffee": "赞赏作者",
"history_msg" : " 条历史消息",
"home": "官网首页",
"i_said_to_ai": "我对AI说",
@@ -534,7 +665,6 @@ const local_lang = {
"open_donate": "打开赞助窗口",
"open_private_chat": "打开私聊面板",
"open_public_chat_panel": "打开公共聊天面板",
"open_relay_setting": "打开中继设置窗口",
"open_room_chat_panel": "打开房间聊天面板",
"open_setting": "打开设置窗口",
"open_share_join_room": "打开分享房间窗口",
@@ -597,7 +727,7 @@ const local_lang = {
"relay_on": "当前已启用中继服务器,有关更多信息,请前往设置查看",
"relay_on_and_more_info_in_setting": "当前已启用中继服务器,有关更多信息,请前往设置查看",
"relay_server_current": "中继服务器当前已",
"relay_server_current_detail": "启用中继服务器可以保证在复杂的p2p网络环境下提供保底的数据中转传输如果禁用则是强制走p2p可在设置中进行p2p检测,可能会出现发送失败!",
"relay_server_current_detail": "启用中继服务器可以保证在复杂的p2p网络环境下提供保底的数据中转传输如果禁用则是强制走p2p可能会出现发送失败",
"relay_setting": "中继设置",
"remote_draw": "远程画笔",
"room": "房间",

13
svr/wxapp-res/app.js Normal file
View File

@@ -0,0 +1,13 @@
//app.js
App({
onLaunch: async function () {
},
globalData: {
userInfo: null, //用户信息
baseUrl: "http://localhost:9092", //访问路径
openId: '', //用户唯一标识
loginState: false, //用户登录状态
token: '', //用户登录返回的token
}
})

17
svr/wxapp-res/app.json Normal file
View File

@@ -0,0 +1,17 @@
{
"pages": [
"pages/login/login",
"pages/succ/succ"
],
"window": {
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "tl-rtc-file",
"navigationBarTextStyle": "black",
"backgroundColor": "#eeeeee",
"backgroundTextStyle": "dark"
},
"networkTimeout": {
"request": 10000,
"downloadFile": 10000
}
}

11
svr/wxapp-res/app.wxss Normal file
View File

@@ -0,0 +1,11 @@
/* *app.wxss* */
.container {
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
padding: 200rpx 0;
box-sizing: border-box;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 490 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 454 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

View File

@@ -0,0 +1,180 @@
const app = getApp()
Page({
/**
* 页面的初始数据
*/
data: {
scene: ''
},
onShow: function(){
wx.hideHomeButton()
},
/**
* 页面加载
*/
onLoad: function (option) {
console.log("扫码");
console.log(option);
if (!!option.scene) {
this.setData({
scene: option.scene
});
this.setScanState('scan');
}
},
copyDemo : function(){
wx.setClipboardData({
data: "https://im.iamtsm.cn",
success() {
wx.showToast({
title: '演示体验地址已复制',
icon: "none",
duration: 1000
})
}
})
},
copyGithub : function(){
wx.setClipboardData({
data: "https://github.com/tl-open-source/tl-rtc-file",
success() {
wx.showToast({
title: 'github开源地址已复制',
icon: "none",
duration: 1000
})
}
})
},
copyGitee : function(){
wx.setClipboardData({
data: "https://gitee.com/iamtsm/tl-rtc-file",
success() {
wx.showToast({
title: 'gitee开源地址已复制',
icon: "none",
duration: 1000
})
}
})
},
//获取用户信息
getUserProfile(info) {
let that = this;
wx.showLoading({
title: '正在登录...',
})
// 执行登录操作
let code = '';
wx.login({
success: (res) => {
code = res.code;
},
});
// 获取用户信息
wx.getUserProfile({
lang: 'zh_CN',
desc: '用户登录',
success: (res) => {
that.GetOpenId(res.rawData, code);
},
fail: () => {
// 失败回调
wx.hideLoading();
}
});
},
GetOpenId: function (userInfo, code) {
console.log(userInfo)
let that = this
wx.request({
url: app.globalData.baseUrl + '/api/login/wechat',
data: {
userInfo: JSON.parse(userInfo),
code: code,
scene: that.data.scene
},
dataType: "json",
method: "POST",
success: function (res) {
wx.hideLoading();
console.log(res.data);
if (!res.data.session_key) {
wx.showToast({
title: "登录失败,请尝试重新扫码登录",
duration: 1000,
icon: 'none',
mask: true
})
wx.exitMiniProgram({
success: (res)=>{
console.log(res)
}
})
return
}
app.globalData.openId = res.data.openid;
app.globalData.token = res.data.token;
app.globalData.userInfo = userInfo;
app.globalData.loginState = true;
that.setScanState('auth_succ');
},
fail: function () {
console.log("失败")
that.setScanState('auth_fail');
}
})
},
setScanState(state) {
let that = this;
wx.request({
url: app.globalData.baseUrl + '/api/login/scanState',
data: {
scene: that.data.scene,
state: state,
token : app.globalData.token
},
dataType: "json",
method: "POST",
success: function (res) {
wx.hideLoading();
if (res.data.code != 200) {
wx.showToast({
title: "登录失败,请尝试重新扫码登录",
duration: 1000,
icon: 'none',
mask: true
})
wx.exitMiniProgram({
success: (res)=>{
console.log(res)
}
})
return
}
if(state === 'auth_succ'){
wx.showToast({
title: "登录成功!",
duration: 1000,
icon: 'none',
mask: true
})
// 在登录成功后跳转到 succ 页面
wx.redirectTo({
url: '/pages/succ/succ',
})
}
},
fail: function () {
console.log("失败")
wx.exitMiniProgram({
success: (res)=>{
console.log(res)
}
})
}
})
}
})

View File

@@ -0,0 +1,5 @@
{
"usingComponents": {},
"navigationBarTitleText": "",
"disableScroll": true
}

View File

@@ -0,0 +1,24 @@
<view class="tlrtcfilePanel">
<view class="tlrtcfileTitle" style="text-align: center;">tl-rtc-file 授权登录</view>
<view class="tlrtcfileIntro">
<view class="tlrtcfileIntroItem">
<image src="/images/demo.png" mode="" /> 演示体验地址
<a>|</a>
<a bind:tap="copyDemo">复制</a>
<view>https://im.iamtsm.cn</view>
</view>
<view class="tlrtcfileIntroItem">
<image src="/images/github.png" mode="" /> github开源地址
<a>|</a>
<a bind:tap="copyGithub">复制</a>
<view>https://github.com/tl-open-source/tl-rtc-file</view>
</view>
<view class="tlrtcfileIntroItem">
<image src="/images/gitee.png" mode="" /> gitee开源地址
<a>|</a>
<a bind:tap="copyGitee">复制</a>
<view>https://gitee.com/iamtsm/tl-rtc-file</view>
</view>
</view>
</view>
<button class="loginBtn" open-type="getUserInfo" bindtap="getUserProfile">确认登录</button>

View File

@@ -0,0 +1,55 @@
.tlrtcfilePanel {
border: 1px white solid;
height: 60vh;
margin-top: 5vh;
}
.tlrtcfileTitle {
font-size: 23px;
font-weight: bold;
color: #383C40;
margin-top: 1vh;
}
.tlrtcfileIntro {
color: #686767;
font-size: 12px;
position: relative;
line-height: 25px;
padding: 8vh;
text-align: left;
}
.tlrtcfileIntroItem{
margin-top: 3vh;
}
.tlrtcfileIntroItem view{
margin-top: 1vh;
}
.tlrtcfileIntro image{
width: 4vh;
height: 4vh;
top: 7px;
position: relative;
}
.tlrtcfileIntro a {
margin-left: 1vh;
}
.tlrtcfileIntro a:hover {
color: rgb(114, 114, 202);
}
.loginBtn {
width: 70% !important;
height: 45px;
border-radius: 5px;
background-color: #0E87EB;
color: #fff;
bottom: 13vh;
position: absolute;
left: 15%;
}

View File

@@ -0,0 +1,62 @@
// pages/index/index.js
Page({
/**
* 页面的初始数据
*/
data: {
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide() {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage() {
},
onShow: function(){
wx.hideHomeButton()
}
})

Some files were not shown because too many files have changed in this diff Show More