mirror of
https://github.com/tl-open-source/tl-rtc-file.git
synced 2025-09-26 19:41:16 +08:00
feat: 环境部署相关完善
feat: 调整turn服务帐号默认为有效帐号模式 feat: 文档更新
This commit is contained in:
0
CHANGELOG.md
Normal file
0
CHANGELOG.md
Normal file
@@ -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"]
|
||||
|
100
README.md
100
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,15 +21,17 @@
|
||||
|
||||
## 准备 (必须步骤)
|
||||
|
||||
安装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 (必须步骤)
|
||||
|
||||
@@ -37,18 +39,18 @@
|
||||
|
||||
"ws": {
|
||||
"port": 8444, #socket 端口
|
||||
"host": "ws://域名 或者 ip:port 或者 域名:port", #socket ip 局域网ip/公网ip, 局域网ip只能在局域网访问,公网ip可在公网访问
|
||||
"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可在公网访问
|
||||
"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-compose up -d
|
||||
#### 使用官方镜像 :
|
||||
|
||||
docker pull iamtsm/tl-rtc-file
|
||||
|
||||
#### 自己打包镜像 :
|
||||
|
||||
两种模式选一种操作即可
|
||||
|
||||
http模式启动:
|
||||
|
||||
修改 `docker/local.env` 中的配置信息或者按需配置conf.json中的ws, 或者wss (需要填容器的ip,端口信息)
|
||||
|
||||
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
|
||||
|
||||
|
@@ -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
|
1
docker/coturn/coturn.env
Normal file
1
docker/coturn/coturn.env
Normal file
@@ -0,0 +1 @@
|
||||
#coturn env feature use
|
@@ -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=
|
||||
realm=tlrtcfile.com
|
35
docker/coturn/turnserver-with-secret-user.conf
Normal file
35
docker/coturn/turnserver-with-secret-user.conf
Normal file
@@ -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
|
@@ -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
|
1
docker/mysql/mysql.env
Normal file
1
docker/mysql/mysql.env
Normal file
@@ -0,0 +1 @@
|
||||
#mysql env feature use
|
6
docker/server.env
Normal file
6
docker/server.env
Normal file
@@ -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
|
41
svr/bin/linux/auto-check-install-local.sh
Executable file
41
svr/bin/linux/auto-check-install-local.sh
Executable file
@@ -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
|
41
svr/bin/linux/auto-check-install-server.sh
Executable file
41
svr/bin/linux/auto-check-install-server.sh
Executable file
@@ -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
|
@@ -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
|
||||
npm run build:pro
|
9
svr/bin/linux/auto-start-server.sh
Executable file
9
svr/bin/linux/auto-start-server.sh
Executable file
@@ -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
|
3
svr/bin/linux/auto-stop.sh
Executable file
3
svr/bin/linux/auto-stop.sh
Executable file
@@ -0,0 +1,3 @@
|
||||
pm2 stop all
|
||||
|
||||
echo "stop all pm2 processes ok"
|
@@ -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路径
|
45
svr/bin/windows/auto-check-install-local.bat
Normal file
45
svr/bin/windows/auto-check-install-local.bat
Normal file
@@ -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
|
45
svr/bin/windows/auto-check-install-server.bat
Normal file
45
svr/bin/windows/auto-check-install-server.bat
Normal file
@@ -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
|
12
svr/bin/windows/auto-start-local.bat
Normal file
12
svr/bin/windows/auto-start-local.bat
Normal file
@@ -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
|
12
svr/bin/windows/auto-start-server.bat
Normal file
12
svr/bin/windows/auto-start-server.bat
Normal file
@@ -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
|
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"version": "10.2.8",
|
||||
"version": "10.2.9",
|
||||
"ws": {
|
||||
"port": 8444,
|
||||
"host": "ws://127.0.0.1:8444"
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -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) {
|
||||
@@ -379,8 +519,8 @@ window.tlrtcfile = {
|
||||
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,7 +633,7 @@ window.tlrtcfile = {
|
||||
<pre><code class="hljs ${lang}">${html}</code></pre>
|
||||
</div>
|
||||
`,
|
||||
area: ["80%","80%"],
|
||||
area: ["80%", "80%"],
|
||||
success: function (layero, index) {
|
||||
document.querySelector(".layui-layer-content").style.borderRadius = "15px"
|
||||
},
|
||||
@@ -519,34 +659,34 @@ window.tlrtcfile = {
|
||||
content: ` <div id="tl-rtc-file-pdf-container" style="height: 100%;"> </div> `,
|
||||
success: function (layero, index) {
|
||||
document.querySelector(".layui-layer-content").style.borderRadius = "15px"
|
||||
try{
|
||||
try {
|
||||
let fileReader = new FileReader();
|
||||
fileReader.onload = function(e) {
|
||||
fileReader.onload = function (e) {
|
||||
pdfjsLib.GlobalWorkerOptions.workerSrc = './pdf.worker.min.js';
|
||||
pdfjsLib.getDocument(e.target.result).promise.then(async function(pdf) {
|
||||
function renderPage(numPages, num){
|
||||
if(num - 1 >= numPages){
|
||||
pdfjsLib.getDocument(e.target.result).promise.then(async function (pdf) {
|
||||
function renderPage(numPages, num) {
|
||||
if (num - 1 >= numPages) {
|
||||
return callback && callback("pdf加载渲染完毕")
|
||||
}
|
||||
pdf.getPage(num).then(function(page) {
|
||||
const viewport = page.getViewport({scale: 1.5});
|
||||
pdf.getPage(num).then(function (page) {
|
||||
const viewport = page.getViewport({ scale: 1.5 });
|
||||
const canvas = document.createElement('canvas');
|
||||
let pdfDom = document.getElementById("tl-rtc-file-pdf-container");
|
||||
if(pdfDom){
|
||||
if (pdfDom) {
|
||||
pdfDom.appendChild(canvas);
|
||||
canvas.setAttribute("style","height: auto; width: 100%;")
|
||||
canvas.setAttribute("style", "height: auto; width: 100%;")
|
||||
canvas.height = viewport.height;
|
||||
canvas.width = viewport.width;
|
||||
|
||||
page.render({
|
||||
canvasContext: canvas.getContext('2d'),
|
||||
viewport: viewport
|
||||
}).promise.then(function(e) {
|
||||
}).promise.then(function (e) {
|
||||
let dom = document.querySelector(".layui-layer-title");
|
||||
if(dom){
|
||||
if (dom) {
|
||||
dom.innerText = `共${numPages}页 - 已渲染${num}页`;
|
||||
}
|
||||
renderPage(numPages, num+1)
|
||||
renderPage(numPages, num + 1)
|
||||
});
|
||||
}
|
||||
})
|
||||
@@ -555,13 +695,13 @@ window.tlrtcfile = {
|
||||
});
|
||||
}
|
||||
fileReader.readAsDataURL(file);
|
||||
}catch(e){
|
||||
} catch (e) {
|
||||
return callback && callback("加载预览pdf资源失败");
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
previewWordFile : function (options) {
|
||||
previewWordFile: function (options) {
|
||||
let { file, max, callback } = options;
|
||||
|
||||
if (file.size > max) {
|
||||
@@ -570,7 +710,7 @@ window.tlrtcfile = {
|
||||
|
||||
layer.open({
|
||||
type: 1,
|
||||
content : `
|
||||
content: `
|
||||
<div style="width:100%;height:100%;" id="tl-rtc-file-word"></div>
|
||||
`,
|
||||
area: ["80%", "80%"],
|
||||
@@ -578,15 +718,15 @@ window.tlrtcfile = {
|
||||
success: function (layero, index) {
|
||||
document.querySelector(".layui-layer-content").style.borderRadius = "15px"
|
||||
let reader = new FileReader();
|
||||
reader.onload = function(event) {
|
||||
let blob = new Blob([event.target.result], {type: file.type});
|
||||
reader.onload = function (event) {
|
||||
let blob = new Blob([event.target.result], { type: file.type });
|
||||
docx.renderAsync(blob, document.getElementById("tl-rtc-file-word"))
|
||||
};
|
||||
reader.readAsArrayBuffer(file);
|
||||
},
|
||||
});
|
||||
},
|
||||
previewExcelFile : function (options) {
|
||||
previewExcelFile: function (options) {
|
||||
let { file, max, callback } = options;
|
||||
|
||||
if (file.size > max) {
|
||||
@@ -647,7 +787,7 @@ window.tlrtcfile = {
|
||||
<img src="${reader.result}" style="max-width: 98%; max-height: 98%; margin-left: 1%; margin-top: 1%;">
|
||||
</div>
|
||||
`,
|
||||
success : function(){
|
||||
success: function () {
|
||||
document.querySelector(".layui-layer-content").style.borderRadius = "15px";
|
||||
}
|
||||
});
|
||||
|
@@ -19,7 +19,9 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
data: function () {
|
||||
let socket = null;
|
||||
if (io) {
|
||||
socket = io(resData.wsHost);
|
||||
socket = io(resData.wsHost,{
|
||||
transports : ['polling', 'websocket']
|
||||
});
|
||||
}
|
||||
return {
|
||||
langMode : "zh", // 默认中文
|
||||
@@ -424,7 +426,7 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
return ['completed', 'connected', 'checking', 'new'].includes(state);
|
||||
},
|
||||
consoleLogo : function(){
|
||||
window.console.log(`%c____ TL-RTC-FILE-V${this.version} ____ \n____ FORK ME IN GITHUB ____ \n____ https://github.com/tl-open-source/tl-rtc-file ____`, this.logo)
|
||||
window.console.log(`%c____ TL-RTC-FILE-V${this.version} ____ \n____ FORK ME ON GITHUB ____ \n____ https://github.com/tl-open-source/tl-rtc-file ____`, this.logo)
|
||||
},
|
||||
changeLanguage: function () {
|
||||
let that = this;
|
||||
|
@@ -1,7 +1,8 @@
|
||||
const express = require("express");
|
||||
const fs = require('fs');
|
||||
const https = require('https');
|
||||
const conf = require("./conf/cfg.json");
|
||||
const { inject_env_config } = require("./conf/env_config");
|
||||
const conf = inject_env_config(require("./conf/cfg.json"));
|
||||
const fileApiRouters = require("./src/controller/router")();
|
||||
const db = require("./src/tables/db");
|
||||
const utils = require("./src/utils/utils");
|
||||
|
@@ -2,7 +2,8 @@ const https = require('https');
|
||||
const socketIO = require('socket.io');
|
||||
const fs = require('fs');
|
||||
const db = require("./src/tables/db");
|
||||
const conf = require("./conf/cfg.json");
|
||||
const { inject_env_config } = require("./conf/env_config");
|
||||
const conf = inject_env_config(require("./conf/cfg.json"));
|
||||
const socket = require("./src/socket/index")
|
||||
const utils = require("./src/utils/utils");
|
||||
|
||||
|
@@ -11,7 +11,7 @@ function initData(req, res) {
|
||||
//是否开启turn
|
||||
const openTurn = (req.query.turn || "") === 'true';
|
||||
//使用的账号模式, true : 有效账号模式, false : 固定账号
|
||||
const useSecret = (req.query.secret || "") === 'true';
|
||||
const useSecret = (req.query.secret || "") === 'true' || true;
|
||||
|
||||
//ice服务器配置
|
||||
const iceServers = utils.genTurnServerIceServersConfig(openTurn, useSecret, "tlrtcfile");
|
||||
|
@@ -1,6 +1,6 @@
|
||||
const os = require('os');
|
||||
const crypto = require('crypto');
|
||||
const cfg = require('./../../conf/cfg.json');
|
||||
const crypto = require('crypto');
|
||||
|
||||
/**
|
||||
* 获取本机ip
|
||||
@@ -208,7 +208,7 @@ function genTurnServerIceServersConfig(withTurn, useSecret, username){
|
||||
}
|
||||
|
||||
// 有效账号模式
|
||||
const secret = cfg.webrtc.turn.secret || "tlrtcfile";
|
||||
const secret = cfg.webrtc.turn.secret;
|
||||
//生成账号的有效期
|
||||
const expirseTime = 60 * 60 * 24 * 1000;
|
||||
//当前时间
|
||||
@@ -222,7 +222,7 @@ function genTurnServerIceServersConfig(withTurn, useSecret, username){
|
||||
iceServers.push({
|
||||
urls : cfg.webrtc.turn.host,
|
||||
username: turnUsername,
|
||||
credential: turnPassword
|
||||
credential: turnPassword,
|
||||
})
|
||||
|
||||
return iceServers;
|
||||
|
@@ -1,5 +0,0 @@
|
||||
pm2 start npm --name=tl-rtc-file-api-server -- run sapi
|
||||
|
||||
sleep 1
|
||||
|
||||
pm2 start npm --name=tl-rtc-file-socket-server -- run ssocket
|
Reference in New Issue
Block a user