diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..e69de29 diff --git a/Dockerfile b/Dockerfile index 51f8274..4e5d135 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,9 @@ FROM node:lts-alpine -COPY svr /app/svr -WORKDIR /app/svr + +COPY svr /tlrtcfile/svr + +WORKDIR /tlrtcfile/svr + RUN npm install --registry=https://registry.npmmirror.com && npm run build:pro - ENTRYPOINT ["node"] diff --git a/README.md b/README.md index bf8f0bc..1bf13d2 100644 --- a/README.md +++ b/README.md @@ -11,9 +11,9 @@ #### 简介 :(tl webrtc datachannel filetools)用webrt在web端传输文件,支持传输超大文件。 -#### 优点 : 分片传输,跨终端,不限平台,方便使用,内网不限速,支持私有部署,支持多文件拖拽发送 +#### 优点 : 分片传输,跨终端,不限平台,方便使用,内网不限速(局域网最高到过70多M/s),支持私有部署,支持多文件拖拽发送,网页文件预览 -#### 扩展 : 扩展了许多丰富的小功能,如本地屏幕录制,远程屏幕共享,远程音视频通话,密码房间,直播,oss云存储,中继服务设置,webrtc检测,统计,文字传输,公共聊天,远程画板,丰富的后台管理,实时执行日志展示,机器人告警通知等功能... 等等 +#### 扩展 : 扩展了许多丰富的小功能,如本地屏幕录制,远程屏幕共享(无延迟),远程音视频通话(无延迟),直播(无延迟),密码房间,oss云存储,中继服务设置,webrtc检测,webrtc统计,文字传输(群聊,私聊),公共聊天,远程画板,AI聊天框,丰富的后台管理,实时执行日志展示,机器人告警通知等功能... 等等 #### 体验 : https://im.iamtsm.cn/file @@ -21,34 +21,36 @@ ## 准备 (必须步骤) -安装node-14.21.x或14.21.x以上,npm后进入项目目录运行下面命令 +安装node-14.21.x或14.21.x以上,npm后,进入项目目录运行下面命令 `cd svr/` `npm install` - 首次运行/自行开发页面,需要启动下面两个命令之一 +首次运行/自行开发页面,用下面两个命令之一即可 - `npm run build:dev` (打包监听文件改动打包min) 或者 `npm run build:pro` 打包min + `npm run build:dev` (如果你需要自己开发/修改前端页面,用这个命令) + + `npm run build:pro` (不需要开发/修改前端页面,用这个命令) ## 配置websocket (必须步骤) 修改cfg.json中相应ws配置,或者wss配置 "ws": { - "port": 8444, #socket 端口 - "host": "ws://域名 或者 ip:port 或者 域名:port", #socket ip 局域网ip/公网ip, 局域网ip只能在局域网访问,公网ip可在公网访问 + "port": 8444, #socket 端口 + "host": "ws://域名 或者 ip:port 或者 域名:port", #socket ip 填局域网ip/公网ip, 局域网ip只能在局域网访问,公网ip可在公网访问 }, "wss" : { - "port": 8444, #socket 端口 - "host": "wss://域名 或者 ip:port 或者 域名:port", #socket ip 局域网ip/公网ip, 局域网ip只能在局域网访问,公网ip可在公网访问 + "port": 8444, #socket 端口 + "host": "wss://域名 或者 ip:port 或者 域名:port", #socket ip 填局域网ip/公网ip, 局域网ip只能在局域网访问,公网ip可在公网访问 }, 常见情况示例 : -比如你是用ip(10.x.x.x)的形式部署socket服务,那么host就为 +比如你是用ip(10.1.2.3)的形式部署socket服务,那么host就为 - ws://10.x.x.x:8444 或者 wss://10.x.x.x:8444 + ws://10.1.2.3:8444 或者 wss://10.1.2.3:8444 如果你有域名,并且配置了代理,比如a.test.com转发到本地socket服务的8444端口,那么host就为 @@ -56,24 +58,54 @@ 如果你有域名,但是没有转发到具体的端口,比如有b.test.com:8444访问的是socket服务的8444端口,那么host就为 - ws://b.test.com:8444 或者 wss://b.test.com + ws://b.test.com:8444 或者 wss://b.test.com:8444 ## 启动 (必须步骤) 启动以下两个服务, 选一种模式启动即可 -http模式 +http模式启动后,访问 http://你的机器ip:9092 即可 api服务: `npm run lapi` socket服务 : `npm run lsocket` -https模式 +https模式启动后,访问 https://你的机器ip:9092 即可 api服务: `npm run sapi` socket服务 : `npm run ssocket` + +## 配置turnserver (局域网非必须步骤,公网必须步骤) + +目前有两种形式去生成使用turn服务的帐号密码,一种是固定帐号密码 (优先推荐),一种是有效期帐号密码。**选一种方式即可** + +ubuntu示例: + + 安装coturn `sudo apt-get install coturn` + +有效帐号密码 : `docker/coturn/turnserver-with-secret-user.conf` + + 1. 修改 `listening-device`, `listening-ip`, `external-ip`, `static-auth-secret`, `realm` 几个字段即可 + + 2. 启动turnserver + + `turnserver -c /这个地方路径填完整/conf/turn/turnserver-with-secret-user.conf` + +固定帐号密码 : `docker/coturn/turnserver-with-fixed-user.conf` + + 1. 修改 `listening-device`, `listening-ip`, `external-ip`, `user`, `realm` 几个字段即可 + + 2. 生成用户 + + `turnadmin -a -u 帐号 -p 密码 -r 这个地方填配置文件中的relam` + + 3. 启动turnserver + + `turnserver -c /这个地方路径填完整/docker/coturn/turnserver-with-secret-user.conf` + + ## 配置数据库 (非必须步骤) 修改cfg.json中相应数据库配置 @@ -112,35 +144,35 @@ https模式 } } -## 配置turnserver (局域网非必须步骤,公网必须步骤) - -ubuntu示例: - - 安装coturn `sudo apt-get install coturn` - - 项目提供了一份配置文件模板在 : `conf/turn/turnserver.conf` - - 修改配置文件后复制一份 `cp conf/turn/turnserver.conf /etc/turnserver.conf` - -示例用户和密码: tlrtcfile - - 生成用户 (turnadmin生成密码) `turnadmin -k -u tlrtcfile -r 你的域名` - 或者 (自定义密码) `turnadmin -a -u tlrtcfile -p tlrtcfile -r 你的域名` - 启动turnserver `turnserver -c /etc/turnserver.conf` - - 可参考示例模板 : `bin/turnStart.sh` - - ## Docker (非必须步骤) - 修改local.env中的配置信息或者按需配置conf.json中的ws, 或者wss (需要填容器的ip,端口信息) + +#### 使用官方镜像 : + + docker pull iamtsm/tl-rtc-file + +#### 自己打包镜像 : + +两种模式选一种操作即可 + + http模式启动: + + 修改 `docker/local.env` 中的配置信息或者按需配置conf.json中的ws, 或者wss (需要填容器的ip,端口信息) - docker-compose up -d + docker-compose --profile=local up -d 访问 : http://localhost:9092 或者 http://本机ip:9092 + https模式启动: + + 修改 `docker/local.env` 中的配置信息或者按需配置conf.json中的ws, 或者wss (需要填容器的ip,端口信息) + + docker-compose --profile=server up -d + + 访问 : https://localhost:9092 或者 https://本机ip:9092 + ## 管理后台 (非必须步骤) -前提 : 需要开启数据库配置 +前提 : 需要开启数据库配置 修改cfg.json中的manage的room和password,默认房间号和密码都是tlrtcfile diff --git a/docker-compose.yml b/docker-compose.yml index 03a2713..b37acb3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,18 +1,46 @@ version: '3' services: - api: + api-local: + profiles: ['local'] env_file: - - local.env + - docker/local.env build: . ports: - 9092:9092 - links: - - socket command: localapi - socket: + + socket-local: + profiles: ['local'] env_file: - - local.env + - docker/local.env build: . ports: - 8444:8444 command: localsocket + + api-server: + profiles: ['server'] + env_file: + - docker/server.env + build: . + ports: + - 9092:9092 + command: serverapi + + socket-server: + profiles: ['server'] + env_file: + - docker/server.env + build: . + ports: + - 8444:8444 + command: serversocket + + # coturn: + # image: coturn/coturn + # restart: always + # ports: + # - "3478:3478/udp" + # - "3478:3478/tcp" + # volumes: + # - ./docker/coturn/turnserver-with-secret-user.conf:/etc/turnserver.conf:ro \ No newline at end of file diff --git a/docker/coturn/coturn.env b/docker/coturn/coturn.env new file mode 100644 index 0000000..a6b58bf --- /dev/null +++ b/docker/coturn/coturn.env @@ -0,0 +1 @@ +#coturn env feature use \ No newline at end of file diff --git a/svr/conf/turn/turnserver.conf b/docker/coturn/turnserver-with-fixed-user.conf similarity index 52% rename from svr/conf/turn/turnserver.conf rename to docker/coturn/turnserver-with-fixed-user.conf index 8c75c9c..ad909a4 100644 --- a/svr/conf/turn/turnserver.conf +++ b/docker/coturn/turnserver-with-fixed-user.conf @@ -1,12 +1,12 @@ -#------------TURN BASE CONFIG------------# +#------------TURN BASE CONFIG 基础配置------------# #监听网卡 -listening-device=eth0 +# listening-device=eth0 #监听ip(内网ip) -listening-ip= +# listening-ip= #监听端口(端口) listening-port=3478 #公网ip -external-ip= +# external-ip= #端口最小值 min-port=49152 #端口最大值 @@ -26,21 +26,8 @@ no-tls #关闭dtls no-dtls -#------------TURN USER CONFIG------------# - +#------------TURN USER CONFIG 固定帐号密码模式------------# #用户账号密码 user=tlrtcfile:tlrtcfile - #来源(域名或ip:port) -realm= - -#------------TURN REST API USER CONFIG------------# - -#开启rest api -#use-auth-secret - -#rest api账号密码 -#static-auth-secret=this-is-the-secret-configured-for-coturn-server - -#来源(域名或ip:port) -#realm= \ No newline at end of file +realm=tlrtcfile.com \ No newline at end of file diff --git a/docker/coturn/turnserver-with-secret-user.conf b/docker/coturn/turnserver-with-secret-user.conf new file mode 100644 index 0000000..9e27c86 --- /dev/null +++ b/docker/coturn/turnserver-with-secret-user.conf @@ -0,0 +1,35 @@ +#------------TURN BASE CONFIG 基础配置------------# +#监听网卡 +# listening-device=eth0 +#监听ip(内网ip) +# listening-ip= +#监听端口(端口) +listening-port=3478 +#公网ip +# external-ip= +#端口最小值 +min-port=49152 +#端口最大值 +min-port=55000 +#cli密码 +cli-password=qwerty +#后台运行 +daemon +#会话指纹 +fingerprint +#中等详细日志 +verbose +#长期凭证 +# lt-cred-mech +#关闭tls +no-tls +#关闭dtls +no-dtls + +#------------TURN REST API USER CONFIG 有效时间帐号密码模式 (优先推荐使用这种方式)------------# +#开启rest api +use-auth-secret +#rest api账号密码 +static-auth-secret=tlrtcfile +#来源(域名或ip:port) +realm=tlrtcfile.com \ No newline at end of file diff --git a/local.env b/docker/local.env similarity index 52% rename from local.env rename to docker/local.env index c719131..6ef533c 100644 --- a/local.env +++ b/docker/local.env @@ -1,6 +1,6 @@ ENV_MODE=local WEBRTC_STUN_HOST=stun:stun.xten.com WEBRTC_TURN_HOST=turn:global.turn.twilio.com:3478?transport=udp -WEBRTC_TURN_USERNAME=dc2d2894d5a9023620c467b0e71cfa6a35457e6679785ed6ae9856fe5bdfa269 -WEBRTC_TURN_CREDENTIAL=tE2DajzSJwnsSbc123 +WEBRTC_TURN_USERNAME=tlrtcfile +WEBRTC_TURN_CREDENTIAL=tlrtcfile WS_HOST=ws://127.0.0.1:8444 \ No newline at end of file diff --git a/docker/mysql/mysql.env b/docker/mysql/mysql.env new file mode 100644 index 0000000..a2064a1 --- /dev/null +++ b/docker/mysql/mysql.env @@ -0,0 +1 @@ +#mysql env feature use \ No newline at end of file diff --git a/docker/server.env b/docker/server.env new file mode 100644 index 0000000..b8726b3 --- /dev/null +++ b/docker/server.env @@ -0,0 +1,6 @@ +ENV_MODE=server +WEBRTC_STUN_HOST=stun:stun.xten.com +WEBRTC_TURN_HOST=turn:global.turn.twilio.com:3478?transport=udp +WEBRTC_TURN_USERNAME=tlrtcfile +WEBRTC_TURN_CREDENTIAL=tlrtcfile +WSS_HOST=wss://127.0.0.1:8444 \ No newline at end of file diff --git a/svr/bin/linux/auto-check-install-local.sh b/svr/bin/linux/auto-check-install-local.sh new file mode 100755 index 0000000..7ab75a1 --- /dev/null +++ b/svr/bin/linux/auto-check-install-local.sh @@ -0,0 +1,41 @@ +#!/bin/bash + +# Step 1: Check if Node.js is installed and install Node.js 18 if not +if ! command -v node &> /dev/null; then + echo "Node.js is not installed. Installing Node.js 18..." + curl -sL https://deb.nodesource.com/setup_18.x | sudo -E bash - + sudo apt-get install -y nodejs +fi + +# Step 2: Output Node.js and npm versions +node_version=$(node -v) +npm_version=$(npm -v) +echo "Node.js version: $node_version" +echo "npm version: $npm_version" +sleep 1 + +# Step 3: Check if pm2 is installed and install it globally if not +if ! command -v pm2 &> /dev/null; then + echo "pm2 is not installed. Installing pm2 globally..." + sudo npm install -g pm2 +fi + +# Step 4: Output pm2 version +pm2_version=$(pm2 -v) +echo "pm2 version: $pm2_version" +sleep 1 + +# Step 5: Check if ports 9092 and 8444 are occupied +port_9092_in_use=$(sudo lsof -i :9092 | grep LISTEN | wc -l) +port_8444_in_use=$(sudo lsof -i :8444 | grep LISTEN | wc -l) + +if [ "$port_9092_in_use" -gt 0 ] || [ "$port_8444_in_use" -gt 0 ]; then + echo "Port 9092 or 8444 is already in use." + exit 1 +fi + +echo "ready to run auto-start-local.sh" +sleep 1 + +# Step 6: Run start-local.sh script to start the service +./auto-start-local.sh diff --git a/svr/bin/linux/auto-check-install-server.sh b/svr/bin/linux/auto-check-install-server.sh new file mode 100755 index 0000000..e458601 --- /dev/null +++ b/svr/bin/linux/auto-check-install-server.sh @@ -0,0 +1,41 @@ +#!/bin/bash + +# Step 1: Check if Node.js is installed and install Node.js 18 if not +if ! command -v node &> /dev/null; then + echo "Node.js is not installed. Installing Node.js 18..." + curl -sL https://deb.nodesource.com/setup_18.x | sudo -E bash - + sudo apt-get install -y nodejs +fi + +# Step 2: Output Node.js and npm versions +node_version=$(node -v) +npm_version=$(npm -v) +echo "Node.js version: $node_version" +echo "npm version: $npm_version" +sleep 1 + +# Step 3: Check if pm2 is installed and install it globally if not +if ! command -v pm2 &> /dev/null; then + echo "pm2 is not installed. Installing pm2 globally..." + sudo npm install -g pm2 +fi + +# Step 4: Output pm2 version +pm2_version=$(pm2 -v) +echo "pm2 version: $pm2_version" +sleep 1 + +# Step 5: Check if ports 9092 and 8444 are occupied +port_9092_in_use=$(sudo lsof -i :9092 | grep LISTEN | wc -l) +port_8444_in_use=$(sudo lsof -i :8444 | grep LISTEN | wc -l) + +if [ "$port_9092_in_use" -gt 0 ] || [ "$port_8444_in_use" -gt 0 ]; then + echo "Port 9092 or 8444 is already in use." + exit 1 +fi + +echo "ready to run auto-start-server.sh" +sleep 1 + +# Step 6: Run start-local.sh script to start the service +./auto-start-server.sh diff --git a/svr/start-local.sh b/svr/bin/linux/auto-start-local.sh similarity index 69% rename from svr/start-local.sh rename to svr/bin/linux/auto-start-local.sh index ac65931..23623e0 100755 --- a/svr/start-local.sh +++ b/svr/bin/linux/auto-start-local.sh @@ -6,4 +6,4 @@ pm2 start npm --name=tl-rtc-file-socket-local -- run lsocket sleep 1 -pm2 start npm --name=tl-rtc-file-build-local -- run build:dev \ No newline at end of file +npm run build:pro \ No newline at end of file diff --git a/svr/bin/linux/auto-start-server.sh b/svr/bin/linux/auto-start-server.sh new file mode 100755 index 0000000..59304f2 --- /dev/null +++ b/svr/bin/linux/auto-start-server.sh @@ -0,0 +1,9 @@ +pm2 start npm --name=tl-rtc-file-api-server -- run sapi + +sleep 1 + +pm2 start npm --name=tl-rtc-file-socket-server -- run ssocket + +sleep 1 + +npm run build:pro \ No newline at end of file diff --git a/svr/bin/linux/auto-stop.sh b/svr/bin/linux/auto-stop.sh new file mode 100755 index 0000000..477740d --- /dev/null +++ b/svr/bin/linux/auto-stop.sh @@ -0,0 +1,3 @@ +pm2 stop all + +echo "stop all pm2 processes ok" \ No newline at end of file diff --git a/svr/bin/turnStart.sh b/svr/bin/turnStart.sh deleted file mode 100644 index 0bfe613..0000000 --- a/svr/bin/turnStart.sh +++ /dev/null @@ -1,8 +0,0 @@ -#生成账号密码方式1 -turnadmin -a -u tlrtcfile -p tlrtcfile -r 域名或ip:port - -#生成账号密码方式2 -turnadmin -k -u tlrtcfile -r 域名或ip:port - -#启动turnserver -turnserver -c turnserver.conf路径 diff --git a/svr/bin/windows/auto-check-install-local.bat b/svr/bin/windows/auto-check-install-local.bat new file mode 100644 index 0000000..864b73a --- /dev/null +++ b/svr/bin/windows/auto-check-install-local.bat @@ -0,0 +1,45 @@ +@echo off +setlocal + +REM Step 1: Check if Node.js is installed and install Node.js 18 if not +where /q node +if %ERRORLEVEL% NEQ 0 ( + echo Node.js is not installed. Installing Node.js 18... + REM Modify the Node.js installer URL if needed + curl -o node_installer.msi https://nodejs.org/dist/v18.0.0/node-v18.0.0-x64.msi + start /wait msiexec /i node_installer.msi /qn + del node_installer.msi +) + +REM Step 2: Output Node.js and npm versions +node -v +npm -v + +REM Step 3: Check if pm2 is installed and install it globally if not +where /q pm2 +if %ERRORLEVEL% NEQ 0 ( + echo pm2 is not installed. Installing pm2 globally... + npm install -g pm2 +) + +REM Step 4: Output pm2 version +pm2 -v + +REM Step 5: Check if ports 9092 and 8444 are occupied +netstat -ano | findstr ":9092" +if %ERRORLEVEL% EQU 0 ( + echo Port 9092 is already in use. + exit /b 1 +) + +netstat -ano | findstr ":8444" +if %ERRORLEVEL% EQU 0 ( + echo Port 8444 is already in use. + exit /b 1 +) + +REM Step 6: Output installation successful message +echo env Installation successful. + +REM Step 7: Run start-local.bat script to start the service +auto-start-local.bat \ No newline at end of file diff --git a/svr/bin/windows/auto-check-install-server.bat b/svr/bin/windows/auto-check-install-server.bat new file mode 100644 index 0000000..864b73a --- /dev/null +++ b/svr/bin/windows/auto-check-install-server.bat @@ -0,0 +1,45 @@ +@echo off +setlocal + +REM Step 1: Check if Node.js is installed and install Node.js 18 if not +where /q node +if %ERRORLEVEL% NEQ 0 ( + echo Node.js is not installed. Installing Node.js 18... + REM Modify the Node.js installer URL if needed + curl -o node_installer.msi https://nodejs.org/dist/v18.0.0/node-v18.0.0-x64.msi + start /wait msiexec /i node_installer.msi /qn + del node_installer.msi +) + +REM Step 2: Output Node.js and npm versions +node -v +npm -v + +REM Step 3: Check if pm2 is installed and install it globally if not +where /q pm2 +if %ERRORLEVEL% NEQ 0 ( + echo pm2 is not installed. Installing pm2 globally... + npm install -g pm2 +) + +REM Step 4: Output pm2 version +pm2 -v + +REM Step 5: Check if ports 9092 and 8444 are occupied +netstat -ano | findstr ":9092" +if %ERRORLEVEL% EQU 0 ( + echo Port 9092 is already in use. + exit /b 1 +) + +netstat -ano | findstr ":8444" +if %ERRORLEVEL% EQU 0 ( + echo Port 8444 is already in use. + exit /b 1 +) + +REM Step 6: Output installation successful message +echo env Installation successful. + +REM Step 7: Run start-local.bat script to start the service +auto-start-local.bat \ No newline at end of file diff --git a/svr/bin/windows/auto-start-local.bat b/svr/bin/windows/auto-start-local.bat new file mode 100644 index 0000000..f98219d --- /dev/null +++ b/svr/bin/windows/auto-start-local.bat @@ -0,0 +1,12 @@ +@echo off + +REM Start the first process +start "" pm2 start npm --name=tl-rtc-file-api-local -- run lapi +timeout /t 1 /nobreak + +REM Start the second process +start "" pm2 start npm --name=tl-rtc-file-socket-local -- run lsocket +timeout /t 1 /nobreak + +REM Run npm build command +npm run build:pro \ No newline at end of file diff --git a/svr/bin/windows/auto-start-server.bat b/svr/bin/windows/auto-start-server.bat new file mode 100644 index 0000000..5cf48e9 --- /dev/null +++ b/svr/bin/windows/auto-start-server.bat @@ -0,0 +1,12 @@ +@echo off + +REM Start the first process +start "" pm2 start npm --name=tl-rtc-file-api-local -- run sapi +timeout /t 1 /nobreak + +REM Start the second process +start "" pm2 start npm --name=tl-rtc-file-socket-local -- run ssocket +timeout /t 1 /nobreak + +REM Run npm build command +npm run build:pro \ No newline at end of file diff --git a/svr/conf/cfg.json b/svr/conf/cfg.json index e8ef7c2..946d92a 100644 --- a/svr/conf/cfg.json +++ b/svr/conf/cfg.json @@ -1,5 +1,5 @@ { - "version": "10.2.8", + "version": "10.2.9", "ws": { "port": 8444, "host": "ws://127.0.0.1:8444" diff --git a/svr/conf/env_config.js b/svr/conf/env_config.js index d0dcfba..4c6510f 100644 --- a/svr/conf/env_config.js +++ b/svr/conf/env_config.js @@ -1,23 +1,24 @@ -const inject_env_config=(conf)=>{ - Object.keys(process.env).filter(key=>/^(WS(S)?_|API_|WEBRTC_).+/.test(key)).map(key=>{ - let data=process.env[key] - if(key.endsWith('_PORT')){ - data=parseInt(data) +const inject_env_config = (conf) => { + Object.keys(process.env).filter(key => /^(WS(S)?_|API_|WEBRTC_).+/.test(key)).map(key => { + let data = process.env[key] + if (key.endsWith('_PORT')) { + data = parseInt(data) } - let curr=conf; - const paths=key.split('_').map(p=>p.toLowerCase()) - const last=paths.pop() - for (const path of paths){ - curr=curr[path] + let curr = conf; + const paths = key.split('_').map(p => p.toLowerCase()) + const last = paths.pop() + for (const path of paths) { + curr = curr[path] } - if(curr){ + if (curr) { console.log(`config ${paths.join('.')}.${last} to ${data}`); - curr[last]=data + curr[last] = data } - + }) return conf } + module.exports = { - inject_env_config + inject_env_config } diff --git a/svr/res/js/comm.js b/svr/res/js/comm.js index f73526d..12595b5 100644 --- a/svr/res/js/comm.js +++ b/svr/res/js/comm.js @@ -5,16 +5,16 @@ // --------------------------- // window.tlrtcfile = { - addUrlHashParams : function(obj){ + addUrlHashParams: function (obj) { let redirect = window.location.protocol + "//" + window.location.host + "#" let oldObj = this.getRequestHashArgsObj(); - obj = Object.assign(oldObj,obj); - for(let key in obj){ + obj = Object.assign(oldObj, obj); + for (let key in obj) { redirect += key + "=" + obj[key] + "&"; } return redirect; }, - getRequestHashArgsObj : function () { + getRequestHashArgsObj: function () { let query = decodeURIComponent(window.location.hash.substring(1)); let args = query.split("&"); let obj = {}; @@ -22,18 +22,18 @@ window.tlrtcfile = { let pair = args[i].split("="); const key = pair[0]; const val = pair[1]; - if(key){ + if (key) { obj[key] = val } } return obj; }, - getRequestHashArgs : function (key) { + getRequestHashArgs: function (key) { let query = decodeURIComponent(window.location.hash.substring(1)); let args = query.split("&"); for (let i = 0; i < args.length; i++) { let pair = args[i].split("="); - if(pair[0] === key){ + if (pair[0] === key) { return pair[1]; } } @@ -163,7 +163,7 @@ window.tlrtcfile = { }, 32) }, loadJS: function (url, callback) { - var script = document.createElement('script'), + let script = document.createElement('script'), fn = callback || function () { }; script.type = 'text/javascript'; //IE @@ -189,7 +189,7 @@ window.tlrtcfile = { ); }, chatKeydown: function (dom, callback) { - if(dom){ + if (dom) { dom.onkeydown = function (e) { if (e.defaultPrevented) { return; @@ -198,7 +198,7 @@ window.tlrtcfile = { if (e.shiftKey) { // shift+enter 换行 return; - }else if (e.key !== undefined) { + } else if (e.key !== undefined) { if (e.key === "Enter") { // enter键执行 callback && callback() @@ -216,7 +216,8 @@ window.tlrtcfile = { }, supposeWebrtc: function (rtcConfig) { try { - let testRTCPeerConnection = window.RTCPeerConnection || window.webkitRTCPeerConnection || window.mozRTCPeerConnection || window.RTCIceGatherer; + let testRTCPeerConnection = window.RTCPeerConnection || window.webkitRTCPeerConnection || + window.mozRTCPeerConnection || window.RTCIceGatherer; if (testRTCPeerConnection) { new RTCPeerConnection(rtcConfig); return true; @@ -228,7 +229,146 @@ window.tlrtcfile = { return false; } }, + checkWebRtcNATType: async function (rtcConfig) { + // 确认网络连接状态 + try { + const rtcConnect = new RTCPeerConnection(rtcConfig); + const dataChannel = rtcConnect.createDataChannel('ping'); // 创建数据通道 + // 生成本地候选者 + const localCandidates = []; + const candidates = {}; + rtcConnect.onicecandidate = (event) => { + if(!event.candidate){//等待几秒钟没有候选者了,可以初步判断结果了 + rtcConnect.close(); + return + } + + localCandidates.push(event.candidate.candidate); + + // if (event.candidate.candidate.indexOf('srflx') === -1) return; + + // let fields; + // if (event.candidate.candidate.indexOf('a=candidate:') === 0) { + // fields = event.candidate.candidate.substring(12).split(' '); + // } else { + // fields = event.candidate.candidate.substring(10).split(' '); + // } + // let candidateFields = { + // ip: fields[4], + // port: parseInt(fields[5], 10), + // type: fields[7] + // }; + + // for (let i = 8; i < fields.length; i += 2) { + // if(fields[i] === 'raddr'){ + // candidateFields.relatedAddress = fields[i + 1]; + // } + // if(fields[i] === 'rport'){ + // candidateFields.relatedPort = parseInt(fields[i + 1], 10); + // } + // if(fields[i] === 'tcptype'){ + // candidateFields.tcpType = fields[i + 1]; + // } + // } + // console.log("candidate : ",candidateFields); + + // if (!candidates[candidateFields.relatedPort]){ + // candidates[candidateFields.relatedPort] = [] + // } + // candidates[candidateFields.relatedPort].push(candidateFields.port); + }; + + await rtcConnect.setLocalDescription(await rtcConnect.createOffer({ + iceRestart: true // 避免使用缓存的ICE候选者 + })); + + // 等待一段时间以获取完整的ICE候选者 + await new Promise((resolve) => setTimeout(resolve, 2000)); + + + // 模拟将本地候选者发送给Server并从Server获取Server的候选者 + const serverCandidates = []; // 假设这里填入从Server获取的候选者 + const remoteCandidates = localCandidates.concat(serverCandidates); + + // 解析ICE候选者中的IP和Port + const candidateIPandPorts = remoteCandidates.map( candidate => { + const fields = candidate.split(' '); + let candidateFields = { + ip: fields[4], + port: parseInt(fields[5], 10), + type: fields[7] + }; + + if (candidate.indexOf('srflx') !== -1){ + let srflxFiled = "" + if (candidate.indexOf('a=candidate:') === 0) { + srflxFiled = candidate.substring(12).split(' '); + } else { + srflxFiled = candidate.substring(10).split(' '); + } + for (let i = 8; i < srflxFiled.length; i += 2) { + if(srflxFiled[i] === 'raddr'){ + candidateFields.relatedAddress = srflxFiled[i + 1]; + } + if(srflxFiled[i] === 'rport'){ + candidateFields.relatedPort = parseInt(srflxFiled[i + 1], 10); + } + if(srflxFiled[i] === 'tcptype'){ + candidateFields.tcpType = srflxFiled[i + 1]; + } + } + + } + return candidateFields; + }); + + console.log("candidateIPandPorts : ",candidateIPandPorts) + + // 判断网络状态是否正常 + const isNetworkBlocked = candidateIPandPorts.length === 0; + if (isNetworkBlocked) { + return "udp-blocked"; + } + + // 判断是否在NAT之后,比较第一个候选者的IP和Port + const isBehindNAT = candidateIPandPorts[0].ip !== candidateIPandPorts[candidateIPandPorts.length - 1].ip || + candidateIPandPorts[0].port !== candidateIPandPorts[candidateIPandPorts.length - 1].port; + + if (!isBehindNAT) { + // 没有经过NAT转换,可能存在网络防火墙 + // TODO: 进行更多验证,例如尝试连接不同的端口和IP + return "no-nat"; + } else { + // 判断是否是端口限制型NAT + const isPortRestrictedNAT = candidateIPandPorts.some((candidate, index) => { + const nextCandidate = candidateIPandPorts[index + 1]; + if (nextCandidate) { + return candidate.ip === nextCandidate.ip && candidate.port !== nextCandidate.port; + } + return false; + }); + + if (isPortRestrictedNAT) return 'port-restricted-nat'; + + // 判断是否是IP限制型NAT + const isIPRestrictedNAT = candidateIPandPorts.some((candidate, index) => { + const nextCandidate = candidateIPandPorts[index + 1]; + if (nextCandidate) { + return candidate.ip !== nextCandidate.ip && candidate.port === nextCandidate.port; + } + return false; + }); + + if (isIPRestrictedNAT) return 'ip-restricted-nat'; + + return 'unknown-nat'; + } + } catch (error) { + console.error('Error occurred:', error); + return 'error'; + } + }, getWebrtcStats: async function (peerConnection) { if (!peerConnection) { return "RTCPeerConnection is not available"; @@ -298,7 +438,7 @@ window.tlrtcfile = { copyTxt: function (id, content) { let that = this; document.querySelector("#" + id).setAttribute("data-clipboard-text", content); - var clipboard = new ClipboardJS('#' + id); + let clipboard = new ClipboardJS('#' + id); clipboard.on('success', function (e) { e.clearSelection(); if (window.layer) { @@ -306,17 +446,17 @@ window.tlrtcfile = { } }); }, - getQrCode : function(id, content){ + getQrCode: function (id, content) { const qrcode = new QRCode(id, { text: content, // 二维码内容 width: 300, height: 300, - colorDark : "#000000", // 码的颜色 - colorLight : "#ffffff", // 码的背景色 - correctLevel : QRCode.CorrectLevel.H // 高度容错 + colorDark: "#000000", // 码的颜色 + colorLight: "#ffffff", // 码的背景色 + correctLevel: QRCode.CorrectLevel.H // 高度容错 }); }, - getOpEventData: function(callback){ + getOpEventData: function (callback) { window.addEventListener('click', function (event) { callback("click", event) }) @@ -330,10 +470,10 @@ window.tlrtcfile = { window.addEventListener('mousedown', function () { callback("mousedown", null) }) - window.addEventListener('mouseup', function() { + window.addEventListener('mouseup', function () { callback("mouseup", null) }) - window.addEventListener('wheel', function(event) { + window.addEventListener('wheel', function (event) { callback("wheel", event) }) window.addEventListener('keydown', function (event) { @@ -376,11 +516,11 @@ window.tlrtcfile = { return (t = t / 1 - 1) * t * t + 1; } - function animateMargin() { + function animateMargin() { currentTime += increment; let val = easeOutCubic(currentTime / duration) * change; - dom.style.marginLeft = val+"%"; - if(val === 100){ + dom.style.marginLeft = val + "%"; + if (val === 100) { callback() } if (currentTime < duration) { @@ -476,7 +616,7 @@ window.tlrtcfile = { html = content; } else { lang = file.name.split(".").pop(); - if(lang === 'log'){lang = 'txt'} + if (lang === 'log') { lang = 'txt' } html = hljs.highlight(content, { language: lang }).value; } @@ -493,13 +633,13 @@ window.tlrtcfile = {
${html}
`,
- area: ["80%","80%"],
+ area: ["80%", "80%"],
success: function (layero, index) {
document.querySelector(".layui-layer-content").style.borderRadius = "15px"
},
});
}
-
+
return callback && callback("预览文件 【" + file.name + "】")
};
@@ -519,34 +659,34 @@ window.tlrtcfile = {
content: `