├── CHANGELOG.md ├── doc ├── gitbook │ ├── dev │ │ ├── SVR.md │ │ ├── CLIENT.md │ │ └── INTRO.md │ ├── FEATURE_LIST.md │ ├── README_DEV.md │ ├── coffee.jpeg │ ├── README.md │ ├── FAQ.md │ ├── install │ │ ├── INSTALL_BY_ZEABUR.md │ │ ├── INSTALL_BY_DOCKER_SHELL.md │ │ ├── INSTALL.md │ │ ├── INSTALL_BY_DOCKER_COMPOSE.md │ │ ├── INSTALL_BY_DOCKER_COMPOSE_WITH_ENV.md │ │ ├── INSTALL_BY_DOCKER_COMPOSE_BUILD_CODE.md │ │ └── INSTALL_BY_COMMAND_SHELL.md │ ├── book.json │ ├── SETTING.md │ └── CONF_ALL.md └── tl-rtc-file-tool.jpg ├── client ├── .vscode │ └── settings.json ├── README.md ├── packages │ └── rtc-web │ │ ├── src │ │ ├── config │ │ │ ├── reg.ts │ │ │ ├── config-enum.ts │ │ │ ├── index.ts │ │ │ ├── socket-event-name.ts │ │ │ └── constant.ts │ │ ├── types │ │ │ ├── index.ts │ │ │ └── common.ts │ │ ├── vite-env.d.ts │ │ ├── components │ │ │ ├── form-base │ │ │ │ ├── index.ts │ │ │ │ └── util.ts │ │ │ ├── menu │ │ │ │ ├── index.ts │ │ │ │ ├── menu-side.vue │ │ │ │ └── menu-list.vue │ │ │ ├── back │ │ │ │ ├── index.ts │ │ │ │ ├── back-previous-level.vue │ │ │ │ └── back-title.vue │ │ │ ├── chat-room │ │ │ │ ├── props.ts │ │ │ │ ├── index.ts │ │ │ │ ├── chat-content.vue │ │ │ │ ├── chat-message.vue │ │ │ │ ├── user-card.vue │ │ │ │ ├── chat-input.vue │ │ │ │ └── chat-room-user.vue │ │ │ ├── base │ │ │ │ ├── index.ts │ │ │ │ ├── dropdown.vue │ │ │ │ ├── svg-icon.vue │ │ │ │ ├── nav-icons.vue │ │ │ │ ├── toast.vue │ │ │ │ └── modal.vue │ │ │ ├── error-boundary │ │ │ │ └── index.vue │ │ │ ├── emoji │ │ │ │ └── index.vue │ │ │ ├── list │ │ │ │ └── select-list.vue │ │ │ ├── form-room │ │ │ │ └── form-room.vue │ │ │ ├── menu-action.vue │ │ │ └── lib.tsx │ │ ├── env.d.ts │ │ ├── assets │ │ │ ├── styles │ │ │ │ ├── tailwindcss.css │ │ │ │ └── index.css │ │ │ └── svg-icon │ │ │ │ ├── count.svg │ │ │ │ ├── camera.svg │ │ │ │ ├── back.svg │ │ │ │ ├── add-icon.svg │ │ │ │ ├── up.svg │ │ │ │ ├── chat.svg │ │ │ │ ├── mirror-image.svg │ │ │ │ ├── nav-menu.svg │ │ │ │ ├── member.svg │ │ │ │ ├── user-smail.svg │ │ │ │ ├── moon.svg │ │ │ │ ├── emoji.svg │ │ │ │ └── hang-up.svg │ │ ├── hooks │ │ │ ├── socket-utils │ │ │ │ ├── index.ts │ │ │ │ ├── useSocketUtils.ts │ │ │ │ └── useSocket.ts │ │ │ ├── useErrorCaptured.ts │ │ │ ├── index.ts │ │ │ ├── useTheme.ts │ │ │ ├── useUseragent.ts │ │ │ ├── useInitData.ts │ │ │ ├── useSwitchByUrl.ts │ │ │ └── useRouterReactive.ts │ │ ├── utils │ │ │ ├── index.ts │ │ │ ├── env.ts │ │ │ ├── reactive.ts │ │ │ └── common.ts │ │ ├── routes │ │ │ ├── index.ts │ │ │ └── router.ts │ │ ├── views │ │ │ ├── file │ │ │ │ └── file.vue │ │ │ ├── welcome.vue │ │ │ ├── video │ │ │ │ ├── video-control.vue │ │ │ │ └── video.vue │ │ │ └── chat │ │ │ │ ├── hooks │ │ │ │ └── useChat.ts │ │ │ │ └── chat.vue │ │ ├── context │ │ │ └── index.ts │ │ ├── App.vue │ │ ├── main.ts │ │ ├── keys │ │ │ ├── server.crt │ │ │ └── server.key │ │ └── layout │ │ │ └── index.vue │ │ ├── .vscode │ │ └── extensions.json │ │ ├── postcss.config.js │ │ ├── components.d.ts │ │ ├── prettier.config.cjs │ │ ├── tsconfig.node.json │ │ ├── README.md │ │ ├── .eslintignore │ │ ├── .gitignore │ │ ├── tailwind.config.js │ │ ├── index.html │ │ ├── .eslintrc.cjs │ │ ├── tsconfig.json │ │ ├── public │ │ └── vite.svg │ │ ├── package.json │ │ └── vite.config.ts ├── pnpm-workspace.yaml └── package.json ├── docker ├── coturn │ ├── Dockerfile │ ├── coturn.env │ ├── turnserver-with-fixed-user.conf │ └── turnserver-with-secret-user.conf ├── mysql │ ├── Dockerfile │ └── mysql.env └── docker-compose-build-code.yml ├── svr ├── src │ ├── bussiness │ │ ├── check │ │ │ ├── words.js │ │ │ ├── content.js │ │ │ └── core.js │ │ ├── oss │ │ │ ├── tx.js │ │ │ ├── aly.js │ │ │ ├── qiniu.js │ │ │ └── oss.js │ │ └── cache │ │ │ ├── key.js │ │ │ ├── cache.js │ │ │ └── scan │ │ │ └── scanCache.js │ ├── controller │ │ ├── comm │ │ │ ├── index.js │ │ │ └── comm.js │ │ ├── check │ │ │ ├── index.js │ │ │ └── check.js │ │ ├── dog │ │ │ ├── index.js │ │ │ └── dog.js │ │ ├── room │ │ │ └── index.js │ │ ├── file │ │ │ └── index.js │ │ ├── login │ │ │ └── index.js │ │ └── router.js │ ├── socket │ │ ├── rtcOffer │ │ │ └── offer.js │ │ ├── rtcAnswer │ │ │ └── answer.js │ │ ├── rtcHeartbeat │ │ │ └── heartbeat.js │ │ ├── rtcCandidate │ │ │ └── candidate.js │ │ ├── rtcCount │ │ │ └── count.js │ │ ├── rtcDisConnect │ │ │ └── disconnect.js │ │ ├── index.js │ │ ├── rtcToken │ │ │ └── token.js │ │ ├── rtcDraw │ │ │ └── draw.js │ │ ├── rtcSubscribe │ │ │ └── subscribe.js │ │ └── rtcExit │ │ │ └── exit.js │ ├── tables │ │ ├── relation.js │ │ ├── file.js │ │ ├── dog.js │ │ ├── user.js │ │ ├── room.js │ │ └── db.js │ └── dao │ │ └── user │ │ └── user.js ├── static │ ├── js │ │ └── vconsole.js │ ├── layui │ │ ├── font │ │ │ ├── iconfont.eot │ │ │ ├── iconfont.ttf │ │ │ ├── iconfont.woff │ │ │ └── iconfont.woff2 │ │ ├── images │ │ │ └── face │ │ │ │ ├── 0.gif │ │ │ │ ├── 1.gif │ │ │ │ ├── 2.gif │ │ │ │ ├── 3.gif │ │ │ │ ├── 4.gif │ │ │ │ ├── 5.gif │ │ │ │ └── 6.gif │ │ ├── font-ext │ │ │ ├── iconfont.ttf │ │ │ ├── iconfont.woff │ │ │ └── iconfont.woff2 │ │ └── css │ │ │ └── modules │ │ │ ├── layer │ │ │ └── default │ │ │ │ ├── icon.png │ │ │ │ ├── icon-ext.png │ │ │ │ ├── loading-0.gif │ │ │ │ ├── loading-1.gif │ │ │ │ └── loading-2.gif │ │ │ └── code.css │ └── css │ │ └── default.min.css ├── README.md ├── web-res │ ├── image │ │ ├── 44826979.png │ │ ├── coffee.jpeg │ │ ├── drawline.png │ │ ├── drawstar.png │ │ ├── drawtext.png │ │ ├── qrcode.jpeg │ │ ├── drawcircle.png │ │ ├── drawdelete.png │ │ ├── drawhexagon.png │ │ ├── drawrectangle.png │ │ ├── drawrhomboid.png │ │ ├── drawstarfill.png │ │ ├── drawtriangle.png │ │ ├── drawcirclefill.png │ │ ├── drawhexagonfill.png │ │ ├── drawrhomboidfill.png │ │ ├── drawtrianglefill.png │ │ └── drawrectanglefill.png │ ├── css │ │ └── comm.css │ ├── disclaimer.html │ └── pay.html ├── wxapp-res │ ├── images │ │ ├── demo.png │ │ ├── gitee.png │ │ ├── succ.png │ │ └── github.png │ ├── pages │ │ ├── login │ │ │ ├── login.json │ │ │ ├── login.wxml │ │ │ └── login.wxss │ │ └── succ │ │ │ ├── succ.json │ │ │ ├── succ.wxml │ │ │ ├── succ.wxss │ │ │ └── succ.js │ ├── app.wxss │ ├── app.js │ ├── app.json │ └── project.config.json ├── test │ └── test.js ├── conf │ ├── keys │ │ ├── server.crt │ │ └── server.key │ ├── env_config.js │ └── cfg.json ├── package.json ├── vite.config.js ├── tlsocket.js └── tlapi.js ├── .dockerignore ├── Dockerfile ├── bin ├── centeros │ ├── auto-stop.sh │ ├── auto-start-http.sh │ ├── auto-start-https.sh │ └── auto-update-project.sh ├── ubuntu16 │ ├── auto-stop.sh │ ├── auto-start-http.sh │ ├── auto-start-https.sh │ └── auto-update-project.sh ├── ubuntu18 │ ├── auto-stop.sh │ ├── auto-start-http.sh │ ├── auto-start-https.sh │ └── auto-update-project.sh ├── ubuntu20 │ ├── auto-stop.sh │ ├── auto-start-http.sh │ ├── auto-start-https.sh │ └── auto-update-project.sh ├── windows │ ├── auto-start-http.bat │ └── auto-start-https.bat ├── auto-push-manifest-to-hub.sh ├── auto-push-amd64-image-to-hub.sh └── auto-push-arm64-image-to-hub.sh ├── PAY.md ├── .all-contributorsrc ├── .gitignore ├── DISCLAIMER.md ├── LICENSE └── tlrtcfile.env /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /doc/gitbook/dev/SVR.md: -------------------------------------------------------------------------------- 1 | # 服务端 -------------------------------------------------------------------------------- /client/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | } -------------------------------------------------------------------------------- /doc/gitbook/FEATURE_LIST.md: -------------------------------------------------------------------------------- 1 | # 待完善补充... -------------------------------------------------------------------------------- /doc/gitbook/dev/CLIENT.md: -------------------------------------------------------------------------------- 1 | # 客户端代码 (浏览器) -------------------------------------------------------------------------------- /client/README.md: -------------------------------------------------------------------------------- 1 | # tl-rtc-file-tool-client -------------------------------------------------------------------------------- /client/packages/rtc-web/src/config/reg.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /doc/gitbook/dev/INTRO.md: -------------------------------------------------------------------------------- 1 | # 项目简介 2 | 3 | -------------------------------------------------------------------------------- /docker/coturn/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM coturn/coturn -------------------------------------------------------------------------------- /doc/gitbook/README_DEV.md: -------------------------------------------------------------------------------- 1 | # tl-rtc-file二次开发手册 -------------------------------------------------------------------------------- /docker/mysql/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mysql/mysql-server:8.0 -------------------------------------------------------------------------------- /svr/src/bussiness/check/words.js: -------------------------------------------------------------------------------- 1 | module.exports = [] -------------------------------------------------------------------------------- /svr/static/js/vconsole.js: -------------------------------------------------------------------------------- 1 | var vConsole = new VConsole(); -------------------------------------------------------------------------------- /client/packages/rtc-web/src/types/index.ts: -------------------------------------------------------------------------------- 1 | export * from './common'; 2 | -------------------------------------------------------------------------------- /svr/README.md: -------------------------------------------------------------------------------- 1 | # tl-rtc-file-tool-svr 2 | 3 | 主要用作webrtc信令交换,部分socket消息处理 -------------------------------------------------------------------------------- /svr/src/bussiness/oss/tx.js: -------------------------------------------------------------------------------- 1 | /** 2 | * oss tx api 3 | * @author iamtsm 4 | */ -------------------------------------------------------------------------------- /client/packages/rtc-web/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /svr/src/bussiness/oss/aly.js: -------------------------------------------------------------------------------- 1 | /** 2 | * oss aly api 3 | * @author iamtsm 4 | */ -------------------------------------------------------------------------------- /client/packages/rtc-web/src/components/form-base/index.ts: -------------------------------------------------------------------------------- 1 | export * from './useForm'; 2 | -------------------------------------------------------------------------------- /svr/src/bussiness/oss/qiniu.js: -------------------------------------------------------------------------------- 1 | /** 2 | * oss qiniu api 3 | * @author iamtsm 4 | */ -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | 3 | svr/node_modules/ 4 | 5 | client/packages/rtc-web/ 6 | 7 | docker/mysql/data/ -------------------------------------------------------------------------------- /doc/gitbook/coffee.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tl-open-source/tl-rtc-file/HEAD/doc/gitbook/coffee.jpeg -------------------------------------------------------------------------------- /client/packages/rtc-web/src/components/menu/index.ts: -------------------------------------------------------------------------------- 1 | export { default as MenuSide } from './menu-side.vue'; 2 | -------------------------------------------------------------------------------- /client/pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | # all packages in direct subdirs of packages/ 3 | - "packages/*" 4 | -------------------------------------------------------------------------------- /doc/tl-rtc-file-tool.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tl-open-source/tl-rtc-file/HEAD/doc/tl-rtc-file-tool.jpg -------------------------------------------------------------------------------- /client/packages/rtc-web/src/env.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'socket.io-client'; 2 | declare module 'vue3-emoji-picker'; 3 | -------------------------------------------------------------------------------- /docker/coturn/coturn.env: -------------------------------------------------------------------------------- 1 | ## coturn 配置 2 | 3 | #自动检测外网ip和中转ip 4 | # DETECT_EXTERNAL_IP=yes 5 | # DETECT_RELAY_IP=yes -------------------------------------------------------------------------------- /svr/web-res/image/44826979.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tl-open-source/tl-rtc-file/HEAD/svr/web-res/image/44826979.png -------------------------------------------------------------------------------- /svr/web-res/image/coffee.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tl-open-source/tl-rtc-file/HEAD/svr/web-res/image/coffee.jpeg -------------------------------------------------------------------------------- /svr/web-res/image/drawline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tl-open-source/tl-rtc-file/HEAD/svr/web-res/image/drawline.png -------------------------------------------------------------------------------- /svr/web-res/image/drawstar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tl-open-source/tl-rtc-file/HEAD/svr/web-res/image/drawstar.png -------------------------------------------------------------------------------- /svr/web-res/image/drawtext.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tl-open-source/tl-rtc-file/HEAD/svr/web-res/image/drawtext.png -------------------------------------------------------------------------------- /svr/web-res/image/qrcode.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tl-open-source/tl-rtc-file/HEAD/svr/web-res/image/qrcode.jpeg -------------------------------------------------------------------------------- /svr/wxapp-res/images/demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tl-open-source/tl-rtc-file/HEAD/svr/wxapp-res/images/demo.png -------------------------------------------------------------------------------- /svr/wxapp-res/images/gitee.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tl-open-source/tl-rtc-file/HEAD/svr/wxapp-res/images/gitee.png -------------------------------------------------------------------------------- /svr/wxapp-res/images/succ.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tl-open-source/tl-rtc-file/HEAD/svr/wxapp-res/images/succ.png -------------------------------------------------------------------------------- /client/packages/rtc-web/src/assets/styles/tailwindcss.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /client/packages/rtc-web/src/hooks/socket-utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from './useSocket'; 2 | export * from './useSocketUtils'; 3 | -------------------------------------------------------------------------------- /svr/web-res/image/drawcircle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tl-open-source/tl-rtc-file/HEAD/svr/web-res/image/drawcircle.png -------------------------------------------------------------------------------- /svr/web-res/image/drawdelete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tl-open-source/tl-rtc-file/HEAD/svr/web-res/image/drawdelete.png -------------------------------------------------------------------------------- /svr/web-res/image/drawhexagon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tl-open-source/tl-rtc-file/HEAD/svr/web-res/image/drawhexagon.png -------------------------------------------------------------------------------- /svr/wxapp-res/images/github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tl-open-source/tl-rtc-file/HEAD/svr/wxapp-res/images/github.png -------------------------------------------------------------------------------- /client/packages/rtc-web/src/config/config-enum.ts: -------------------------------------------------------------------------------- 1 | export const enum ConfigEnum { 2 | useRelay = 'tl-rtc-file-use-relay', 3 | } 4 | -------------------------------------------------------------------------------- /svr/static/layui/font/iconfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tl-open-source/tl-rtc-file/HEAD/svr/static/layui/font/iconfont.eot -------------------------------------------------------------------------------- /svr/static/layui/font/iconfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tl-open-source/tl-rtc-file/HEAD/svr/static/layui/font/iconfont.ttf -------------------------------------------------------------------------------- /svr/static/layui/font/iconfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tl-open-source/tl-rtc-file/HEAD/svr/static/layui/font/iconfont.woff -------------------------------------------------------------------------------- /svr/static/layui/images/face/0.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tl-open-source/tl-rtc-file/HEAD/svr/static/layui/images/face/0.gif -------------------------------------------------------------------------------- /svr/static/layui/images/face/1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tl-open-source/tl-rtc-file/HEAD/svr/static/layui/images/face/1.gif -------------------------------------------------------------------------------- /svr/static/layui/images/face/2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tl-open-source/tl-rtc-file/HEAD/svr/static/layui/images/face/2.gif -------------------------------------------------------------------------------- /svr/static/layui/images/face/3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tl-open-source/tl-rtc-file/HEAD/svr/static/layui/images/face/3.gif -------------------------------------------------------------------------------- /svr/static/layui/images/face/4.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tl-open-source/tl-rtc-file/HEAD/svr/static/layui/images/face/4.gif -------------------------------------------------------------------------------- /svr/static/layui/images/face/5.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tl-open-source/tl-rtc-file/HEAD/svr/static/layui/images/face/5.gif -------------------------------------------------------------------------------- /svr/static/layui/images/face/6.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tl-open-source/tl-rtc-file/HEAD/svr/static/layui/images/face/6.gif -------------------------------------------------------------------------------- /svr/web-res/image/drawrectangle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tl-open-source/tl-rtc-file/HEAD/svr/web-res/image/drawrectangle.png -------------------------------------------------------------------------------- /svr/web-res/image/drawrhomboid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tl-open-source/tl-rtc-file/HEAD/svr/web-res/image/drawrhomboid.png -------------------------------------------------------------------------------- /svr/web-res/image/drawstarfill.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tl-open-source/tl-rtc-file/HEAD/svr/web-res/image/drawstarfill.png -------------------------------------------------------------------------------- /svr/web-res/image/drawtriangle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tl-open-source/tl-rtc-file/HEAD/svr/web-res/image/drawtriangle.png -------------------------------------------------------------------------------- /client/packages/rtc-web/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"] 3 | } 4 | -------------------------------------------------------------------------------- /svr/static/layui/font-ext/iconfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tl-open-source/tl-rtc-file/HEAD/svr/static/layui/font-ext/iconfont.ttf -------------------------------------------------------------------------------- /svr/static/layui/font/iconfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tl-open-source/tl-rtc-file/HEAD/svr/static/layui/font/iconfont.woff2 -------------------------------------------------------------------------------- /svr/web-res/image/drawcirclefill.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tl-open-source/tl-rtc-file/HEAD/svr/web-res/image/drawcirclefill.png -------------------------------------------------------------------------------- /svr/web-res/image/drawhexagonfill.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tl-open-source/tl-rtc-file/HEAD/svr/web-res/image/drawhexagonfill.png -------------------------------------------------------------------------------- /svr/web-res/image/drawrhomboidfill.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tl-open-source/tl-rtc-file/HEAD/svr/web-res/image/drawrhomboidfill.png -------------------------------------------------------------------------------- /svr/web-res/image/drawtrianglefill.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tl-open-source/tl-rtc-file/HEAD/svr/web-res/image/drawtrianglefill.png -------------------------------------------------------------------------------- /svr/wxapp-res/pages/login/login.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": {}, 3 | "navigationBarTitleText": "", 4 | "disableScroll": true 5 | } -------------------------------------------------------------------------------- /svr/wxapp-res/pages/succ/succ.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": {}, 3 | "navigationBarTitleText": "", 4 | "disableScroll": true 5 | } -------------------------------------------------------------------------------- /svr/static/layui/font-ext/iconfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tl-open-source/tl-rtc-file/HEAD/svr/static/layui/font-ext/iconfont.woff -------------------------------------------------------------------------------- /svr/static/layui/font-ext/iconfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tl-open-source/tl-rtc-file/HEAD/svr/static/layui/font-ext/iconfont.woff2 -------------------------------------------------------------------------------- /svr/web-res/image/drawrectanglefill.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tl-open-source/tl-rtc-file/HEAD/svr/web-res/image/drawrectanglefill.png -------------------------------------------------------------------------------- /client/packages/rtc-web/src/config/index.ts: -------------------------------------------------------------------------------- 1 | export * from './config-enum'; 2 | export * from './constant'; 3 | export * from './socket-event-name'; 4 | -------------------------------------------------------------------------------- /client/packages/rtc-web/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from './common'; 2 | export * from './user'; 3 | export * from './reactive'; 4 | export * from './env'; 5 | -------------------------------------------------------------------------------- /svr/static/layui/css/modules/layer/default/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tl-open-source/tl-rtc-file/HEAD/svr/static/layui/css/modules/layer/default/icon.png -------------------------------------------------------------------------------- /svr/static/layui/css/modules/layer/default/icon-ext.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tl-open-source/tl-rtc-file/HEAD/svr/static/layui/css/modules/layer/default/icon-ext.png -------------------------------------------------------------------------------- /svr/static/layui/css/modules/layer/default/loading-0.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tl-open-source/tl-rtc-file/HEAD/svr/static/layui/css/modules/layer/default/loading-0.gif -------------------------------------------------------------------------------- /svr/static/layui/css/modules/layer/default/loading-1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tl-open-source/tl-rtc-file/HEAD/svr/static/layui/css/modules/layer/default/loading-1.gif -------------------------------------------------------------------------------- /svr/static/layui/css/modules/layer/default/loading-2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tl-open-source/tl-rtc-file/HEAD/svr/static/layui/css/modules/layer/default/loading-2.gif -------------------------------------------------------------------------------- /svr/wxapp-res/pages/succ/succ.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 登录成功 5 | -------------------------------------------------------------------------------- /client/packages/rtc-web/src/components/back/index.ts: -------------------------------------------------------------------------------- 1 | export { default as BackPreviousLevel } from './back-previous-level.vue'; 2 | export { default as BackTitle } from './back-title.vue'; 3 | -------------------------------------------------------------------------------- /doc/gitbook/README.md: -------------------------------------------------------------------------------- 1 | ## tl-rtc-file 2 | 3 | 欢迎使用,觉得不错的可以在github或gitee上面start/watch/fork 一键三连,感兴趣/有开发能力的可以提交你的代码共同维护 4 | 5 | ## 如果觉得项目不错,可以赞赏支持开发者进行后续的维护 6 | 7 | ![image](coffee.jpeg) 8 | -------------------------------------------------------------------------------- /client/packages/rtc-web/src/utils/env.ts: -------------------------------------------------------------------------------- 1 | export const isProd = () => { 2 | return import.meta.env.PROD; 3 | }; 4 | 5 | export const isDev = () => { 6 | return import.meta.env.DEV; 7 | }; 8 | -------------------------------------------------------------------------------- /doc/gitbook/FAQ.md: -------------------------------------------------------------------------------- 1 | # 常见问题 2 | 3 | ## 4 | 5 | ``` 6 | 7 | ``` 8 | 9 | ## 10 | 11 | ``` 12 | 13 | ``` 14 | 15 | ## 16 | 17 | ``` 18 | 19 | ``` 20 | 21 | ## 22 | 23 | ``` 24 | 25 | ``` -------------------------------------------------------------------------------- /docker/mysql/mysql.env: -------------------------------------------------------------------------------- 1 | #mysql env feature use 2 | 3 | #设置root密码 4 | MYSQL_ROOT_PASSWORD=tlrtcfile 5 | #设置数据库 6 | MYSQL_DATABASE=webchat 7 | #设置用户 8 | MYSQL_USER=tlrtcfile 9 | MYSQL_PASSWORD=tlrtcfile -------------------------------------------------------------------------------- /client/packages/rtc-web/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | 'postcss-import': {}, 4 | 'tailwindcss/nesting': {}, 5 | tailwindcss: {}, 6 | autoprefixer: {}, 7 | }, 8 | }; 9 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:lts-alpine 2 | 3 | COPY . /tlrtcfile 4 | 5 | WORKDIR /tlrtcfile/svr 6 | 7 | RUN npm install --registry=https://registry.npmmirror.com && npm run build:pro 8 | 9 | ENTRYPOINT ["node"] 10 | -------------------------------------------------------------------------------- /client/packages/rtc-web/src/components/chat-room/props.ts: -------------------------------------------------------------------------------- 1 | export type UserCardProps = { 2 | iconname?: string; 3 | iconcolor?: string; 4 | username?: string; 5 | time?: string; 6 | reverse?: boolean; 7 | }; 8 | -------------------------------------------------------------------------------- /client/packages/rtc-web/src/types/common.ts: -------------------------------------------------------------------------------- 1 | export type CommonFnType = (...args: any[]) => any; 2 | 3 | export type StringKeyObject = Record; 4 | 5 | export type PathValue = T[P]; 6 | -------------------------------------------------------------------------------- /doc/gitbook/install/INSTALL_BY_ZEABUR.md: -------------------------------------------------------------------------------- 1 | # 通过zeabur托管平台部署 2 | 3 | 这种托管平台一键部署的形式,目前仅仅开发支持了 `api服务` 和 `socket服务` 仅提供基础功能可用性 4 | 5 | `mysql服务` 和 `coturn服务`都是暂不提供的,并且代码分支可能存在同步不及时的情况。 6 | 7 | 所以这就导致了,托管平台一键部署的有一些使用局限性 8 | 9 | 后面有时间会继续同步更新这种部署模式 -------------------------------------------------------------------------------- /client/packages/rtc-web/components.d.ts: -------------------------------------------------------------------------------- 1 | // components.d.ts 2 | declare module 'vue' { 3 | export interface GlobalComponents { 4 | SvgIcon: typeof import('./src/components/base/svg-icon.vue')['default']; 5 | } 6 | } 7 | 8 | export {}; 9 | -------------------------------------------------------------------------------- /client/packages/rtc-web/src/routes/index.ts: -------------------------------------------------------------------------------- 1 | import { createWebHistory, createRouter } from 'vue-router'; 2 | 3 | import { routes } from './router'; 4 | 5 | export default createRouter({ 6 | history: createWebHistory(), 7 | routes, 8 | }); 9 | -------------------------------------------------------------------------------- /client/packages/rtc-web/src/views/file/file.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /client/packages/rtc-web/prettier.config.cjs: -------------------------------------------------------------------------------- 1 | // prettier.config.cjs 2 | module.exports = { 3 | printWidth: 80, //单行长度 4 | tabWidth: 2, //缩进长度 5 | semi: true, //句末使用分号 6 | singleQuote: true, //使用单引号 7 | plugins: [require("prettier-plugin-tailwindcss")], 8 | }; 9 | -------------------------------------------------------------------------------- /svr/wxapp-res/app.wxss: -------------------------------------------------------------------------------- 1 | /* *app.wxss* */ 2 | 3 | .container { 4 | height: 100%; 5 | display: flex; 6 | flex-direction: column; 7 | align-items: center; 8 | justify-content: space-between; 9 | padding: 200rpx 0; 10 | box-sizing: border-box; 11 | } 12 | -------------------------------------------------------------------------------- /client/packages/rtc-web/src/utils/reactive.ts: -------------------------------------------------------------------------------- 1 | import { CommonFnType } from '@/types'; 2 | import { computed, MaybeRef, ref } from 'vue'; 3 | export function resolveRef(r: MaybeRef | CommonFnType) { 4 | return typeof r === 'function' ? computed(r as any) : ref(r); 5 | } 6 | -------------------------------------------------------------------------------- /svr/src/controller/comm/index.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const comm = require("./comm"); 3 | 4 | 5 | module.exports = function () { 6 | const router = express.Router(); 7 | 8 | router.get("/initData",comm.initData); 9 | 10 | return router; 11 | } -------------------------------------------------------------------------------- /client/packages/rtc-web/src/views/welcome.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /svr/src/controller/check/index.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const check = require("./check"); 3 | 4 | 5 | module.exports = function () { 6 | const router = express.Router(); 7 | 8 | router.get("/check", check.checkContent); 9 | 10 | return router; 11 | } -------------------------------------------------------------------------------- /svr/src/controller/dog/index.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const dog = require('./dog'); 3 | 4 | 5 | module.exports = function () { 6 | const router = express.Router(); 7 | 8 | router.get("/question/list", dog.getQuestionList); 9 | 10 | return router; 11 | } -------------------------------------------------------------------------------- /client/packages/rtc-web/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true 8 | }, 9 | "include": ["vite.config.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /doc/gitbook/book.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "tl-rtc-file", 3 | "language": "zh-hans", 4 | "author": "iamtsm", 5 | "links": { 6 | "sidebar": { 7 | "iamtsm": "https://im.iamtsm.cn" 8 | } 9 | }, 10 | "plugins": [ 11 | "-search", 12 | "-sharing" 13 | ] 14 | } -------------------------------------------------------------------------------- /svr/wxapp-res/app.js: -------------------------------------------------------------------------------- 1 | //app.js 2 | App({ 3 | onLaunch: async function () { 4 | 5 | }, 6 | globalData: { 7 | userInfo: null, //用户信息 8 | baseUrl: "http://localhost:9092", //访问路径 9 | openId: '', //用户唯一标识 10 | loginState: false, //用户登录状态 11 | token: '', //用户登录返回的token 12 | } 13 | }) -------------------------------------------------------------------------------- /client/packages/rtc-web/src/components/base/index.ts: -------------------------------------------------------------------------------- 1 | export { default as SvgIcon } from './svg-icon.vue'; 2 | export { default as NavIcons } from './nav-icons.vue'; 3 | export { default as Modal } from './modal.vue'; 4 | export { default as Dropdown } from './dropdown.vue'; 5 | export { default as ToastBox } from './toast.vue'; 6 | -------------------------------------------------------------------------------- /client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "client", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "dev:web": "pnpm -C packages/rtc-web dev", 8 | "build:web": "pnpm -C packages/rtc-web build" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC" 13 | } -------------------------------------------------------------------------------- /client/packages/rtc-web/src/hooks/useErrorCaptured.ts: -------------------------------------------------------------------------------- 1 | import { onErrorCaptured, ref } from 'vue'; 2 | 3 | // 捕获 组件error 4 | export const useErrorCaptured = () => { 5 | const errors = ref([]); 6 | 7 | onErrorCaptured((e) => { 8 | errors.value.push(e.message); 9 | }); 10 | 11 | return errors; 12 | }; 13 | -------------------------------------------------------------------------------- /bin/centeros/auto-stop.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ######################### 3 | # 提供pm2删除停止服务的脚本 4 | # 包含api服务,socket服务 5 | # @auther: iamtsm 6 | # @version: v1.0.0 7 | ######################### 8 | 9 | pm2 del tl-rtc-file-api 10 | 11 | pm2 del tl-rtc-file-socket 12 | 13 | echo "stop and [tl-rtc-file-api] / [tl-rtc-file-socket] pm2 processes ok" -------------------------------------------------------------------------------- /bin/ubuntu16/auto-stop.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ######################### 3 | # 提供pm2删除停止服务的脚本 4 | # 包含api服务,socket服务 5 | # @auther: iamtsm 6 | # @version: v1.0.0 7 | ######################### 8 | 9 | pm2 del tl-rtc-file-api 10 | 11 | pm2 del tl-rtc-file-socket 12 | 13 | echo "stop and [tl-rtc-file-api] / [tl-rtc-file-socket] pm2 processes ok" -------------------------------------------------------------------------------- /bin/ubuntu18/auto-stop.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ######################### 3 | # 提供pm2删除停止服务的脚本 4 | # 包含api服务,socket服务 5 | # @auther: iamtsm 6 | # @version: v1.0.0 7 | ######################### 8 | 9 | pm2 del tl-rtc-file-api 10 | 11 | pm2 del tl-rtc-file-socket 12 | 13 | echo "stop and [tl-rtc-file-api] / [tl-rtc-file-socket] pm2 processes ok" -------------------------------------------------------------------------------- /bin/ubuntu20/auto-stop.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ######################### 3 | # 提供pm2删除停止服务的脚本 4 | # 包含api服务,socket服务 5 | # @auther: iamtsm 6 | # @version: v1.0.0 7 | ######################### 8 | 9 | pm2 del tl-rtc-file-api 10 | 11 | pm2 del tl-rtc-file-socket 12 | 13 | echo "stop and [tl-rtc-file-api] / [tl-rtc-file-socket] pm2 processes ok" -------------------------------------------------------------------------------- /client/packages/rtc-web/README.md: -------------------------------------------------------------------------------- 1 | # rtc-web 2 | 3 | ## 目的 4 | 5 | web 端,同时响应式适配小屏幕 6 | 7 | ## 技术选型 8 | 9 | - vue 3.3 10 | - vite 4 11 | - daisyui (基于 tailwindcss 的 components) 12 | 13 | ## 一些目录说明 14 | 15 | - components/lib (一些用于基础样式的组件) 16 | 17 | - components/base (基础复用的组件) 18 | 19 | - hooks/ (全局可以复用的 hook) 20 | 21 | - view/ (页面) 22 | -------------------------------------------------------------------------------- /svr/src/controller/room/index.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | 3 | 4 | module.exports = function () { 5 | const router = express.Router(); 6 | 7 | router.get("/room", (req, res) => { 8 | res.json({ 9 | room : "request room api ok!" 10 | }) 11 | }); 12 | 13 | return router; 14 | } -------------------------------------------------------------------------------- /client/packages/rtc-web/.eslintignore: -------------------------------------------------------------------------------- 1 | *.sh 2 | *.md 3 | *.woff 4 | *.ttf 5 | *.yaml 6 | .vscode 7 | .idea 8 | node_modules 9 | dist 10 | public 11 | docs 12 | .husky 13 | .eslintrc.js 14 | 15 | # Allowlist 'test.js' in the '.build' folder 16 | # But do not allow anything else in the '.build' folder to be linted 17 | !.build 18 | .build/* 19 | !.build/test.js -------------------------------------------------------------------------------- /bin/windows/auto-start-http.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | REM Start the first process 4 | start "" pm2 start npm --name=tl-rtc-file-api -- run http-api 5 | timeout /t 1 /nobreak 6 | 7 | REM Start the second process 8 | start "" pm2 start npm --name=tl-rtc-file-socket -- run http-socket 9 | timeout /t 1 /nobreak 10 | 11 | REM Run npm build command 12 | npm run build:pro -------------------------------------------------------------------------------- /bin/windows/auto-start-https.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | REM Start the first process 4 | start "" pm2 start npm --name=tl-rtc-file-api -- run https-api 5 | timeout /t 1 /nobreak 6 | 7 | REM Start the second process 8 | start "" pm2 start npm --name=tl-rtc-file-socket -- run https-socket 9 | timeout /t 1 /nobreak 10 | 11 | REM Run npm build command 12 | npm run build:pro -------------------------------------------------------------------------------- /client/packages/rtc-web/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode 17 | .idea 18 | .DS_Store 19 | *.suo 20 | *.ntvs* 21 | *.njsproj 22 | *.sln 23 | *.sw? 24 | -------------------------------------------------------------------------------- /client/packages/rtc-web/src/components/chat-room/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ChatInput } from './chat-input.vue'; 2 | export { default as ChatMessage } from './chat-message.vue'; 3 | export { default as ChatRoomUser } from './chat-room-user.vue'; 4 | export { default as ChatContent } from './chat-content.vue'; 5 | export { default as UserCard } from './user-card.vue'; 6 | -------------------------------------------------------------------------------- /client/packages/rtc-web/src/hooks/index.ts: -------------------------------------------------------------------------------- 1 | export * from './useTheme'; 2 | export * from './useRoom'; 3 | export * from './useInitData'; 4 | export * from './socket-utils'; 5 | export * from './useRouterReactive'; 6 | export * from './useDragChangeSize'; 7 | export * from './useSwitchByUrl'; 8 | export * from './useErrorCaptured'; 9 | export * from './useUseragent'; 10 | -------------------------------------------------------------------------------- /client/packages/rtc-web/src/components/menu/menu-side.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 14 | 15 | 18 | -------------------------------------------------------------------------------- /client/packages/rtc-web/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | export default { 3 | darkMode: ['class', '[data-theme="dark"]'], 4 | content: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'], 5 | daisyui: { 6 | themes: ['light', 'dark'], 7 | }, 8 | theme: { 9 | extend: {}, 10 | }, 11 | plugins: [require('daisyui')], 12 | }; 13 | -------------------------------------------------------------------------------- /svr/src/controller/file/index.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const file = require("../../dao/file/file") 3 | 4 | module.exports = function () { 5 | const router = express.Router(); 6 | 7 | router.get("/file", (req, res) => { 8 | res.json({ 9 | dog : "request file api ok!" 10 | }) 11 | }); 12 | 13 | return router; 14 | } -------------------------------------------------------------------------------- /bin/centeros/auto-start-http.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ######################### 3 | # 提供pm2启动管理http服务的脚本 4 | # 包含api服务,socket服务 5 | # @auther: iamtsm 6 | # @version: v1.0.0 7 | ######################### 8 | 9 | pm2 start npm --name=tl-rtc-file-api -- run http-api 10 | 11 | sleep 1 12 | 13 | pm2 start npm --name=tl-rtc-file-socket -- run http-socket 14 | 15 | sleep 1 16 | 17 | npm run build:pro -------------------------------------------------------------------------------- /bin/centeros/auto-start-https.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ######################### 3 | # 提供pm2启动管理https服务的脚本 4 | # 包含api服务,socket服务 5 | # @auther: iamtsm 6 | # @version: v1.0.0 7 | ######################### 8 | 9 | pm2 start npm --name=tl-rtc-file-api -- run https-api 10 | 11 | sleep 1 12 | 13 | pm2 start npm --name=tl-rtc-file-socket -- run https-socket 14 | 15 | sleep 1 16 | 17 | npm run build:pro -------------------------------------------------------------------------------- /bin/ubuntu16/auto-start-http.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ######################### 3 | # 提供pm2启动管理http服务的脚本 4 | # 包含api服务,socket服务 5 | # @auther: iamtsm 6 | # @version: v1.0.0 7 | ######################### 8 | 9 | pm2 start npm --name=tl-rtc-file-api -- run http-api 10 | 11 | sleep 1 12 | 13 | pm2 start npm --name=tl-rtc-file-socket -- run http-socket 14 | 15 | sleep 1 16 | 17 | npm run build:pro -------------------------------------------------------------------------------- /bin/ubuntu16/auto-start-https.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ######################### 3 | # 提供pm2启动管理https服务的脚本 4 | # 包含api服务,socket服务 5 | # @auther: iamtsm 6 | # @version: v1.0.0 7 | ######################### 8 | 9 | pm2 start npm --name=tl-rtc-file-api -- run https-api 10 | 11 | sleep 1 12 | 13 | pm2 start npm --name=tl-rtc-file-socket -- run https-socket 14 | 15 | sleep 1 16 | 17 | npm run build:pro -------------------------------------------------------------------------------- /bin/ubuntu18/auto-start-http.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ######################### 3 | # 提供pm2启动管理http服务的脚本 4 | # 包含api服务,socket服务 5 | # @auther: iamtsm 6 | # @version: v1.0.0 7 | ######################### 8 | 9 | pm2 start npm --name=tl-rtc-file-api -- run http-api 10 | 11 | sleep 1 12 | 13 | pm2 start npm --name=tl-rtc-file-socket -- run http-socket 14 | 15 | sleep 1 16 | 17 | npm run build:pro -------------------------------------------------------------------------------- /bin/ubuntu18/auto-start-https.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ######################### 3 | # 提供pm2启动管理https服务的脚本 4 | # 包含api服务,socket服务 5 | # @auther: iamtsm 6 | # @version: v1.0.0 7 | ######################### 8 | 9 | pm2 start npm --name=tl-rtc-file-api -- run https-api 10 | 11 | sleep 1 12 | 13 | pm2 start npm --name=tl-rtc-file-socket -- run https-socket 14 | 15 | sleep 1 16 | 17 | npm run build:pro -------------------------------------------------------------------------------- /bin/ubuntu20/auto-start-http.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ######################### 3 | # 提供pm2启动管理http服务的脚本 4 | # 包含api服务,socket服务 5 | # @auther: iamtsm 6 | # @version: v1.0.0 7 | ######################### 8 | 9 | pm2 start npm --name=tl-rtc-file-api -- run http-api 10 | 11 | sleep 1 12 | 13 | pm2 start npm --name=tl-rtc-file-socket -- run http-socket 14 | 15 | sleep 1 16 | 17 | npm run build:pro -------------------------------------------------------------------------------- /bin/ubuntu20/auto-start-https.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ######################### 3 | # 提供pm2启动管理https服务的脚本 4 | # 包含api服务,socket服务 5 | # @auther: iamtsm 6 | # @version: v1.0.0 7 | ######################### 8 | 9 | pm2 start npm --name=tl-rtc-file-api -- run https-api 10 | 11 | sleep 1 12 | 13 | pm2 start npm --name=tl-rtc-file-socket -- run https-socket 14 | 15 | sleep 1 16 | 17 | npm run build:pro -------------------------------------------------------------------------------- /client/packages/rtc-web/src/assets/styles/index.css: -------------------------------------------------------------------------------- 1 | @import './tailwindcss'; 2 | 3 | /* width */ 4 | ::-webkit-scrollbar { 5 | width: 4px; 6 | } 7 | 8 | /* Track */ 9 | ::-webkit-scrollbar-track { 10 | background: #f1f1f1; 11 | } 12 | 13 | /* Handle */ 14 | ::-webkit-scrollbar-thumb { 15 | background: #888; 16 | } 17 | 18 | /* Handle on hover */ 19 | ::-webkit-scrollbar-thumb:hover { 20 | background: #555; 21 | } 22 | -------------------------------------------------------------------------------- /client/packages/rtc-web/src/context/index.ts: -------------------------------------------------------------------------------- 1 | import { InjectionKey, Ref } from 'vue'; 2 | 3 | export type InitDataKeyType = Partial<{ 4 | langMode: string; 5 | socket: any; 6 | logo: string; 7 | version: string; 8 | options: RTCOfferOptions; 9 | config: { 10 | iceServers: RTCIceServer[]; 11 | }; 12 | }>; 13 | 14 | export const InitDataKey: InjectionKey> = 15 | Symbol('initDataKey'); 16 | -------------------------------------------------------------------------------- /client/packages/rtc-web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Rtc 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /client/packages/rtc-web/src/config/socket-event-name.ts: -------------------------------------------------------------------------------- 1 | export const enum SocketEventName { 2 | Count = 'count', 3 | CreateAndJoin = 'createAndJoin', 4 | RoomCreated = 'created', 5 | RoomExit = 'exit', 6 | RoomJoin = 'joined', 7 | RoomOffer = 'offer', 8 | RoomAnswer = 'answer', 9 | RoomCandidate = 'candidate', 10 | } 11 | 12 | // chat 的事件名 13 | export const enum ChatEventName { 14 | ChatingRoom = 'chatingRoom', 15 | } 16 | -------------------------------------------------------------------------------- /svr/wxapp-res/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "pages": [ 3 | "pages/login/login", 4 | "pages/succ/succ" 5 | ], 6 | "window": { 7 | "navigationBarBackgroundColor": "#fff", 8 | "navigationBarTitleText": "tl-rtc-file", 9 | "navigationBarTextStyle": "black", 10 | "backgroundColor": "#eeeeee", 11 | "backgroundTextStyle": "dark" 12 | }, 13 | "networkTimeout": { 14 | "request": 10000, 15 | "downloadFile": 10000 16 | } 17 | } -------------------------------------------------------------------------------- /doc/gitbook/install/INSTALL_BY_DOCKER_SHELL.md: -------------------------------------------------------------------------------- 1 | # docker命令一键脚本部署 2 | 3 | 为了简便大家的部署操作,项目默认提供了docker命令的一键部署脚本,macos, ubuntu都可以用 4 | 5 | 脚本放在 `bin/` 目录中,脚本名称为 `auto-pull-and-start-docker.sh` 6 | 7 | ### 进入脚本目录 8 | 9 | ``` 10 | cd bin/ 11 | ``` 12 | 13 | ### 给脚本执行权限 14 | 15 | ``` 16 | chmod +x ./auto-pull-and-start-docker.sh 17 | ``` 18 | 19 | ### 执行脚本 20 | 21 | ``` 22 | ./auto-pull-and-start-docker.sh 23 | ``` 24 | 25 | ![执行脚本](https://qnproxy.iamtsm.cn/image-15.png "执行脚本") -------------------------------------------------------------------------------- /client/packages/rtc-web/src/assets/svg-icon/count.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/packages/rtc-web/src/components/back/back-previous-level.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 20 | -------------------------------------------------------------------------------- /client/packages/rtc-web/src/components/form-base/util.ts: -------------------------------------------------------------------------------- 1 | import { isObject } from 'lodash'; 2 | 3 | type Event = { target: any }; 4 | 5 | export const isCheckBoxInput = (element: any): element is HTMLInputElement => 6 | element.type === 'checkbox'; 7 | 8 | export const getEventValue = (event: unknown) => 9 | isObject(event) && (event as Event).target 10 | ? isCheckBoxInput((event as Event).target) 11 | ? (event as Event).target.checked 12 | : (event as Event).target.value 13 | : event; 14 | -------------------------------------------------------------------------------- /client/packages/rtc-web/src/hooks/socket-utils/useSocketUtils.ts: -------------------------------------------------------------------------------- 1 | import { SocketEventName } from '@/config'; 2 | import { useSocket } from './useSocket'; 3 | import { ref } from 'vue'; 4 | 5 | export const useSocketCount = () => { 6 | const count = ref(0); 7 | const handleSocketCount = (socket: any) => { 8 | socket.on(SocketEventName.Count, (data: any) => { 9 | count.value = data.mc; 10 | }); 11 | }; 12 | 13 | useSocket(handleSocketCount); 14 | 15 | return { 16 | data: count, 17 | }; 18 | }; 19 | -------------------------------------------------------------------------------- /client/packages/rtc-web/src/routes/router.ts: -------------------------------------------------------------------------------- 1 | export const routes = [ 2 | { path: '/', component: () => import('@/views/welcome.vue') }, 3 | { path: '/chat/:roomId', component: () => import('@/views/chat/chat.vue') }, 4 | { 5 | path: '/video/:roomId', 6 | component: () => import('@/views/video/video.vue'), 7 | }, 8 | // 将匹配所有内容并将其放在 `$route.params.pathMatch` 下 9 | { 10 | path: '/:pathMatch(.*)*', 11 | name: 'NotFound', 12 | redirect() { 13 | return { path: '/' }; 14 | }, 15 | }, 16 | ]; 17 | -------------------------------------------------------------------------------- /client/packages/rtc-web/src/hooks/socket-utils/useSocket.ts: -------------------------------------------------------------------------------- 1 | import { InitDataKey } from '@/context'; 2 | import { CommonFnType } from '@/types'; 3 | import { inject, watch } from 'vue'; 4 | 5 | export const useSocket = (fn?: CommonFnType) => { 6 | const initData = inject(InitDataKey); 7 | 8 | if (initData) { 9 | watch( 10 | () => initData.value.socket, 11 | async (v) => { 12 | if (v) { 13 | await fn?.(v); 14 | } 15 | }, 16 | { 17 | immediate: true, 18 | } 19 | ); 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /client/packages/rtc-web/src/assets/svg-icon/camera.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/packages/rtc-web/src/App.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 21 | -------------------------------------------------------------------------------- /svr/src/controller/check/check.js: -------------------------------------------------------------------------------- 1 | const check = require("../../bussiness/check/content"); 2 | 3 | /** 4 | * 检查内容 5 | * @param {*} req 6 | * @param {*} res 7 | * @returns 8 | */ 9 | function checkContent(req, res) { 10 | try { 11 | let content = req.query.content; 12 | if(content.length > 1000){ 13 | res.json({ 14 | content: "内容过长" 15 | }) 16 | return; 17 | } 18 | res.json({ 19 | content: check.contentFilter(content) 20 | }) 21 | } catch (err) { 22 | console.log(err); 23 | res.json({ 24 | content: "", 25 | err : "系统繁忙" 26 | }) 27 | } 28 | } 29 | 30 | module.exports = { 31 | checkContent 32 | } -------------------------------------------------------------------------------- /svr/src/controller/dog/dog.js: -------------------------------------------------------------------------------- 1 | const dogDao = require('../../dao/dog/dog.js'); 2 | 3 | /** 4 | * @description 获取问题列表 5 | */ 6 | async function getQuestionList(req, res){ 7 | try { 8 | 9 | const list = await dogDao.getDogSendBugInfo({ 10 | limit : 30 11 | }, req.ctx.tables, req.ctx.dbClient); 12 | 13 | res.json({ 14 | questionList : list, 15 | code : 200, 16 | msg : "ok" 17 | }) 18 | 19 | } catch (err) { 20 | console.log(err); 21 | res.json({ 22 | content: "", 23 | err : "系统繁忙" 24 | }) 25 | } 26 | } 27 | 28 | module.exports = { 29 | getQuestionList 30 | } -------------------------------------------------------------------------------- /client/packages/rtc-web/src/assets/svg-icon/back.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docker/coturn/turnserver-with-fixed-user.conf: -------------------------------------------------------------------------------- 1 | #------------TURN BASE CONFIG 基础配置------------# 2 | #监听网卡 3 | # listening-device=eth0 4 | #监听ip(内网ip) 5 | # listening-ip= 6 | #监听端口(端口) 7 | listening-port=3478 8 | #公网ip 9 | # external-ip= 10 | #端口最小值 11 | min-port=49152 12 | #端口最大值 13 | min-port=55000 14 | #cli密码 15 | cli-password=qwerty 16 | #后台运行-默认关闭 docker运行时需关闭, 自行部署时,可以开启 17 | # daemon 18 | #会话指纹 19 | fingerprint 20 | #中等详细日志 21 | verbose 22 | #长期凭证 23 | lt-cred-mech 24 | #关闭tls 25 | no-tls 26 | #关闭dtls 27 | no-dtls 28 | 29 | #------------TURN USER CONFIG 固定帐号密码模式------------# 30 | #用户账号密码 31 | user=tlrtcfile:tlrtcfile 32 | #来源(域名或ip:port) 33 | realm=tlrtcfile.com -------------------------------------------------------------------------------- /client/packages/rtc-web/src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue'; 2 | import './assets/styles/index.css'; 3 | 4 | import router from './routes'; 5 | 6 | import 'virtual:svg-icons-register'; 7 | import SvgIcon from '@/components/base/svg-icon.vue'; 8 | 9 | import App from './App.vue'; 10 | 11 | import VConsole from 'vconsole'; 12 | import { useUserAgent } from '@/hooks'; 13 | import { isDev } from '@/utils'; 14 | 15 | const { isMobile } = useUserAgent(); 16 | 17 | function setup() { 18 | if (isMobile && isDev()) { 19 | new VConsole(); 20 | } 21 | return createApp(App).use(router).component('svg-icon', SvgIcon); 22 | } 23 | 24 | setup().mount('#app'); 25 | -------------------------------------------------------------------------------- /svr/wxapp-res/project.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "appid": "", 3 | "projectname": "", 4 | "compileType": "miniprogram", 5 | "libVersion": "3.0.2", 6 | "packOptions": { 7 | "ignore": [], 8 | "include": [] 9 | }, 10 | "setting": { 11 | "coverView": true, 12 | "es6": true, 13 | "postcss": true, 14 | "minified": true, 15 | "showShadowRootInWxmlPanel": true, 16 | "compileHotReLoad": true, 17 | "babelSetting": { 18 | "ignore": [], 19 | "disablePlugins": [], 20 | "outputPath": "" 21 | } 22 | }, 23 | "condition": {}, 24 | "editorSetting": { 25 | "tabIndent": "insertSpaces", 26 | "tabSize": 2 27 | } 28 | } -------------------------------------------------------------------------------- /client/packages/rtc-web/src/assets/svg-icon/add-icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/packages/rtc-web/src/components/back/back-title.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 27 | -------------------------------------------------------------------------------- /svr/src/controller/login/index.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const login = require("./login"); 3 | 4 | module.exports = function () { 5 | const router = express.Router(); 6 | 7 | router.post("/wechat", login.loginWechat); 8 | 9 | router.get("/qrcode", login.getLoginWechatQrCode); 10 | 11 | router.post("/scanState", login.scanState); 12 | 13 | router.get("/scanState", login.scanState); 14 | 15 | router.get("/getScanState", login.getScanState); 16 | 17 | router.get("/state", login.getTokenState); 18 | 19 | router.get("/logout", login.logout); 20 | 21 | router.post("/info", login.getLoginInfo); 22 | 23 | return router; 24 | } -------------------------------------------------------------------------------- /svr/src/bussiness/oss/oss.js: -------------------------------------------------------------------------------- 1 | const aly = require('./aly'); 2 | const tx = require('./tx'); 3 | const qiniu = require('./qiniu'); 4 | const seafile = require('./seafile'); 5 | const {inject_env_config} = require("./../../../conf/env_config"); 6 | const cfg = inject_env_config(require('./../../../conf/cfg.json')); 7 | 8 | const oss = { 9 | aly, 10 | tx, 11 | qiniu, 12 | seafile, 13 | } 14 | 15 | /** 16 | * 获取上传token 17 | */ 18 | const getUploadToken = async function(){ 19 | 20 | } 21 | 22 | /** 23 | * 获取上传url 24 | */ 25 | const getUploadUrl = async function(){ 26 | 27 | } 28 | 29 | /** 30 | * 获取下载url 31 | */ 32 | const getDownLoadUrl = async function(){ 33 | 34 | } -------------------------------------------------------------------------------- /svr/src/bussiness/cache/key.js: -------------------------------------------------------------------------------- 1 | 2 | const prefix = "tl-rtc-file-"; 3 | 4 | // 状态缓存 5 | const stateKeys = { 6 | // 扫码状态缓存 7 | SCAN_STATE_KEY : prefix + "scene-state-", 8 | // 登录状态缓存 9 | TOKEN_STATE_KEY : prefix + "token-state-" 10 | } 11 | 12 | // 信息缓存 13 | const infoKeys = { 14 | // 登录信息缓存 15 | LOGIN_INFO_KEY : prefix + "login-info-", 16 | // 用户信息缓存 17 | USER_INFO_KEY : prefix + "user-info-", 18 | } 19 | 20 | // cookie key 21 | const cookieKey = { 22 | // 用户登录cookie key 23 | USER_LOGIN_COOKIE_KEY : "_tl_u", 24 | } 25 | 26 | module.exports = { 27 | StateKey : stateKeys, 28 | InfoKey: infoKeys, 29 | CookieKey: cookieKey, 30 | }; 31 | 32 | -------------------------------------------------------------------------------- /svr/src/socket/rtcOffer/offer.js: -------------------------------------------------------------------------------- 1 | const rtcConstant = require("../rtcConstant"); 2 | const rtcClientEvent = rtcConstant.rtcClientEvent 3 | /** 4 | * webrtc offer 5 | * 转发offer消息至room其他客户端 [from,to,room,sdp] 6 | * @param {*} io socketio对象 7 | * @param {*} socket 单个socket连接 8 | * @param {*} tables 数据表对象 9 | * @param {*} dbClient sequelize-orm对象 10 | * @param {*} data event参数 11 | * @returns 12 | */ 13 | async function offer(io, socket, tables, dbClient, data){ 14 | let otherClient = io.sockets.connected[data.to]; 15 | if (!otherClient) { 16 | return; 17 | } 18 | otherClient.emit(rtcClientEvent.offer, data); 19 | } 20 | 21 | module.exports = { 22 | offer 23 | } -------------------------------------------------------------------------------- /PAY.md: -------------------------------------------------------------------------------- 1 | 2 | # 开源项目定制服务收费标准 (v1.0.3.2023) 3 | 4 | - **联系方式:** 1905333456 (qq) 5 | - **GitHub:** https://github.com/iamtsm 6 | 7 | 8 | ## 项目定制服务 9 | 10 | ### 1. 定制开发支持 11 | - **服务内容:** 免费提供定制开发技术支持,可协助部署安装相关事项,或协助自行开发,细节问题答疑 12 | - **免费:** 非常乐意为各位使用的小伙伴提供帮助,大家觉得好用的同时可以点击start支持下 13 | 14 | ### 2. 项目功能定制 15 | - **服务内容:** 对项目进行功能定制或扩展 16 | - **价格:** 按功能大小,紧急程度,耗时,定制内容是否允许开源,等情况收费 17 | 18 | ## 收费模式 19 | - **按小时计费:** 根据实际工作时间计算费用,目前个人定制功能2小时起,每小时200~300 20 | - **阶段性付费:** 确认开发前,预付1/3,开发完成付1/3,交付上线1/3 21 | 22 | ## 付款方式 23 | - 微信支付 24 | - 赞赏码支付 25 | - 支付宝支付 26 | 27 | ## 交付时间 28 | 根据项目的复杂性和工作量,具体的交付时间将在项目定制阶段与客户协商确定,一般会在预估的时间内,如有延期会提前告知。 29 | 30 | ## 其他备注 31 | 此收费标准仅限开发功能,以及交付上线收取的费用,不包括后期维护以及后续功能迭代。 -------------------------------------------------------------------------------- /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | "README.md" 4 | ], 5 | "imageSize": 100, 6 | "commit": false, 7 | "contributors": [ 8 | { 9 | "login": "iamtsm", 10 | "name": "iamtsm", 11 | "avatar_url": "https://user-images.githubusercontent.com/44826979/154636603-14635c19-0b28-433f-a420-3bcc0ab084ba.png", 12 | "profile": "https://blog.iamtsm.cn", 13 | "contributions": [ 14 | "code", 15 | "ideas", 16 | "bug" 17 | ] 18 | } 19 | ], 20 | "contributorsPerLine": 7, 21 | "projectName": "tl-rtc-file", 22 | "projectOwner": "iamtsm", 23 | "repoType": "github", 24 | "repoHost": "https://github.com", 25 | "skipCi": true 26 | } 27 | -------------------------------------------------------------------------------- /svr/src/socket/rtcAnswer/answer.js: -------------------------------------------------------------------------------- 1 | const rtcConstant = require("../rtcConstant"); 2 | const rtcClientEvent = rtcConstant.rtcClientEvent 3 | 4 | /** 5 | * webrtc answer 6 | * 转发answer消息至room其他客户端 [from,to,room,sdp] 7 | * @param {*} io socketio对象 8 | * @param {*} socket 单个socket连接 9 | * @param {*} tables 数据表对象 10 | * @param {*} dbClient sequelize-orm对象 11 | * @param {*} data event参数 12 | * @returns 13 | */ 14 | async function answer(io, socket, tables, dbClient, data){ 15 | let otherClient = io.sockets.connected[data.to]; 16 | if (!otherClient) { 17 | return; 18 | } 19 | otherClient.emit(rtcClientEvent.answer, data); 20 | } 21 | 22 | module.exports = { 23 | answer 24 | } -------------------------------------------------------------------------------- /svr/src/socket/rtcHeartbeat/heartbeat.js: -------------------------------------------------------------------------------- 1 | const rtcConstant = require("../rtcConstant"); 2 | const rtcClientEvent = rtcConstant.rtcClientEvent 3 | const utils = require("../../../src/utils/utils"); 4 | 5 | /** 6 | * 心跳 7 | * @param {*} io socketio对象 8 | * @param {*} socket 单个socket连接 9 | * @param {*} tables 数据表对象 10 | * @param {*} dbClient sequelize-orm对象 11 | * @param {*} data event参数 12 | * @returns 13 | */ 14 | async function heartbeat(io, socket, tables, dbClient, data){ 15 | try{ 16 | socket.emit(rtcClientEvent.heartbeat, { 17 | status : "ok" 18 | }) 19 | }catch(e){ 20 | utils.tlConsole(e) 21 | } 22 | } 23 | 24 | module.exports = { 25 | heartbeat 26 | } -------------------------------------------------------------------------------- /doc/gitbook/install/INSTALL.md: -------------------------------------------------------------------------------- 1 | # 选择你的私有部署形式 2 | 3 | 无论是哪种模式部署,都需要先修改好 `tlrtcfile.env`, 具体配置说明在配置部分进行查看, 再进行安装,启动。 4 | 5 | 目前支持一下几种自行部署方式,可根据你的需要选择一种,进行私有化部署,具体安装细节流程参考具体模式下的文档 6 | 7 | - `命令部署` : 支持windows, linux, macos各种系统。 8 | 9 | - `服务器一键脚本部署` : 支持ubuntu16/18/20系统一键脚本, windows一键脚本 10 | 11 | - `官方docker镜像一键脚本` : 支持arm64, amd64/v8两种架构的系统 12 | 13 | - `官方docker-compose挂载配置部署` : 支持arm64, amd64/v8两种架构的系统 14 | 15 | - `官方docker-compose内置配置部署` : 支持arm64, amd64/v8两种架构的系统 16 | 17 | - `自行打包镜像-docker-compose部署` : 支持arm64, amd64/v8两种架构的系统,以及其他架构的系统(取决于你的打包机器所属的架构) 18 | 19 | - `托管平台-zeabur部署` : 目前支持基础的服务(api, socket)的一键部署 20 | 21 | 22 | 在选择模式部署之前,可以先查看阅读一下配置相关文档 23 | 24 | [项目配置统计](../CONF_ALL.md) 25 | 26 | [配置简要说明](../ENV_SETTING.md) -------------------------------------------------------------------------------- /docker/coturn/turnserver-with-secret-user.conf: -------------------------------------------------------------------------------- 1 | #------------TURN BASE CONFIG 基础配置------------# 2 | #监听网卡 3 | # listening-device=eth0 4 | #监听ip(内网ip) 5 | # listening-ip= 6 | #监听端口(端口) 7 | listening-port=3478 8 | #公网ip 9 | # external-ip= 10 | #端口最小值 11 | min-port=49152 12 | #端口最大值 13 | min-port=55000 14 | #cli密码 15 | cli-password=qwerty 16 | #后台运行-默认关闭 docker运行时需关闭, 自行部署时,可以开启 17 | # daemon 18 | #会话指纹 19 | fingerprint 20 | #中等详细日志 21 | verbose 22 | #长期凭证 23 | lt-cred-mech 24 | #关闭tls 25 | no-tls 26 | #关闭dtls 27 | no-dtls 28 | 29 | #------------TURN REST API USER CONFIG 有效时间帐号密码模式 (优先推荐使用这种方式)------------# 30 | #开启rest api 31 | use-auth-secret 32 | #rest api账号密码 33 | static-auth-secret=tlrtcfile 34 | #来源(域名或ip:port) 35 | realm=tlrtcfile.com -------------------------------------------------------------------------------- /client/packages/rtc-web/src/assets/svg-icon/up.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /svr/src/socket/rtcCandidate/candidate.js: -------------------------------------------------------------------------------- 1 | const rtcConstant = require("../rtcConstant"); 2 | const rtcClientEvent = rtcConstant.rtcClientEvent 3 | 4 | /** 5 | * webrtc candidate 6 | * 转发candidate消息至room其他客户端 [from,to,room,candidate[sdpMid,sdpMLineIndex,sdp]] 7 | * @param {*} io socketio对象 8 | * @param {*} socket 单个socket连接 9 | * @param {*} tables 数据表对象 10 | * @param {*} dbClient sequelize-orm对象 11 | * @param {*} data event参数 12 | * @returns 13 | */ 14 | async function candidate(io, socket, tables, dbClient, data){ 15 | let otherClient = io.sockets.connected[data.to]; 16 | if (!otherClient){ 17 | return; 18 | } 19 | otherClient.emit(rtcClientEvent.candidate, data); 20 | } 21 | 22 | module.exports = { 23 | candidate 24 | } -------------------------------------------------------------------------------- /svr/wxapp-res/pages/succ/succ.wxss: -------------------------------------------------------------------------------- 1 | /* pages/result/result.wxss */ 2 | page{ 3 | text-align: center; 4 | background-color: #ffffff; 5 | } 6 | .scc{ 7 | margin-top: 100rpx; 8 | width: 160rpx; 9 | height: 160rpx; 10 | } 11 | .resuil{ 12 | margin-top: 30rpx; 13 | font-weight: 600; 14 | letter-spacing: 3rpx; 15 | font-size: 36rpx; 16 | } 17 | .btn{ 18 | width: 280rpx; 19 | letter-spacing: 3rpx; 20 | height: 70rpx; 21 | font-weight: 500; 22 | line-height: 70rpx; 23 | border: 1px solid #3bb270; 24 | color: #3bb270; 25 | border-radius: 10rpx; 26 | position: absolute; 27 | bottom: 200rpx; 28 | left: 50%; 29 | margin-left: -130rpx; 30 | } 31 | 32 | .btn :hover, .btn:active{ 33 | color: #189b53; 34 | } -------------------------------------------------------------------------------- /svr/src/socket/rtcCount/count.js: -------------------------------------------------------------------------------- 1 | const rtcConstant = require("../rtcConstant"); 2 | const rtcClientEvent = rtcConstant.rtcClientEvent 3 | const utils = require("../../../src/utils/utils"); 4 | 5 | /** 6 | * 在线人数统计广播 7 | * @param {*} io socketio对象 8 | * @param {*} socket 单个socket连接 9 | * @param {*} tables 数据表对象 10 | * @param {*} dbClient sequelize-orm对象 11 | * @param {*} data event参数 12 | * @returns 13 | */ 14 | async function count(io, socket, tables, dbClient, data){ 15 | try{ 16 | let allManCount = Object.keys(io.sockets.connected).length || 0; 17 | io.sockets.emit(rtcClientEvent.count, { 18 | mc : allManCount 19 | }) 20 | }catch(e){ 21 | utils.tlConsole(e) 22 | } 23 | } 24 | 25 | module.exports = { 26 | count 27 | } -------------------------------------------------------------------------------- /client/packages/rtc-web/src/components/error-boundary/index.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 32 | -------------------------------------------------------------------------------- /client/packages/rtc-web/src/components/chat-room/chat-content.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 32 | -------------------------------------------------------------------------------- /svr/src/controller/router.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | const {inject_env_config} = require("../../conf/env_config"); 4 | const cfg = inject_env_config(require("../../conf/cfg.json")); 5 | const utils = require("../../src/utils/utils"); 6 | 7 | module.exports = () => { 8 | let routers = {}; 9 | let dirs = fs.readdirSync(__dirname); 10 | for (let file of dirs) { 11 | //过滤文件夹和文件 12 | if (cfg.api.router.filter.whiteDir.includes(file) 13 | || cfg.api.router.filter.whiteFile.includes(file)) continue; 14 | try { 15 | //添加router 16 | let stat = fs.statSync(path.join(__dirname, file, 'index.js')); 17 | if (stat && stat.isFile()) { 18 | routers["/api/" + file] = require("./" + file + '/index')(); 19 | } 20 | } catch (e) { 21 | utils.tlConsole(e); 22 | } 23 | } 24 | return routers; 25 | } -------------------------------------------------------------------------------- /svr/wxapp-res/pages/succ/succ.js: -------------------------------------------------------------------------------- 1 | // pages/index/index.js 2 | Page({ 3 | 4 | /** 5 | * 页面的初始数据 6 | */ 7 | data: { 8 | 9 | }, 10 | 11 | /** 12 | * 生命周期函数--监听页面加载 13 | */ 14 | onLoad(options) { 15 | 16 | }, 17 | 18 | /** 19 | * 生命周期函数--监听页面初次渲染完成 20 | */ 21 | onReady() { 22 | 23 | }, 24 | 25 | /** 26 | * 生命周期函数--监听页面隐藏 27 | */ 28 | onHide() { 29 | 30 | }, 31 | 32 | /** 33 | * 生命周期函数--监听页面卸载 34 | */ 35 | onUnload() { 36 | 37 | }, 38 | 39 | /** 40 | * 页面相关事件处理函数--监听用户下拉动作 41 | */ 42 | onPullDownRefresh() { 43 | 44 | }, 45 | 46 | /** 47 | * 页面上拉触底事件的处理函数 48 | */ 49 | onReachBottom() { 50 | 51 | }, 52 | 53 | /** 54 | * 用户点击右上角分享 55 | */ 56 | onShareAppMessage() { 57 | 58 | }, 59 | onShow: function(){ 60 | wx.hideHomeButton() 61 | } 62 | }) -------------------------------------------------------------------------------- /svr/test/test.js: -------------------------------------------------------------------------------- 1 | const io = require('socket.io-client'); 2 | 3 | describe('测试', function () { 4 | 5 | 6 | function getSocket(){ 7 | return io("ws://localhost:8444"); 8 | } 9 | 10 | it('socket test', function () { 11 | 12 | const socket1 = getSocket(); 13 | socket1.on("count",function(res){ 14 | console.log(res) 15 | }) 16 | socket1.emit("count"); 17 | socket1.emit("createAndJoin",{ 18 | room : 1111 19 | }) 20 | 21 | const socket2 = getSocket(); 22 | socket2.emit("createAndJoin",{ 23 | room : 1111 24 | }) 25 | 26 | 27 | // const socket3 = io("ws://localhost:8444"); 28 | // socket1.on("count",function(res){ 29 | // console.log(res) 30 | // }) 31 | 32 | 33 | }); 34 | 35 | }); -------------------------------------------------------------------------------- /svr/src/tables/relation.js: -------------------------------------------------------------------------------- 1 | // relation 2 | module.exports = (sequelize, DataTypes) => { 3 | return { 4 | Relation: sequelize.define('relation', { 5 | id: { 6 | type: DataTypes.INTEGER, 7 | comment: '关联id', 8 | primaryKey: true, 9 | autoIncrement: true 10 | }, 11 | type: { 12 | type: DataTypes.STRING(20), 13 | comment: '关联类型' 14 | }, 15 | source_id: { 16 | type: DataTypes.STRING(25), 17 | comment: '源对象id' 18 | }, 19 | target_id: { 20 | type: DataTypes.STRING(25), 21 | comment: '关联对象id' 22 | }, 23 | flag: { 24 | type: DataTypes.INTEGER, 25 | comment: '标志位', 26 | defaultValue: 0, 27 | } 28 | }, { 29 | timestamps: true, 30 | comment: '关联记录表', 31 | indexes: [{ 32 | name: 'created_at_index', 33 | method: 'BTREE', 34 | fields: ['created_at'] 35 | }] 36 | }) 37 | }; 38 | } 39 | -------------------------------------------------------------------------------- /client/packages/rtc-web/src/components/base/dropdown.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 35 | -------------------------------------------------------------------------------- /client/packages/rtc-web/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | es2021: true, 5 | }, 6 | extends: [ 7 | 'eslint:recommended', 8 | 'plugin:vue/vue3-recommended', 9 | 'plugin:@typescript-eslint/recommended', 10 | 'plugin:prettier/recommended', 11 | 'eslint-config-prettier', 12 | ], 13 | parser: 'vue-eslint-parser', 14 | parserOptions: { 15 | ecmaVersion: 'latest', 16 | parser: '@typescript-eslint/parser', 17 | sourceType: 'module', 18 | }, 19 | plugins: ['vue', '@typescript-eslint', 'prettier'], 20 | rules: { 21 | indent: ['error', 2, { offsetTernaryExpressions: true }], 22 | semi: ['error', 'always'], 23 | 'vue/attribute-hyphenation': 'off', 24 | '@typescript-eslint/no-explicit-any': 'off', 25 | 'no-debugger': 'off', 26 | '@typescript-eslint/no-non-null-assertion': 'off', 27 | }, 28 | }; 29 | -------------------------------------------------------------------------------- /svr/src/socket/rtcDisConnect/disconnect.js: -------------------------------------------------------------------------------- 1 | const daoRoom = require("./../../dao/room/room") 2 | const rtcCount = require("./../rtcCount/count"); 3 | const rtcConstant = require("../rtcConstant"); 4 | const rtcClientEvent = rtcConstant.rtcClientEvent; 5 | const rtcLocalNetRoom = require("../rtcLocalNetRoom/localNetRoom"); 6 | 7 | /** 8 | * 断开连接的操作 9 | * @param {*} io socketio对象 10 | * @param {*} socket 单个socket连接 11 | * @param {*} tables 数据表对象 12 | * @param {*} dbClient sequelize-orm对象 13 | * @param {*} data event参数 14 | */ 15 | async function disconnect(io, socket, tables, dbClient, data){ 16 | socket.broadcast.emit(rtcClientEvent.exit, { 17 | from : socket.id, 18 | }); 19 | 20 | await daoRoom.exitRoomBySid({ socket_id: socket.id }, tables, dbClient); 21 | 22 | rtcCount.count(io, socket, tables, dbClient, data); 23 | } 24 | 25 | module.exports = { 26 | disconnect 27 | } -------------------------------------------------------------------------------- /svr/src/socket/index.js: -------------------------------------------------------------------------------- 1 | const connect = require("./connect"); 2 | const rtcConstant = require("./rtcConstant"); 3 | const rtcServerEvent = rtcConstant.rtcServerEvent 4 | 5 | /** 6 | * socket事件初始化入口 7 | * @param {*} tables 8 | * @param {*} dbClient 9 | * @param {*} io 10 | * @returns 11 | */ 12 | async function excute(tables, dbClient, io) { 13 | if (io === undefined || io === null) { 14 | console.error("init socket error "); 15 | return; 16 | } 17 | 18 | io.sockets.on(rtcServerEvent.connection, function (socket) { 19 | if(socket.id === undefined || socket.id === null || socket.id === 0 || socket.id === "0"){ 20 | socket.emit("tips", { 21 | to: socket.id, 22 | msg: "非法连接" 23 | }); 24 | return 25 | } 26 | connect(io, socket, tables, dbClient) 27 | }); 28 | } 29 | 30 | module.exports = { 31 | excute 32 | } -------------------------------------------------------------------------------- /svr/conf/keys/server.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICVTCCAb4CCQCCDZ6FebPIqjANBgkqhkiG9w0BAQsFADBvMQswCQYDVQQGEwJV 3 | UzENMAsGA1UECAwETWFyczETMBEGA1UEBwwKaVRyYW5zd2FycDETMBEGA1UECgwK 4 | aVRyYW5zd2FycDETMBEGA1UECwwKaVRyYW5zd2FycDESMBAGA1UEAwwJMTI3LjAu 5 | MC4xMB4XDTE4MDcxMDAzMTYzN1oXDTI4MDcwNzAzMTYzN1owbzELMAkGA1UEBhMC 6 | VVMxDTALBgNVBAgMBE1hcnMxEzARBgNVBAcMCmlUcmFuc3dhcnAxEzARBgNVBAoM 7 | CmlUcmFuc3dhcnAxEzARBgNVBAsMCmlUcmFuc3dhcnAxEjAQBgNVBAMMCTEyNy4w 8 | LjAuMTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1I6AQ6eNez85kcKjwy3g 9 | /vcnXtw+EbP4Ab37fLhIIWG+XzmEBAqnCYjM3nmlDIGEfNylGReo9mD2OHg46a1D 10 | wjd3pxTMit41pCTCiu8S9A2UJfbhSzQrfs+IZcNye4KR9/FzNEW6KoKQ0uc6X33E 11 | 0xe41hbRMQoKB3WmxvyN8PcCAwEAATANBgkqhkiG9w0BAQsFAAOBgQAd7GNDtKWA 12 | 0OpSCzMu0pbmss9Erh4/RC8D+wK4+TXgPDKyZ6hX4FYPvk+ryMvwxJf0o4jjx5cx 13 | Yew7UjaKHlGXq+CNVRFYlltsbvO3oQTNkajuyYGWzMSuNxNsT3apOxH7SIu3qao8 14 | COSwj5FxZ2JU7O+SBVFZoJrFXEa+KJMQzQ== 15 | -----END CERTIFICATE----- 16 | -------------------------------------------------------------------------------- /svr/conf/keys/server.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIICXAIBAAKBgQDUjoBDp417PzmRwqPDLeD+9yde3D4Rs/gBvft8uEghYb5fOYQE 3 | CqcJiMzeeaUMgYR83KUZF6j2YPY4eDjprUPCN3enFMyK3jWkJMKK7xL0DZQl9uFL 4 | NCt+z4hlw3J7gpH38XM0RboqgpDS5zpffcTTF7jWFtExCgoHdabG/I3w9wIDAQAB 5 | AoGAWsy1BjGhQrDzisy24D3NC53Q97jl2vIiU7wwnkqqpXf3tv3+4ysZx/zkZ3VX 6 | iEwbqKso69srlnQ9OkpBJbGaa6lZe+z7BGzv2eJr+hKjjVjR122eDjAtXw+Tmt6c 7 | iBUG9+ITC1GdhXLEgTXtYuPq8hbDhoAVI007E+5JuQoO8kECQQD3aR6zGfR7EOmn 8 | byGkNMMPY/FoH894BpB4l7gIlNXH3pxBqukrEwnmVXSfak2PAkeLFO+bgCc6baIZ 9 | +R4iOLn/AkEA2++dIndbY7nmGs7Q//sM7MkFMiFQ4h1nN38V9AEHaUonxlwo+Nks 10 | PTAaVd78YIh6yBlfexm0Fxi1mEVQApqZCQJAFUPqyJglhGJqwuJxcMy8K1l6yWla 11 | isV9q2/W+J3aViiTI63OBs7HHg4gTQd1DSK0BYdSJPp55LLBqRvZdDWN/wJBAMQa 12 | M46exAL4p55xl9MWwyCB4LshD6B9vSGzlBx7qmMMNsjcNcAkzBhGwsScTYW5S1kN 13 | nqABfB037/s0mjGoLRkCQF18j4MovyFaj8VAqY8YUmf86Ez9JmL9kHNA8yEoSjuI 14 | xsRa6y5Nza5y83Mojt4W+PfS386riJ7txqrPdezyag4= 15 | -----END RSA PRIVATE KEY----- 16 | -------------------------------------------------------------------------------- /client/packages/rtc-web/src/keys/server.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICVTCCAb4CCQCCDZ6FebPIqjANBgkqhkiG9w0BAQsFADBvMQswCQYDVQQGEwJV 3 | UzENMAsGA1UECAwETWFyczETMBEGA1UEBwwKaVRyYW5zd2FycDETMBEGA1UECgwK 4 | aVRyYW5zd2FycDETMBEGA1UECwwKaVRyYW5zd2FycDESMBAGA1UEAwwJMTI3LjAu 5 | MC4xMB4XDTE4MDcxMDAzMTYzN1oXDTI4MDcwNzAzMTYzN1owbzELMAkGA1UEBhMC 6 | VVMxDTALBgNVBAgMBE1hcnMxEzARBgNVBAcMCmlUcmFuc3dhcnAxEzARBgNVBAoM 7 | CmlUcmFuc3dhcnAxEzARBgNVBAsMCmlUcmFuc3dhcnAxEjAQBgNVBAMMCTEyNy4w 8 | LjAuMTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1I6AQ6eNez85kcKjwy3g 9 | /vcnXtw+EbP4Ab37fLhIIWG+XzmEBAqnCYjM3nmlDIGEfNylGReo9mD2OHg46a1D 10 | wjd3pxTMit41pCTCiu8S9A2UJfbhSzQrfs+IZcNye4KR9/FzNEW6KoKQ0uc6X33E 11 | 0xe41hbRMQoKB3WmxvyN8PcCAwEAATANBgkqhkiG9w0BAQsFAAOBgQAd7GNDtKWA 12 | 0OpSCzMu0pbmss9Erh4/RC8D+wK4+TXgPDKyZ6hX4FYPvk+ryMvwxJf0o4jjx5cx 13 | Yew7UjaKHlGXq+CNVRFYlltsbvO3oQTNkajuyYGWzMSuNxNsT3apOxH7SIu3qao8 14 | COSwj5FxZ2JU7O+SBVFZoJrFXEa+KJMQzQ== 15 | -----END CERTIFICATE----- 16 | -------------------------------------------------------------------------------- /client/packages/rtc-web/src/keys/server.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIICXAIBAAKBgQDUjoBDp417PzmRwqPDLeD+9yde3D4Rs/gBvft8uEghYb5fOYQE 3 | CqcJiMzeeaUMgYR83KUZF6j2YPY4eDjprUPCN3enFMyK3jWkJMKK7xL0DZQl9uFL 4 | NCt+z4hlw3J7gpH38XM0RboqgpDS5zpffcTTF7jWFtExCgoHdabG/I3w9wIDAQAB 5 | AoGAWsy1BjGhQrDzisy24D3NC53Q97jl2vIiU7wwnkqqpXf3tv3+4ysZx/zkZ3VX 6 | iEwbqKso69srlnQ9OkpBJbGaa6lZe+z7BGzv2eJr+hKjjVjR122eDjAtXw+Tmt6c 7 | iBUG9+ITC1GdhXLEgTXtYuPq8hbDhoAVI007E+5JuQoO8kECQQD3aR6zGfR7EOmn 8 | byGkNMMPY/FoH894BpB4l7gIlNXH3pxBqukrEwnmVXSfak2PAkeLFO+bgCc6baIZ 9 | +R4iOLn/AkEA2++dIndbY7nmGs7Q//sM7MkFMiFQ4h1nN38V9AEHaUonxlwo+Nks 10 | PTAaVd78YIh6yBlfexm0Fxi1mEVQApqZCQJAFUPqyJglhGJqwuJxcMy8K1l6yWla 11 | isV9q2/W+J3aViiTI63OBs7HHg4gTQd1DSK0BYdSJPp55LLBqRvZdDWN/wJBAMQa 12 | M46exAL4p55xl9MWwyCB4LshD6B9vSGzlBx7qmMMNsjcNcAkzBhGwsScTYW5S1kN 13 | nqABfB037/s0mjGoLRkCQF18j4MovyFaj8VAqY8YUmf86Ez9JmL9kHNA8yEoSjuI 14 | xsRa6y5Nza5y83Mojt4W+PfS386riJ7txqrPdezyag4= 15 | -----END RSA PRIVATE KEY----- 16 | -------------------------------------------------------------------------------- /client/packages/rtc-web/src/components/emoji/index.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 40 | -------------------------------------------------------------------------------- /svr/wxapp-res/pages/login/login.wxml: -------------------------------------------------------------------------------- 1 | 2 | tl-rtc-file 授权登录 3 | 4 | 5 | 演示体验地址 6 | | 7 | 复制 8 | https://im.iamtsm.cn 9 | 10 | 11 | github开源地址 12 | | 13 | 复制 14 | https://github.com/tl-open-source/tl-rtc-file 15 | 16 | 17 | gitee开源地址 18 | | 19 | 复制 20 | https://gitee.com/iamtsm/tl-rtc-file 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /svr/src/tables/file.js: -------------------------------------------------------------------------------- 1 | // file 2 | module.exports = (sequelize, DataTypes) => { 3 | return { 4 | File: sequelize.define('file', { 5 | id: { 6 | type: DataTypes.INTEGER, 7 | primaryKey: true, 8 | autoIncrement: true, 9 | unique: true, 10 | comment: '数据id', 11 | }, 12 | room_id: { 13 | type: DataTypes.STRING(16), 14 | comment: '房间号' 15 | }, 16 | code: { 17 | type: DataTypes.STRING(128), 18 | comment: '取件码' 19 | }, 20 | name: { 21 | type: DataTypes.STRING(256), 22 | comment: '文件名称' 23 | }, 24 | oss_name: { 25 | type: DataTypes.STRING(256), 26 | comment: 'oss中的文件名称' 27 | }, 28 | download: { 29 | type: DataTypes.STRING(256), 30 | comment: '下载链接' 31 | }, 32 | content: { 33 | type: DataTypes.TEXT, 34 | comment: '详细信息' 35 | }, 36 | }, { 37 | timestamps: true, 38 | comment: '房间表', 39 | indexes: [{ 40 | name: 'code_index', 41 | method: 'BTREE', 42 | fields: ['code'] 43 | }] 44 | }) 45 | }; 46 | } 47 | -------------------------------------------------------------------------------- /client/packages/rtc-web/src/layout/index.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 34 | -------------------------------------------------------------------------------- /client/packages/rtc-web/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "target": "ES2020", 5 | "useDefineForClassFields": true, 6 | "module": "ESNext", 7 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 8 | "skipLibCheck": true, 9 | 10 | /* Bundler mode */ 11 | "moduleResolution": "bundler", 12 | "allowImportingTsExtensions": true, 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "noEmit": true, 16 | "jsx": "preserve", 17 | "jsxImportSource": "vue", 18 | 19 | /* Linting */ 20 | "strict": true, 21 | "noUnusedLocals": true, 22 | "noUnusedParameters": true, 23 | "noFallthroughCasesInSwitch": true, 24 | 25 | /* types */ 26 | "types": ["vite/client"], 27 | 28 | /* paths */ 29 | "paths": { 30 | "@/*": ["src/*"] 31 | } 32 | }, 33 | "include": [ 34 | "src/**/*.ts", 35 | "src/**/*.d.ts", 36 | "src/**/*.tsx", 37 | "src/**/*.vue", 38 | "components.d.ts" 39 | ], 40 | "references": [{ "path": "./tsconfig.node.json" }] 41 | } 42 | -------------------------------------------------------------------------------- /client/packages/rtc-web/src/components/list/select-list.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 41 | -------------------------------------------------------------------------------- /client/packages/rtc-web/src/components/chat-room/chat-message.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 40 | -------------------------------------------------------------------------------- /svr/wxapp-res/pages/login/login.wxss: -------------------------------------------------------------------------------- 1 | .tlrtcfilePanel { 2 | border: 1px white solid; 3 | height: 60vh; 4 | margin-top: 5vh; 5 | } 6 | 7 | .tlrtcfileTitle { 8 | font-size: 23px; 9 | font-weight: bold; 10 | color: #383C40; 11 | margin-top: 1vh; 12 | } 13 | 14 | .tlrtcfileIntro { 15 | color: #686767; 16 | font-size: 12px; 17 | position: relative; 18 | line-height: 25px; 19 | padding: 8vh; 20 | text-align: left; 21 | } 22 | 23 | .tlrtcfileIntroItem{ 24 | margin-top: 3vh; 25 | } 26 | 27 | .tlrtcfileIntroItem view{ 28 | margin-top: 1vh; 29 | } 30 | 31 | .tlrtcfileIntro image{ 32 | width: 4vh; 33 | height: 4vh; 34 | top: 7px; 35 | position: relative; 36 | } 37 | 38 | .tlrtcfileIntro a { 39 | margin-left: 1vh; 40 | } 41 | 42 | .tlrtcfileIntro a:hover { 43 | color: rgb(114, 114, 202); 44 | } 45 | 46 | .loginBtn { 47 | width: 70% !important; 48 | height: 45px; 49 | border-radius: 5px; 50 | background-color: #0E87EB; 51 | color: #fff; 52 | bottom: 13vh; 53 | position: absolute; 54 | left: 15%; 55 | } -------------------------------------------------------------------------------- /client/packages/rtc-web/src/assets/svg-icon/chat.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/packages/rtc-web/src/assets/svg-icon/mirror-image.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/packages/rtc-web/src/components/chat-room/user-card.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # ---> Node 2 | # Logs 3 | logs 4 | *.log 5 | npm-debug.log* 6 | 7 | # Runtime data 8 | pids 9 | *.pid 10 | *.seed 11 | 12 | # Directory for instrumented libs generated by jscoverage/JSCover 13 | lib-cov 14 | 15 | # Coverage directory used by tools like istanbul 16 | coverage 17 | 18 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 19 | .grunt 20 | 21 | # node-waf configuration 22 | .lock-wscript 23 | 24 | # Compiled binary addons (http://nodejs.org/api/addons.html) 25 | build/Release 26 | 27 | # Dependency directory 28 | # https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git 29 | node_modules 30 | 31 | # ---> Java 32 | *.class 33 | 34 | # Mobile Tools for Java (J2ME) 35 | .mtj.tmp/ 36 | 37 | # Package Files # 38 | *.jar 39 | *.war 40 | *.ear 41 | 42 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 43 | hs_err_pid* 44 | 45 | svr/web-res/js/*.min.* 46 | svr/web-res/css/*.min.* 47 | 48 | dist/ 49 | 50 | client/dist 51 | 52 | package-lock.json 53 | 54 | 55 | docker/mysql/data/* 56 | 57 | client_dist -------------------------------------------------------------------------------- /DISCLAIMER.md: -------------------------------------------------------------------------------- 1 | 2 | # 免责声明 3 | 1.1 如使用本演示网站/开源项目,即表示您已同意本条款。我们 (开源项目维护团队) 始终在不断更改和改进我们的服务。我们可能会增加或删除功能,也可能暂停或彻底停止某项服务。您可以随时停止使用我们的服务,我们也可以随时对我们的服务增加新的限制。 4 | 5 | 1.2 该网站/开源项目的使用者在使用本演示网站/开源项目前,应仔细阅读以下条款并同意遵守。使用本演示网站/开源项目将被视为对本免责协议的接受和遵守。 6 | 7 | ## 演示网站/开源项目的性质 8 | 2.1 本演示网站/开源项目旨在帮助大家学习webrtc,提供一个学习/演示demo环境,和基础功能代码入门教学 9 | 10 | 2.2 本演示网站/开源项目提供的服务旨在为用户提供数据传输功能演示,包括但不限于文件传输、直播、音视频通话、远程屏幕共享、聊天等功能的演示。 11 | 12 | 2.3 使用者应理解,本演示网站/开源项目仅提供技术支持演示,不对数据传输的准确性、及时性、完整性、安全性或合法性负责。使用者应自行承担使用本演示网站/开源项目的风险。 13 | 14 | ## 使用者责任 15 | 3.1 使用者应承担使用本演示网站/开源项目的全部责任和风险。对于使用者通过本演示网站/开源项目进行的任何操作、行为或后果,本演示网站/开源项目不承担任何责任。 16 | 17 | 3.2 使用者不得利用本演示网站/开源项目进行任何违法、侵权、威胁、诽谤、骚扰、破坏性或其他不当行为。若使用者违反上述规定,本演示网站/开源项目有权立即终止或限制使用者的访问权限。 18 | 19 | 3.3 本演示网站使用者严禁上传违法违规内容,包括但不限于:色情低俗、政治敏感、、暴力、恶意软件、诈骗、贩卖公民隐私,上传上述内容将永久封禁账号或 IP 地址,并可能承担承担刑事责任 20 | 21 | ## 免责条款 22 | 4.1 本演示网站/开源项目不对任何由于使用本演示网站/开源项目而引起的任何直接、间接、附带、特殊、惩罚性或后续的损害承担责任,包括但不限于利润损失、商业中断、计算机系统故障、数据丢失或其他经济损失。 23 | 24 | 4.2 本演示网站/开源项目不对任何第三方通过本演示网站/开源项目发布、提供或传输的任何内容的准确性、合法性、安全性或可靠性负责。 25 | 26 | ## 免责声明的范围 27 | 本免责协议适用于使用本演示网站/开源项目的所有用户,无论其身份或目的。该免责协议的内容适用于因使用本演示网站/开源项目而产生的一切事项,包括但不限于使用本演示网站/开源项目的内容、服务或信息所引起的任何纠纷、损失或损害。 28 | -------------------------------------------------------------------------------- /client/packages/rtc-web/src/hooks/useTheme.ts: -------------------------------------------------------------------------------- 1 | import { useLocalStorage, usePreferredDark } from '@vueuse/core'; 2 | import { watch, computed } from 'vue'; 3 | 4 | export enum ThemeEnum { 5 | DARK = 'dark', 6 | LIGHT = 'light', 7 | } 8 | 9 | export const useTheme = () => { 10 | const isPreferredDark = usePreferredDark(); 11 | 12 | const themeInfo = useLocalStorage( 13 | 'rtc-theme', 14 | isPreferredDark ? ThemeEnum.DARK : ThemeEnum.LIGHT 15 | ); 16 | 17 | const setTheme = (theme: ThemeEnum) => { 18 | themeInfo.value = theme; 19 | }; 20 | 21 | const isDark = computed(() => themeInfo.value === ThemeEnum.DARK); 22 | 23 | return { 24 | themeInfo, 25 | setTheme, 26 | isDark, 27 | }; 28 | }; 29 | 30 | export const useSwitchTheme = () => { 31 | const themeInfomation = useTheme(); 32 | const { themeInfo } = themeInfomation; 33 | 34 | watch( 35 | () => themeInfo.value, 36 | (v) => { 37 | document.documentElement.setAttribute('data-theme', v); 38 | }, 39 | { 40 | immediate: true, 41 | } 42 | ); 43 | 44 | return { ...themeInfomation }; 45 | }; 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 iamtsm 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /svr/src/bussiness/cache/cache.js: -------------------------------------------------------------------------------- 1 | const { LRUCache } = require('lru-cache'); 2 | 3 | // 5分钟缓存 4 | const shortTimeCache = new LRUCache({ 5 | max: 500, 6 | maxAge: 1000 * 60 * 5 7 | }); 8 | 9 | // 1小时缓存 10 | const longTimeCache = new LRUCache({ 11 | max: 500, 12 | maxAge: 1000 * 60 * 60 13 | }); 14 | 15 | // 1天缓存 16 | const dayTimeCache = new LRUCache({ 17 | max: 500, 18 | maxAge: 1000 * 60 * 60 * 24 19 | }); 20 | 21 | 22 | const setShortTimeCache = (key, value) => { 23 | shortTimeCache.set(key, value); 24 | } 25 | 26 | const getShortTimeCache = (key) => { 27 | return shortTimeCache.get(key); 28 | } 29 | 30 | const setLongTimeCache = (key, value) => { 31 | longTimeCache.set(key, value); 32 | } 33 | 34 | const getLongTimeCache = (key) => { 35 | return longTimeCache.get(key); 36 | } 37 | 38 | const setDayTimeCache = (key, value) => { 39 | dayTimeCache.set(key, value); 40 | } 41 | 42 | const getDayTimeCache = (key) => { 43 | return dayTimeCache.get(key); 44 | } 45 | 46 | module.exports = { 47 | setShortTimeCache, getShortTimeCache, 48 | setLongTimeCache, getLongTimeCache, 49 | setDayTimeCache, getDayTimeCache 50 | } -------------------------------------------------------------------------------- /client/packages/rtc-web/src/components/base/svg-icon.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 39 | 40 | 49 | -------------------------------------------------------------------------------- /svr/static/css/default.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | Theme: Default 3 | Description: Original highlight.js style 4 | Author: (c) Ivan Sagalaev 5 | Maintainer: @highlightjs/core-team 6 | Website: https://highlightjs.org/ 7 | License: see project LICENSE 8 | Touched: 2021 9 | */pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background:#f0f0f0;color:#444}.hljs-comment{color:#888}.hljs-punctuation,.hljs-tag{color:#444a}.hljs-tag .hljs-attr,.hljs-tag .hljs-name{color:#444}.hljs-attribute,.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-name,.hljs-selector-tag{font-weight:700}.hljs-deletion,.hljs-number,.hljs-quote,.hljs-selector-class,.hljs-selector-id,.hljs-string,.hljs-template-tag,.hljs-type{color:#800}.hljs-section,.hljs-title{color:#800;font-weight:700}.hljs-link,.hljs-operator,.hljs-regexp,.hljs-selector-attr,.hljs-selector-pseudo,.hljs-symbol,.hljs-template-variable,.hljs-variable{color:#bc6060}.hljs-literal{color:#78a960}.hljs-addition,.hljs-built_in,.hljs-bullet,.hljs-code{color:#397300}.hljs-meta{color:#1f7199}.hljs-meta .hljs-string{color:#4d99bf}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700} -------------------------------------------------------------------------------- /client/packages/rtc-web/src/hooks/useUseragent.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable indent */ 2 | export const useUserAgent = () => { 3 | const uerAgent = window.navigator.userAgent; 4 | 5 | const isMobile = /Mobi|Android|iPhone/i.test(uerAgent); 6 | 7 | const getNetWorkState = () => { 8 | let networkStr = uerAgent.match(/NetType\/\w+/) 9 | ? uerAgent.match(/NetType\/\w+/)?.[0] 10 | : 'NetType/other'; 11 | networkStr = networkStr?.toLowerCase().replace('nettype/', ''); 12 | if ( 13 | networkStr && 14 | !['wifi', '5g', '3g', '4g', '2g', '3gnet', 'slow-2g'].includes(networkStr) 15 | ) { 16 | if ((navigator as any).connection) { 17 | networkStr = (navigator as any).connection.effectiveType; 18 | } 19 | } 20 | switch (networkStr) { 21 | case 'wifi': 22 | return 'wifi'; 23 | case '5g': 24 | return '5g'; 25 | case '4g': 26 | return '4g'; 27 | case '3g' || '3gnet': 28 | return '3g'; 29 | case '2g' || 'slow-2g': 30 | return '2g'; 31 | default: 32 | return 'unknow'; 33 | } 34 | }; 35 | 36 | return { 37 | isMobile, 38 | getNetWorkState, 39 | }; 40 | }; 41 | -------------------------------------------------------------------------------- /client/packages/rtc-web/src/assets/svg-icon/nav-menu.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/packages/rtc-web/src/config/constant.ts: -------------------------------------------------------------------------------- 1 | export type MenuActionType = { 2 | name: string; 3 | tip: string; 4 | color?: string; 5 | tipDir?: string; 6 | btn?: boolean; 7 | disabled?: boolean; 8 | }; 9 | 10 | export const ChatAction: MenuActionType[] = [ 11 | { name: 'member', tip: '显示成员', color: undefined }, 12 | ]; 13 | 14 | export const ChatInputAction: MenuActionType[] = [ 15 | { name: 'emoji', tip: '表情' }, 16 | ]; 17 | 18 | export const VideoMenuAction: MenuActionType[] = [ 19 | { 20 | name: 'member', 21 | tip: '显示成员', 22 | color: undefined, 23 | tipDir: 'tooltip-top', 24 | btn: true, 25 | }, 26 | ]; 27 | 28 | export const VideoControlMenuAction: MenuActionType[] = [ 29 | { 30 | name: 'camera', 31 | tip: '开启/关闭摄像头', 32 | color: '#707070', 33 | tipDir: 'tooltip-top', 34 | btn: true, 35 | disabled: false, 36 | }, 37 | // { 38 | // name: 'mirror-image', 39 | // tip: '开启镜像', 40 | // color: '#707070', 41 | // tipDir: 'tooltip-top', 42 | // btn: true, 43 | // disabled: false, 44 | // }, 45 | { 46 | name: 'hang-up', 47 | tip: '结束通话', 48 | color: undefined, 49 | tipDir: 'tooltip-top', 50 | btn: true, 51 | disabled: false, 52 | }, 53 | ]; 54 | -------------------------------------------------------------------------------- /client/packages/rtc-web/src/views/video/video-control.vue: -------------------------------------------------------------------------------- 1 | 36 | 37 | 43 | -------------------------------------------------------------------------------- /doc/gitbook/install/INSTALL_BY_DOCKER_COMPOSE.md: -------------------------------------------------------------------------------- 1 | # docker-compose官方镜像部署 2 | 3 | 项目默认提供了已经打包好的镜像放在dockerhub,供大家下载使用。并提供了 `docker-compose.yml` 配置,供大家使用 4 | 5 | ### 前置条件 6 | 7 | - 需要安装好docker-compose,V1或者V2都可以 8 | 9 | - DockerHub需要科学上网 10 | 11 | - 需要下载源码包 12 | 13 | ### 准备启动 14 | 15 | 根据你的 `Docker Compose` 版本在 `主目录` 执行如下对应的命令 16 | 17 | - 对于 `Docker Compose V1` 18 | 19 | ``` 20 | docker-compose --profile=http up -d 21 | ``` 22 | 23 | - 对于 `Docker Compose V2` 24 | 25 | ``` 26 | docker compose --profile=http up -d 27 | ``` 28 | 29 | ### 注意事项 30 | 31 | 由于项目的服务需要依赖一些配置,所以容器方式都挂载了一些配置文件。 32 | 33 | api容器和socket容器 挂载配置如下 34 | 35 | ``` 36 | volumes: 37 | - ./tlrtcfile.env:/tlrtcfile/tlrtcfile.env 38 | ``` 39 | 40 | mysql容器的挂载配置如下,作用是: 将mysql容器内的数据文件挂载出来进行保存 41 | 42 | ``` 43 | volumes: 44 | - ./docker/mysql/data/db:/var/lib/mysql 45 | - ./docker/mysql/data/my.cnf:/etc/mysql/conf.d/my.cnf 46 | - ./docker/mysql/data/log:/var/log/mysql 47 | - ./docker/mysql/data/init.sql:/docker-entrypoint-initdb.d/init.sql 48 | ``` 49 | 50 | coturn的挂载配置,作用是: 将项目默认提供的turn的配置,提供给coturn容器使用 51 | 52 | ``` 53 | volumes: 54 | - ./docker/coturn/turnserver-with-secret-user.conf:/etc/turnserver.conf 55 | ``` 56 | 57 | 如果觉得这种方式麻烦,或者不合适自己的场景,可以选择另外一种 `内置配置的docker-compose` -------------------------------------------------------------------------------- /bin/centeros/auto-update-project.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ######################### 3 | # 提供一键更新项目代码版本的脚本 4 | # @auther: iamtsm 5 | # @version: v1.0.0 6 | ######################### 7 | 8 | # Check if the current directory is a Git repository 9 | if [ -d .git ]; then 10 | # The current directory is a Git repository, so we can pull the latest changes 11 | echo "Current directory is a Git repository. Pulling latest changes..." 12 | git pull 13 | else 14 | # The current directory is not a Git repository 15 | echo "Current directory is not a Git repository." 16 | 17 | # Check if Git is installed 18 | if ! command -v git &> /dev/null; then 19 | # Git is not installed, so let's try to install it 20 | echo "Git is not installed. Installing Git..." 21 | sudo yum install -y git 22 | fi 23 | 24 | # Initialize a new Git repository and set the remote URL 25 | echo "Initializing a new Git repository and setting remote URL..." 26 | git init 27 | git remote add origin https://github.com/tl-open-source/tl-rtc-file.git 28 | 29 | # Pull the latest changes from the remote repository (use 'master' branch) 30 | git pull origin master 31 | 32 | # Optionally, you can set the default branch to 'master' 33 | git symbolic-ref refs/remotes/origin/HEAD refs/remotes/origin/master 34 | fi 35 | 36 | echo "Done." 37 | -------------------------------------------------------------------------------- /svr/src/tables/dog.js: -------------------------------------------------------------------------------- 1 | // dog 2 | module.exports = (sequelize, DataTypes) => { 3 | return { 4 | DogOther : { 5 | Flag : { 6 | IS_DEV_ADMIN : 0x1, //是否是开发者团队的操作记录 7 | IS_SET_TOP : 0x2, //是否设置消息记录类型置顶 8 | } 9 | }, 10 | Dog: sequelize.define('dog', { 11 | id: { 12 | type: DataTypes.INTEGER, 13 | comment: '功能id', 14 | primaryKey: true, 15 | autoIncrement: true 16 | }, 17 | name: { 18 | type: DataTypes.STRING(20), 19 | comment: '操作功能名称' 20 | }, 21 | room_id: { 22 | type: DataTypes.STRING(25), 23 | comment: '房间号' 24 | }, 25 | socket_id: { 26 | type: DataTypes.STRING(25), 27 | comment: '连接id' 28 | }, 29 | device: { 30 | type: DataTypes.STRING(256), 31 | comment: '设备' 32 | }, 33 | flag: { 34 | type: DataTypes.INTEGER, 35 | comment: '标志位', 36 | defaultValue: 0, 37 | }, 38 | content: { 39 | type: DataTypes.TEXT, 40 | comment: '详细信息' 41 | }, 42 | handshake: { 43 | type: DataTypes.TEXT, 44 | comment: '客户端信息' 45 | } 46 | }, { 47 | timestamps: true, 48 | comment: '功能记录表', 49 | indexes: [{ 50 | name: 'created_at_index', 51 | method: 'BTREE', 52 | fields: ['created_at'] 53 | },{ 54 | name: 'room_id_name_index', 55 | method: 'BTREE', 56 | fields: ['room_id','name'] 57 | }] 58 | }) 59 | }; 60 | } 61 | -------------------------------------------------------------------------------- /client/packages/rtc-web/src/components/chat-room/chat-input.vue: -------------------------------------------------------------------------------- 1 | 35 | 36 | 55 | -------------------------------------------------------------------------------- /doc/gitbook/SETTING.md: -------------------------------------------------------------------------------- 1 | # 设置选项说明 2 | 3 | ### webrtc检测 4 | 5 | 简单检测浏览器是否支持webrtc,如果不支持,网页许多功能将不能使用。一般来说,目前市面上大多数浏览器都是支持webrtc的 6 | 7 | ### 日志列表 8 | 9 | 点击打开日志列表,会将系统执行日志进行展示,包括系统日志,用户操作日志 10 | 11 | ### 执行日志 【此开关刷新网页后失效,需重新设置】 12 | 13 | 控制是否开启日志输出。默认是开启的。 14 | 15 | ### 中继设置 【此开关刷新网页后有效,长久保存】 16 | 17 | 用于保障webrtc数据兜底成功传输,默认是不开启的,但是不开启的话,可能在一些网络受限的情况下,可能导致p2p连接失败。 18 | 19 | ### 文件持久化 【此开关刷新网页后有效,长久保存】 20 | 21 | 支持接收的文件存放在浏览器的indexedDb数据库中,也就是长久存放在客户端,刷新网页后会自动加载历史的接收缓存文件记录,支持选择性删除。 22 | 23 | ### socket地址 【此设置刷新网页后有效,长久保存】 24 | 25 | 自定义websocket地址,支持连接指定websocket服务地址。 26 | 27 | ### ai上下文 【此开关刷新网页后失效,需重新设置】 28 | 29 | chatgpt聊天框的对话上下文的简单处理。 30 | 31 | ### 消息红点 【此设置刷新网页后有效,长久保存】 32 | 33 | 主动关闭消息通知的红点,适用于 “强迫症“ 伙伴。 34 | 35 | ### 固定房间号 【此设置刷新网页后有效,长久保存】 36 | 37 | 自定义一个房间号,设置好之后,每次自动进入,无需额外输入。 38 | 39 | ### 局域网房间 (开发中) 40 | 41 | 开启设置后,你创建的房间,同一个局域网用户进入网页后将看到你创建的房间。 42 | 43 | ### 系统通知 (开发中) 【此设置刷新网页后有效,长久保存】 44 | 45 | 开启设置后,当收到消息或者提示时,会使用浏览器自带的桌面消息提示,进行及时的音效,横幅提示 46 | 47 | ### 文件分片传输大小 【此开关刷新网页后失效,需重新设置】 48 | 49 | 由于网站的文件传输是分片传输,但是由于webrtc的数据传输通道有限制,所以项目提供了一个合理范围的可选项,用于自定义控制每次webrtc的数据通道发送数据时的分片大小。默认是 16KB,最大可调整到64KB(不同浏览器实现可能不同,16~64是我认为比较合适的可选范围) 50 | 51 | ### 预览文件大小限制 【此开关刷新网页后失效,需重新设置】 52 | 53 | 在选择完待传输的文件后,网站支持在线本地预览多种格式的文件。但是由于预览文件过大,会导致浏览器过于卡顿,所以提供了一个我认为比较合适的可选范围,默认5M,最大15M 54 | 55 | ### 执行日志输出限制 【此开关刷新网页后失效,需重新设置】 56 | 57 | 由于数据传输和用户操作量日志可能较大,对浏览器会造成一些卡顿,默认提供了一个我认为比较合适的可选范围,默认300条,最大800条 -------------------------------------------------------------------------------- /doc/gitbook/install/INSTALL_BY_DOCKER_COMPOSE_WITH_ENV.md: -------------------------------------------------------------------------------- 1 | # docker-compose官方镜像部署 - 内置配置文件形式 2 | 3 | 项目默认提供了已经打包好的镜像放在dockerhub,供大家下载使用。并提供了 `docker-compose-with-all-env.yml` 配置,供大家使用。 4 | 5 | 这种方式和前一种 `docker-compose.yml` 的唯一区别就是,这个方式内置了大部分配置,无需依赖挂载文件。 6 | 7 | 即使不下载源码,单独下载这个yml文件,也可以部署。 8 | 9 | ### 前置条件 10 | 11 | - 需要安装好docker-compose,V1或者V2都可以 12 | 13 | - DockerHub需要科学上网 14 | 15 | - 需要修改好内置配置的数据,具体可以参考 [配置简要说明](../ENV_SETTING.md) 16 | 17 | - 如果不下载源码包,且你需要配置挂载文件/目录的时候,需要按需下载/设置好挂载依赖的目录/文件,如果下载源码包则可以忽略这一条件 18 | 19 | ### 准备启动 20 | 21 | 根据你的 `Docker Compose` 版本在 `主目录` 执行如下对应的命令 22 | 23 | - 对于 `Docker Compose V1` 24 | 25 | ``` 26 | docker-compose --profile=http up -d 27 | ``` 28 | 29 | - 对于 `Docker Compose V2` 30 | 31 | ``` 32 | docker compose --profile=http up -d 33 | ``` 34 | 35 | ### 注意事项 36 | 37 | 虽然内置了大部分配置,但是还有部分配置依然是需要通过挂载的形式,但挂载的配置不是必需存在,如不需要可以注释挂载代码,目前有两个容器有挂载配置。 38 | 39 | 但是注释挂载设置后,将会带来一些小问题,如果你可以接受的话。 40 | 41 | mysql容器的挂载配置如下,作用是: 将mysql容器内的数据文件挂载出来进行保存 42 | 43 | ``` 44 | volumes: 45 | - ./docker/mysql/data/db:/var/lib/mysql 46 | - ./docker/mysql/data/my.cnf:/etc/mysql/conf.d/my.cnf 47 | - ./docker/mysql/data/log:/var/log/mysql 48 | - ./docker/mysql/data/init.sql:/docker-entrypoint-initdb.d/init.sql 49 | ``` 50 | 51 | coturn的挂载配置,作用是: 将项目默认提供的turn的配置,提供给coturn容器使用 52 | 53 | ``` 54 | volumes: 55 | - ./docker/coturn/turnserver-with-secret-user.conf:/etc/turnserver.conf 56 | ``` -------------------------------------------------------------------------------- /svr/src/tables/user.js: -------------------------------------------------------------------------------- 1 | // user 2 | module.exports = (sequelize, DataTypes) => { 3 | return { 4 | UserOther : { 5 | Flag : { 6 | IS_SUBSCRIBE_WEBSITE_NOTIFY : 0x1, //是否已订阅网站通知 7 | } 8 | }, 9 | User: sequelize.define('user', { 10 | id: { 11 | type: DataTypes.INTEGER, 12 | primaryKey: true, 13 | autoIncrement: true, 14 | unique: true, 15 | comment: '数据id', 16 | }, 17 | type: { 18 | type: DataTypes.STRING(6), 19 | comment: '帐号类型, wx, qq, wb, web, other' 20 | }, 21 | openid: { 22 | type: DataTypes.STRING(64), 23 | comment: '微信openid' 24 | }, 25 | avatar : { 26 | type: DataTypes.STRING(512), 27 | comment: '头像地址' 28 | }, 29 | uname: { 30 | type: DataTypes.STRING(20), 31 | comment: '姓名,昵称' 32 | }, 33 | pwd : { 34 | type: DataTypes.STRING(12), 35 | comment: '用户密码' 36 | }, 37 | solt : { 38 | type: DataTypes.STRING(32), 39 | comment: '加密solt' 40 | }, 41 | role: { 42 | type: DataTypes.STRING(15), 43 | comment: '用户身份' 44 | }, 45 | flag: { 46 | type: DataTypes.INTEGER, 47 | comment: '标志位', 48 | defaultValue: 0, 49 | } 50 | }, { 51 | timestamps: true, 52 | comment: '用户表', 53 | indexes: [{ 54 | name: 'created_at_index', 55 | method: 'BTREE', 56 | fields: ['created_at'] 57 | }] 58 | }) 59 | }; 60 | } -------------------------------------------------------------------------------- /client/packages/rtc-web/src/hooks/useInitData.ts: -------------------------------------------------------------------------------- 1 | import { ConfigEnum } from '@/config'; 2 | import { useFetch, useLocalStorage } from '@vueuse/core'; 3 | import { ref, shallowReactive } from 'vue'; 4 | import io from 'socket.io-client'; 5 | import { InitDataKeyType } from '@/context'; 6 | import { isDev } from '@/utils'; 7 | 8 | export const useFetchData = async () => { 9 | const useTurn = useLocalStorage(ConfigEnum.useRelay, isDev() ? false : true); 10 | 11 | const { data, error } = await useFetch( 12 | () => `/api/comm/initData?turn=${useTurn.value}` 13 | ) 14 | .get() 15 | .json(); 16 | 17 | return { data, error }; 18 | }; 19 | 20 | export const useInitData = async () => { 21 | const { data, error } = await useFetchData(); 22 | const initData = ref({ 23 | langMode: 'zh', // 默认中文 24 | }); 25 | 26 | if (data.value) { 27 | const { wsHost, logo, version, rtcConfig, options } = data.value; 28 | initData.value = Object.assign({}, initData.value, { 29 | socket: wsHost ? shallowReactive(io(wsHost)) : null, 30 | logo, 31 | version, 32 | options: Object.keys(options).reduce( 33 | (cur, next) => ({ ...cur, [next]: Boolean(options[next]) }), 34 | {} 35 | ), 36 | config: rtcConfig, 37 | }); 38 | } 39 | 40 | if (error.value) { 41 | throw error.value; 42 | } 43 | 44 | return { 45 | initData, 46 | }; 47 | }; 48 | -------------------------------------------------------------------------------- /client/packages/rtc-web/src/hooks/useSwitchByUrl.ts: -------------------------------------------------------------------------------- 1 | import { useWindowSize } from '@vueuse/core'; 2 | import { Ref, computed, watch } from 'vue'; 3 | import { useRouteQueryReactive } from './useRouterReactive'; 4 | 5 | export const useSwitchMember = () => { 6 | const { width } = useWindowSize(); 7 | const showMembers = useRouteQueryReactive('showMembers', '0', { 8 | transform: Number, 9 | }); 10 | 11 | const switchMember = () => { 12 | showMembers.value = showMembers.value === 0 ? 1 : 0; 13 | }; 14 | 15 | const open = computed(() => showMembers.value === 1); 16 | 17 | // 是否是宽屏, max-width = 1024 18 | const isLgScreen = computed(() => width.value <= 1024); 19 | 20 | watch( 21 | () => isLgScreen.value, 22 | (w) => { 23 | showMembers.value = w ? 0 : 1; 24 | }, 25 | { 26 | immediate: true, 27 | } 28 | ); 29 | 30 | return { 31 | open, 32 | switchMember, 33 | isLgScreen, 34 | }; 35 | }; 36 | 37 | export const useSwitchSiderbar = () => { 38 | const showSiderbar = useRouteQueryReactive('showSiderbar', '1', { 39 | transform: Number, 40 | }) as Ref; 41 | 42 | const switchSiderbar = () => { 43 | showSiderbar.value = showSiderbar.value === 0 ? 1 : 0; 44 | }; 45 | 46 | const open = computed(() => { 47 | return showSiderbar.value === 1; 48 | }); 49 | 50 | return { 51 | switchSiderbar, 52 | open, 53 | showSiderbar, 54 | }; 55 | }; 56 | -------------------------------------------------------------------------------- /doc/gitbook/install/INSTALL_BY_DOCKER_COMPOSE_BUILD_CODE.md: -------------------------------------------------------------------------------- 1 | # docker-compose自行打包部署 2 | 3 | 项目默认提供了 `docker/docker-compose-build-code.yml` 配置,供大家自行打包源码镜像使用。 4 | 5 | ### 前置条件 6 | 7 | - 需要安装好docker-compose,V1或者V2都可以 8 | 9 | - DockerHub需要科学上网 10 | 11 | - 需要下载源码包 12 | 13 | ### 准备打包 14 | 15 | 进入 `docker/` 目录后根据你的 `Docker Compose` 版本在主目录执行如下对应的命令 16 | 17 | - 对于 `Docker Compose V1` 18 | 19 | ``` 20 | docker-compose -f docker-compose-build-code.yml build 21 | ``` 22 | 23 | - 对于 `Docker Compose V2` 24 | 25 | ``` 26 | docker compose -f docker-compose-build-code.yml build 27 | ``` 28 | 29 | ### 启动容器 30 | 31 | 按照默认配置打包好镜像后,随后可以参照 `官方docker镜像一键脚本` 文档,进行启动即可。 32 | 33 | **如果有变动镜像名称或者容器名称之类的操作,需要注意同步修改相关启动配置** 34 | 35 | ### 注意事项 36 | 37 | 由于项目的服务需要依赖一些配置,所以容器方式都挂载了一些配置文件。 38 | 39 | api容器和socket容器 挂载配置如下 40 | 41 | ``` 42 | volumes: 43 | - ./tlrtcfile.env:/tlrtcfile/tlrtcfile.env 44 | ``` 45 | 46 | mysql容器的挂载配置如下,作用是: 将mysql容器内的数据文件挂载出来进行保存 47 | 48 | ``` 49 | volumes: 50 | - ./docker/mysql/data/db:/var/lib/mysql 51 | - ./docker/mysql/data/my.cnf:/etc/mysql/conf.d/my.cnf 52 | - ./docker/mysql/data/log:/var/log/mysql 53 | - ./docker/mysql/data/init.sql:/docker-entrypoint-initdb.d/init.sql 54 | ``` 55 | 56 | coturn的挂载配置,作用是: 将项目默认提供的turn的配置,提供给coturn容器使用 57 | 58 | ``` 59 | volumes: 60 | - ./docker/coturn/turnserver-with-secret-user.conf:/etc/turnserver.conf 61 | ``` 62 | 63 | 如果觉得这种方式麻烦,或者不合适自己的场景,可以选择另外一种 `内置配置的docker-compose` -------------------------------------------------------------------------------- /bin/ubuntu16/auto-update-project.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ######################### 3 | # 提供一键更新项目代码版本的脚本 4 | # @auther: iamtsm 5 | # @version: v1.0.0 6 | ######################### 7 | 8 | # Check if the current directory is a Git repository 9 | if [ -d .git ]; then 10 | # The current directory is a Git repository, so we can pull the latest changes 11 | echo "Current directory is a Git repository. Pulling latest changes..." 12 | git pull 13 | else 14 | # The current directory is not a Git repository 15 | echo "Current directory is not a Git repository." 16 | 17 | # Check if Git is installed 18 | if ! command -v git &> /dev/null; then 19 | # Git is not installed, so let's try to install it 20 | echo "Git is not installed. Installing Git..." 21 | sudo apt-get update 22 | sudo apt-get install -y git # Adjust this for CentOS or other Linux distributions 23 | fi 24 | 25 | # Initialize a new Git repository and set the remote URL 26 | echo "Initializing a new Git repository and setting remote URL..." 27 | git init 28 | git remote add origin https://github.com/tl-open-source/tl-rtc-file.git 29 | 30 | # Pull the latest changes from the remote repository (use 'master' branch) 31 | git pull origin master 32 | 33 | # Optionally, you can set the default branch to 'master' 34 | git symbolic-ref refs/remotes/origin/HEAD refs/remotes/origin/master 35 | fi 36 | 37 | echo "Done." -------------------------------------------------------------------------------- /bin/ubuntu18/auto-update-project.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ######################### 3 | # 提供一键更新项目代码版本的脚本 4 | # @auther: iamtsm 5 | # @version: v1.0.0 6 | ######################### 7 | 8 | # Check if the current directory is a Git repository 9 | if [ -d .git ]; then 10 | # The current directory is a Git repository, so we can pull the latest changes 11 | echo "Current directory is a Git repository. Pulling latest changes..." 12 | git pull 13 | else 14 | # The current directory is not a Git repository 15 | echo "Current directory is not a Git repository." 16 | 17 | # Check if Git is installed 18 | if ! command -v git &> /dev/null; then 19 | # Git is not installed, so let's try to install it 20 | echo "Git is not installed. Installing Git..." 21 | sudo apt-get update 22 | sudo apt-get install -y git # Adjust this for CentOS or other Linux distributions 23 | fi 24 | 25 | # Initialize a new Git repository and set the remote URL 26 | echo "Initializing a new Git repository and setting remote URL..." 27 | git init 28 | git remote add origin https://github.com/tl-open-source/tl-rtc-file.git 29 | 30 | # Pull the latest changes from the remote repository (use 'master' branch) 31 | git pull origin master 32 | 33 | # Optionally, you can set the default branch to 'master' 34 | git symbolic-ref refs/remotes/origin/HEAD refs/remotes/origin/master 35 | fi 36 | 37 | echo "Done." -------------------------------------------------------------------------------- /bin/ubuntu20/auto-update-project.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ######################### 3 | # 提供一键更新项目代码版本的脚本 4 | # @auther: iamtsm 5 | # @version: v1.0.0 6 | ######################### 7 | 8 | # Check if the current directory is a Git repository 9 | if [ -d .git ]; then 10 | # The current directory is a Git repository, so we can pull the latest changes 11 | echo "Current directory is a Git repository. Pulling latest changes..." 12 | git pull 13 | else 14 | # The current directory is not a Git repository 15 | echo "Current directory is not a Git repository." 16 | 17 | # Check if Git is installed 18 | if ! command -v git &> /dev/null; then 19 | # Git is not installed, so let's try to install it 20 | echo "Git is not installed. Installing Git..." 21 | sudo apt-get update 22 | sudo apt-get install -y git # Adjust this for CentOS or other Linux distributions 23 | fi 24 | 25 | # Initialize a new Git repository and set the remote URL 26 | echo "Initializing a new Git repository and setting remote URL..." 27 | git init 28 | git remote add origin https://github.com/tl-open-source/tl-rtc-file.git 29 | 30 | # Pull the latest changes from the remote repository (use 'master' branch) 31 | git pull origin master 32 | 33 | # Optionally, you can set the default branch to 'master' 34 | git symbolic-ref refs/remotes/origin/HEAD refs/remotes/origin/master 35 | fi 36 | 37 | echo "Done." -------------------------------------------------------------------------------- /client/packages/rtc-web/src/assets/svg-icon/member.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/packages/rtc-web/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /svr/src/bussiness/check/content.js: -------------------------------------------------------------------------------- 1 | const checkLib = require("./core").trie 2 | 3 | /** 4 | * 内容过滤 5 | * @param {*} text 6 | * @param {*} replaceChar 7 | * @returns 8 | */ 9 | function contentFilter(text, replaceChar = '*') { 10 | let filteredText = ''; 11 | let currentIndex = 0; 12 | 13 | while (currentIndex < text.length) { 14 | const match = checkLib.search(text.slice(currentIndex)); 15 | if (match) { 16 | filteredText += replaceChar.repeat(match.length); 17 | currentIndex += match.length; 18 | } else { 19 | filteredText += text[currentIndex]; 20 | currentIndex++; 21 | } 22 | } 23 | return filteredText; 24 | } 25 | 26 | /** 27 | * 对象内容过滤 28 | * @param {*} text 29 | * @returns 30 | */ 31 | function objectContentFilter(obj) { 32 | if (obj === null || obj === undefined) { 33 | return obj; 34 | } 35 | if (typeof obj === 'string') { 36 | return contentFilter(obj); 37 | } 38 | if (typeof obj === 'object') { 39 | if (obj instanceof Array) { 40 | return obj.map(item => objectContentFilter(item)); 41 | } else { 42 | const newObj = {}; 43 | for (const key in obj) { 44 | newObj[key] = objectContentFilter(obj[key]); 45 | } 46 | return newObj; 47 | } 48 | } 49 | return obj; 50 | } 51 | 52 | module.exports = { 53 | contentFilter, objectContentFilter 54 | } -------------------------------------------------------------------------------- /svr/src/socket/rtcToken/token.js: -------------------------------------------------------------------------------- 1 | const request = require('request'); 2 | const {inject_env_config} = require("./../../../conf/env_config"); 3 | const cfg = inject_env_config(require("./../../../conf/cfg.json")) 4 | const utils = require("./../../utils/utils"); 5 | 6 | /** 7 | * token 处理 8 | * 通过token去api服务拿到用户信息, 在后续的事件中可以关联到用户信息 9 | * @param {*} io socketio对象 10 | * @param {*} socket 单个socket连接 11 | * @param {*} tables 数据表对象 12 | * @param {*} dbClient sequelize-orm对象 13 | * @param {*} data event参数 14 | * @returns 15 | */ 16 | async function token(io, socket, tables, dbClient, data){ 17 | const { token } = socket.handshake.query; 18 | 19 | if(!token || token.length < 16){ 20 | utils.tlConsole("匿名用户-token空") 21 | return; 22 | } 23 | 24 | request({ 25 | method: "POST", 26 | url: `${cfg.login.token.url}/api/login/info`, 27 | json: true, 28 | headers: { 29 | "content-type": "application/json", 30 | }, 31 | qs: { 32 | token, key : cfg.login.token.key 33 | }, 34 | }, (err, res, body) => { 35 | if(err){ 36 | console.log(err); 37 | return; 38 | } 39 | 40 | if(body.code !== 200){ 41 | console.log(body); 42 | return; 43 | } 44 | 45 | socket.userInfo = body.userInfo; 46 | 47 | utils.tlConsole("同步token信息成功 : ", token, body.userInfo) 48 | }); 49 | } 50 | 51 | module.exports = { 52 | token 53 | } -------------------------------------------------------------------------------- /svr/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tl-rtc-file", 3 | "version": "10.2.8", 4 | "description": "webrtc, p2p, file, screen, video, live, draw, chat", 5 | "main": "main.js", 6 | "scripts": { 7 | "http-api": "cross-env tl_rtc_file_env_mode=http node tlapi", 8 | "http-socket": "cross-env tl_rtc_file_env_mode=http node tlsocket", 9 | "https-api": "cross-env tl_rtc_file_env_mode=https node tlapi", 10 | "https-socket": "cross-env tl_rtc_file_env_mode=https node tlsocket", 11 | "test": "cross-env mocha ./test/test.js", 12 | "build:dev": "vite build --watch", 13 | "build:pro": "vite build" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/tl-open-source/tl-rtc-file" 18 | }, 19 | "keywords": [ 20 | "none" 21 | ], 22 | "author": "iamtsm", 23 | "license": "MIT", 24 | "dependencies": { 25 | "@grpc/grpc-js": "^1.8.0", 26 | "@grpc/proto-loader": "^0.6.0", 27 | "body-parser": "^1.20.2", 28 | "concurrently": "^8.2.0", 29 | "cookie-parser": "^1.4.6", 30 | "cross-env": "^5.2.0", 31 | "dotenv": "^16.3.1", 32 | "express": "^4.17.1", 33 | "glob": "^10.3.1", 34 | "google-protobuf": "^3.0.0", 35 | "lru-cache": "^10.0.1", 36 | "mocha": "^10.2.0", 37 | "mysql2": "^2.1.0", 38 | "openai": "^3.3.0", 39 | "request": "^2.88.2", 40 | "rollup-plugin-copy": "^3.4.0", 41 | "sequelize": "^6.1.0", 42 | "sequelize-pool": "^6.0.0", 43 | "socket.io": "^2.3.0", 44 | "terser": "^5.18.2", 45 | "tl-ngrpc": "^1.0.1", 46 | "uuid": "^9.0.1", 47 | "vite": "^4.3.9" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /client/packages/rtc-web/src/hooks/useRouterReactive.ts: -------------------------------------------------------------------------------- 1 | import { useRouteParams, useRouteQuery } from '@vueuse/router'; 2 | import { Ref, toRefs, watch } from 'vue'; 3 | import { useRoute } from 'vue-router'; 4 | 5 | export const useRouteParamsReactive = (keys: T[]) => { 6 | const params = keys.reduce( 7 | (cur, next) => ({ ...cur, [next]: useRouteParams(next) }), 8 | {} as Record> 9 | ); 10 | 11 | const route = useRoute(); 12 | 13 | watch( 14 | () => route.params, 15 | (v) => { 16 | Object.keys(v).forEach((key) => { 17 | if (params[key as T]) { 18 | params[key as T].value = v[key]; 19 | } 20 | }); 21 | }, 22 | { 23 | immediate: true, 24 | } 25 | ); 26 | 27 | return params; 28 | }; 29 | 30 | export const useRouteQueryReactive = ( 31 | ...args: Parameters 32 | ) => { 33 | const { query } = toRefs(useRoute()); 34 | 35 | const [key, defaultValue, options] = args; 36 | const valueRef = useRouteQuery(key, defaultValue, options); 37 | 38 | watch( 39 | () => query.value[key], 40 | (v: any) => { 41 | const nv = options?.transform?.(v); 42 | if (options?.transform === Number) { 43 | if (!isNaN(nv as number)) { 44 | valueRef.value = nv; 45 | } 46 | } else { 47 | valueRef.value = nv; 48 | } 49 | }, 50 | { 51 | immediate: true, 52 | deep: true, 53 | } 54 | ); 55 | 56 | return valueRef; 57 | }; 58 | -------------------------------------------------------------------------------- /client/packages/rtc-web/src/assets/svg-icon/user-smail.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/packages/rtc-web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rtc-web", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vue-tsc && vite build" 9 | }, 10 | "dependencies": { 11 | "@types/lodash": "^4.14.195", 12 | "@vitejs/plugin-vue-jsx": "^3.0.1", 13 | "@vueuse/core": "^10.2.0", 14 | "@vueuse/router": "^10.2.1", 15 | "dayjs": "^1.11.9", 16 | "lodash": "^4.17.21", 17 | "nanoid": "^4.0.2", 18 | "socket.io-client": "2.3.0", 19 | "vue": "^3.3.4", 20 | "vue-router": "4", 21 | "vue3-emoji-picker": "^1.1.7", 22 | "vue3-popper": "^1.5.0" 23 | }, 24 | "devDependencies": { 25 | "@types/node": "^20.3.1", 26 | "@typescript-eslint/eslint-plugin": "^5.60.0", 27 | "@typescript-eslint/parser": "^5.60.0", 28 | "@vitejs/plugin-vue": "^4.2.3", 29 | "autoprefixer": "^10.4.14", 30 | "daisyui": "^3.1.6", 31 | "eslint": "^8.43.0", 32 | "eslint-config-prettier": "^8.8.0", 33 | "eslint-plugin-prettier": "^4.2.1", 34 | "eslint-plugin-vue": "^9.15.1", 35 | "postcss": "^8.4.24", 36 | "postcss-import": "^15.1.0", 37 | "prettier": "^2.8.8", 38 | "prettier-plugin-tailwindcss": "^0.3.0", 39 | "tailwindcss": "^3.3.2", 40 | "terser": "^5.19.4", 41 | "typescript": "^5.1.3", 42 | "vconsole": "^3.15.1", 43 | "vite": "^4.3.9", 44 | "vite-plugin-eslint": "^1.8.1", 45 | "vite-plugin-svg-icons": "^2.0.1", 46 | "vue-eslint-parser": "^9.3.1", 47 | "vue-tsc": "^1.8.1" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /client/packages/rtc-web/src/components/base/nav-icons.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /svr/src/socket/rtcDraw/draw.js: -------------------------------------------------------------------------------- 1 | const utils = require("./../../utils/utils"); 2 | const daoDog = require("./../../dao/dog/dog") 3 | const rtcConstant = require("../rtcConstant"); 4 | const rtcClientEvent = rtcConstant.rtcClientEvent 5 | const check = require("../../bussiness/check/content"); 6 | const daoRelation = require("../../dao/relation/relation"); 7 | 8 | 9 | /** 10 | * canvas画图 11 | * @param {*} io socketio对象 12 | * @param {*} socket 单个socket连接 13 | * @param {*} tables 数据表对象 14 | * @param {*} dbClient sequelize-orm对象 15 | * @param {*} data event参数 16 | * @returns 17 | */ 18 | async function draw(io, socket, tables, dbClient, data){ 19 | let { to } = data; 20 | 21 | let {handshake, userAgent, ip} = utils.getSocketClientInfo(socket); 22 | 23 | let otherClient = io.sockets.connected[to]; 24 | if (!otherClient){ 25 | return; 26 | } 27 | 28 | otherClient.emit(rtcClientEvent.draw, data); 29 | 30 | //控制操作事件入库 31 | let recoderId = await daoDog.addDogData({ 32 | name: "canvas画图", 33 | roomId: "", 34 | socketId: "", 35 | device: userAgent, 36 | flag: 0, 37 | content: JSON.stringify(data), 38 | handshake: JSON.stringify(handshake), 39 | ip: ip 40 | }, tables, dbClient); 41 | 42 | //添加用户-操作关联记录 43 | if(socket.userId){ 44 | daoRelation.addUserDogRelation({ 45 | dogId : recoderId, 46 | userId : socket.userId, 47 | }, tables, dbClient); 48 | } 49 | } 50 | 51 | module.exports = { 52 | draw 53 | } -------------------------------------------------------------------------------- /client/packages/rtc-web/src/views/chat/hooks/useChat.ts: -------------------------------------------------------------------------------- 1 | import { ChatEventName } from '@/config'; 2 | import { useSocket } from '@/hooks'; 3 | import { unescapeStr } from '@/utils'; 4 | import { ref, shallowRef } from 'vue'; 5 | 6 | export type SendMessage = { 7 | content: string; 8 | room: string; 9 | from: string; 10 | nickName: string; 11 | recoderId: any; 12 | time: string; 13 | to?: string; 14 | }; 15 | 16 | // export type MessageStatus = 'fail' | 'complete' | 'pending'; 17 | export type MessageType = 'send' | 'receive'; 18 | 19 | export type MessageInfo = { 20 | content: string; 21 | // status: MessageStatus; 22 | type: MessageType; 23 | } & SendMessage; 24 | 25 | export const useChat = () => { 26 | const socketRef = shallowRef(); 27 | const msgList = ref([]); 28 | 29 | const handleChat = (socket: any) => { 30 | socketRef.value = socket; 31 | socket.on(ChatEventName.ChatingRoom, handleChatingRoom); 32 | }; 33 | const handleChatingRoom = (data: SendMessage) => { 34 | console.log('receive', data); 35 | msgList.value.push({ 36 | ...data, 37 | content: unescapeStr(data.content), 38 | type: 'receive', 39 | }); 40 | }; 41 | 42 | const sendMessage = (data: SendMessage) => { 43 | socketRef.value.emit(ChatEventName.ChatingRoom, data); 44 | msgList.value.push({ 45 | ...data, 46 | content: unescapeStr(data.content), 47 | type: 'send', 48 | }); 49 | }; 50 | 51 | useSocket(handleChat); 52 | 53 | return { 54 | sendMessage, 55 | msgList, 56 | }; 57 | }; 58 | -------------------------------------------------------------------------------- /svr/src/tables/room.js: -------------------------------------------------------------------------------- 1 | // room 2 | module.exports = (sequelize, DataTypes) => { 3 | return { 4 | RoomOther: { 5 | Flag : { 6 | IS_MANAGE_ROOM : 0x1, //是否是管理后台房间 7 | IS_SYSTEM_QUESTION_ROOM : 0x2, //是否是系统反馈问题房间 8 | }, 9 | }, 10 | Room: sequelize.define('room', { 11 | id: { 12 | type: DataTypes.INTEGER, 13 | primaryKey: true, 14 | autoIncrement: true, 15 | unique: true, 16 | comment: '数据id', 17 | }, 18 | room_id: { 19 | type: DataTypes.STRING(32), 20 | comment: '房间频道号码' 21 | }, 22 | uid: { 23 | type: DataTypes.INTEGER, 24 | comment: '用户的id', 25 | }, 26 | uname: { 27 | type: DataTypes.STRING(20), 28 | comment: '用户姓名,昵称' 29 | }, 30 | socket_id: { 31 | type: DataTypes.STRING(30), 32 | comment: '用户进入房间时的socket.id' 33 | }, 34 | pwd: { 35 | type: DataTypes.STRING(6), 36 | comment: '房间密码' 37 | }, 38 | status: { 39 | type: DataTypes.INTEGER, 40 | comment: '房间状态', 41 | defaultValue: 0 42 | }, 43 | ip: { 44 | type: DataTypes.STRING(32), 45 | comment: 'ip' 46 | }, 47 | device: { 48 | type: DataTypes.STRING(256), 49 | comment: '设备信息' 50 | }, 51 | flag: { 52 | type: DataTypes.INTEGER, 53 | comment: '标志位', 54 | defaultValue: 0, 55 | }, 56 | content: { 57 | type: DataTypes.TEXT, 58 | comment: '详细信息' 59 | } 60 | }, { 61 | timestamps: true, 62 | comment: '匿名用户房间表', 63 | indexes: [{ 64 | name: 'created_at_index', 65 | method: 'BTREE', 66 | fields: ['created_at'] 67 | }] 68 | }) 69 | }; 70 | } 71 | -------------------------------------------------------------------------------- /svr/vite.config.js: -------------------------------------------------------------------------------- 1 | import path from "path"; 2 | import * as glob from "glob"; 3 | import { fileURLToPath } from 'url'; 4 | import copy from "rollup-plugin-copy"; 5 | const url = import.meta.url; 6 | 7 | export default { 8 | plugins: [ 9 | copy({ 10 | targets: [ 11 | { src: 'web-res/*.html', dest: 'web-res/dist' }, 12 | { src: 'web-res/image/*', dest: 'web-res/dist/image' }, 13 | { src: 'static/*', dest: 'web-res/dist/static' }, 14 | ], 15 | hook: 'writeBundle', 16 | verbose: true 17 | }) 18 | ], 19 | build: { 20 | rollupOptions: { 21 | input: Object.fromEntries( 22 | glob.sync('web-res/*(js|css)/*.*(js|css)').map(file => [ 23 | path.relative( 24 | './web-res', 25 | file.slice(0, file.length - path.extname(file).length) 26 | ), 27 | fileURLToPath(new URL(file, url)) 28 | ]) 29 | ), 30 | output: { 31 | dir: "./web-res/dist/", 32 | entryFileNames: "[name].min.js", 33 | assetFileNames: "css/[name].min[extname]" 34 | }, 35 | }, 36 | minify: "terser", 37 | terserOptions: { 38 | compress: true, 39 | mangle: true, 40 | toplevel: false, 41 | keep_classnames: false 42 | }, 43 | reportCompressedSize: false, 44 | sourcemap: false, 45 | }, 46 | }; -------------------------------------------------------------------------------- /client/packages/rtc-web/src/assets/svg-icon/moon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /bin/auto-push-manifest-to-hub.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ######################### 3 | # 一键推送dockerhub多架构脚本 4 | # @auther: iamtsm 5 | # @version: v1.0.0 6 | ######################### 7 | 8 | build_and_push_manifest() { 9 | local image_name=$1 10 | local tag=$2 11 | local target_name=$3 12 | local image_prefix="iamtsm/tl-rtc-file" 13 | local arch_arm64="arm64" 14 | local arch_amd64="amd64" 15 | 16 | echo "###################################### craete manifest $image_prefix-$target_name:$tag" 17 | docker manifest create $image_prefix-$target_name:$tag \ 18 | $image_prefix-$target_name-$arch_arm64:$tag \ 19 | $image_prefix-$target_name-$arch_amd64:$tag --amend 20 | 21 | echo "###################################### push manifest $image_prefix-$target_name:$tag" 22 | docker manifest push $image_prefix-$target_name:$tag 23 | } 24 | 25 | latest_version=latest 26 | 27 | if [ $# -eq 0 ]; then 28 | # 如果没有传入参数,默认执行所有镜像的打包发布逻辑 29 | echo "Please input args" 30 | else 31 | # 有传入参数时,遍历处理每个参数 32 | for image_arg in "$@"; do 33 | case $image_arg in 34 | api) 35 | build_and_push_manifest "api" $latest_version "api" 36 | ;; 37 | socket) 38 | build_and_push_manifest "socket" $latest_version "socket" 39 | ;; 40 | mysql) 41 | build_and_push_manifest "mysql" $latest_version "mysql" 42 | ;; 43 | coturn) 44 | build_and_push_manifest "coturn" $latest_version "coturn" 45 | ;; 46 | *) 47 | echo "Invalid argument: $image_arg" 48 | ;; 49 | esac 50 | done 51 | fi -------------------------------------------------------------------------------- /client/packages/rtc-web/src/assets/svg-icon/emoji.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /svr/tlsocket.js: -------------------------------------------------------------------------------- 1 | const http = require('http'); // http 2 | const https = require('https'); 3 | const socketIO = require('socket.io'); 4 | const fs = require('fs'); 5 | const db = require("./src/tables/db"); //db 6 | const { inject_env_config, load_env_config } = require("./conf/env_config"); 7 | //加载环境变量 8 | load_env_config(); 9 | //加载环境变量完毕后,注入配置 10 | const conf = inject_env_config(require("./conf/cfg.json")); //conf 11 | const socket = require("./src/socket/index") //socket handler 12 | const utils = require("./src/utils/utils"); 13 | 14 | //打印logo 15 | utils.tlConsoleIcon() 16 | 17 | async function start(){ 18 | // Socket连接监听 19 | let io = null; 20 | 21 | if(process.env.tl_rtc_file_env_mode == 'http'){ 22 | io = socketIO.listen(http.createServer().listen(conf.socket.port)); 23 | }else{ 24 | let options = { 25 | key: fs.readFileSync('./conf/keys/server.key'), 26 | cert: fs.readFileSync('./conf/keys/server.crt') 27 | } 28 | io = socketIO.listen( 29 | https.createServer(options).listen(conf.socket.port) 30 | ); 31 | } 32 | 33 | if (!conf.db.open) {// 没开db 34 | socket.excute({}, {}, io); 35 | utils.tlConsole("db not open ...") 36 | } else { // 开了db 37 | let { tables, dbClient } = await db.excute(conf) 38 | socket.excute(tables, dbClient, io); 39 | utils.tlConsole("db init done ...") 40 | } 41 | 42 | utils.tlConsole("socket init done ...") 43 | utils.tlConsole("socket ",process.env.tl_rtc_file_env_mode," server listen on ", conf.socket.port, " successful"); 44 | } 45 | 46 | 47 | start(); -------------------------------------------------------------------------------- /client/packages/rtc-web/src/components/form-room/form-room.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /svr/static/layui/css/modules/code.css: -------------------------------------------------------------------------------- 1 | html #layuicss-skincodecss{display:none;position:absolute;width:1989px}.layui-code-view{display:block;position:relative;margin:10px 0;padding:0;border:1px solid #eee;border-left-width:6px;background-color:#fafafa;color:#333;font-family:Courier New;font-size:13px}.layui-code-title{position:relative;padding:0 10px;height:40px;line-height:40px;border-bottom:1px solid #eee;font-size:12px}.layui-code-title>.layui-code-about{position:absolute;right:10px;top:0;color:#b7b7b7}.layui-code-about>a{padding-left:10px}.layui-code-view>.layui-code-ol,.layui-code-view>.layui-code-ul{position:relative;overflow:auto}.layui-code-view>.layui-code-ol>li{position:relative;margin-left:45px;line-height:20px;padding:0 10px;border-left:1px solid #e2e2e2;list-style-type:decimal-leading-zero;*list-style-type:decimal;background-color:#fff}.layui-code-view>.layui-code-ol>li:first-child,.layui-code-view>.layui-code-ul>li:first-child{padding-top:10px}.layui-code-view>.layui-code-ol>li:last-child,.layui-code-view>.layui-code-ul>li:last-child{padding-bottom:10px}.layui-code-view>.layui-code-ul>li{position:relative;line-height:20px;padding:0 10px;list-style-type:none;*list-style-type:none;background-color:#fff}.layui-code-view pre{margin:0}.layui-code-dark{border:1px solid #0c0c0c;border-left-color:#3f3f3f;background-color:#0c0c0c;color:#c2be9e}.layui-code-dark>.layui-code-title{border-bottom:none}.layui-code-dark>.layui-code-ol>li,.layui-code-dark>.layui-code-ul>li{background-color:#3f3f3f;border-left:none}.layui-code-dark>.layui-code-ul>li{margin-left:6px}.layui-code-demo .layui-code{visibility:visible!important;margin:-15px;border-top:none;border-right:none;border-bottom:none}.layui-code-demo .layui-tab-content{padding:15px;border-top:none} -------------------------------------------------------------------------------- /svr/src/controller/comm/comm.js: -------------------------------------------------------------------------------- 1 | const utils = require("../../utils/utils"); 2 | const {inject_env_config} = require("../../../conf/env_config") 3 | const conf = inject_env_config(require("../../../conf/cfg.json")); 4 | const webrtcConf = conf.webrtc; 5 | 6 | /** 7 | * 获取ip地址,初始化等相关配置 8 | * @param {*} req 9 | * @param {*} res 10 | */ 11 | function initData(req, res) { 12 | //是否开启turn 13 | const openTurn = (req.query.turn || "") === 'true'; 14 | //使用的账号模式, true : 有效账号模式, false : 固定账号 15 | const useSecret = (req.query.secret || "") === 'true' || true; 16 | 17 | //ice服务器配置 18 | const iceServers = utils.genTurnServerIceServersConfig(openTurn, useSecret, "tlrtcfile"); 19 | 20 | //系统房间 21 | const systemRoomList = ['tlrtcfile问题反馈']; 22 | 23 | if(process.env.tl_rtc_file_env_mode === 'http'){ 24 | 25 | let regexIP = /^((?:(?:25[0-5]|2[0-4]\d|[01]?\d?\d)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d?\d))$/; 26 | let ip = utils.getLocalIP(); 27 | if (!regexIP.test(ip)) { 28 | ip = utils.getClientIP(req) 29 | } 30 | if (!regexIP.test(ip)) { 31 | ip = "127.0.0.1" 32 | } 33 | 34 | let wsHost = conf.socket.host || ip + conf.socket.port; 35 | 36 | res.json({ 37 | version : conf.version, 38 | wsHost: "ws://" + wsHost, 39 | rtcConfig: { iceServers }, 40 | options: webrtcConf.options, 41 | logo : utils.genClientLogo(), 42 | systemRoomList : systemRoomList 43 | }) 44 | }else{ 45 | 46 | let wsHost = conf.socket.host || ip; 47 | 48 | res.json({ 49 | version : conf.version, 50 | wsHost: "wss://" + wsHost, 51 | rtcConfig: { iceServers }, 52 | options: webrtcConf.options, 53 | logo : utils.genClientLogo(), 54 | systemRoomList : systemRoomList 55 | }) 56 | } 57 | } 58 | 59 | module.exports = { 60 | initData, 61 | } -------------------------------------------------------------------------------- /svr/web-res/css/comm.css: -------------------------------------------------------------------------------- 1 | 2 | [v-cloak]{ 3 | visibility: hidden !important; /*防止被覆盖*/ 4 | } 5 | 6 | /* svg主题 */ 7 | .svg_black{ 8 | width: 22px; 9 | height: 22px; 10 | fill: black; 11 | } 12 | .svg_white{ 13 | width: 22px; 14 | height: 22px; 15 | fill: #168ce6; 16 | } 17 | .svg_black:hover{ 18 | width: 22px; 19 | height: 22px; 20 | fill: #168ce6; 21 | } 22 | .svg_white:hover{ 23 | width: 22px; 24 | height: 22px; 25 | fill: black; 26 | } 27 | 28 | /* 按钮主题 */ 29 | .btn_black{ 30 | width: 22px; 31 | height: 22px; 32 | background-color: black; 33 | } 34 | .btn_white{ 35 | width: 22px; 36 | height: 22px; 37 | background-color: #168ce6; 38 | } 39 | .btn_black:hover{ 40 | width: 22px; 41 | height: 22px; 42 | background-color: #168ce6; 43 | } 44 | .btn_white:hover{ 45 | width: 22px; 46 | height: 22px; 47 | background-color: black; 48 | } 49 | 50 | /* 字体主题 */ 51 | .font_black{ 52 | width: 22px; 53 | height: 22px; 54 | color: black; 55 | } 56 | .font_white{ 57 | width: 22px; 58 | height: 22px; 59 | color: #168ce6; 60 | } 61 | .font_black:hover{ 62 | width: 22px; 63 | height: 22px; 64 | color: #168ce6; 65 | } 66 | .font_white:hover{ 67 | width: 22px; 68 | height: 22px; 69 | color: black; 70 | } 71 | 72 | 73 | /* 侧边栏主题 */ 74 | ::-webkit-scrollbar{ 75 | width: 4px; 76 | height: 4px; 77 | background-color: #F5F5F5; 78 | } 79 | ::-webkit-scrollbar-track{ 80 | -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3); 81 | border-radius: 10px; 82 | background-color: #FFF; 83 | } 84 | ::-webkit-scrollbar-thumb{ 85 | border-radius: 10px; 86 | -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,.3); 87 | background-color: #AAA; 88 | } -------------------------------------------------------------------------------- /client/packages/rtc-web/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | import vue from '@vitejs/plugin-vue'; 3 | import eslintPlugin from 'vite-plugin-eslint'; 4 | import { resolve } from 'path'; 5 | import vueJsx from '@vitejs/plugin-vue-jsx'; 6 | 7 | import fs from 'fs'; 8 | 9 | import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'; 10 | 11 | const pathResolve = (path: string) => resolve(__dirname, path); 12 | 13 | // http://localhost:9092/api 14 | // https://im.iamtsm.cn/api 15 | 16 | // https://vitejs.dev/config/ 17 | export default defineConfig({ 18 | build: { 19 | outDir: resolve(__dirname, '../../dist/rtc-web'), 20 | minify: 'terser', 21 | emptyOutDir: true, 22 | terserOptions: { 23 | compress: { 24 | drop_console: true, 25 | drop_debugger: true, 26 | }, 27 | }, 28 | }, 29 | resolve: { 30 | alias: [ 31 | { 32 | find: '@', 33 | replacement: pathResolve('src'), 34 | }, 35 | ], 36 | }, 37 | server: { 38 | host: '0.0.0.0', 39 | proxy: { 40 | '/api': { 41 | target: 'https://192.168.1.11:9092/api', 42 | changeOrigin: true, 43 | rewrite: (path) => path.replace(/^\/api/, ''), 44 | secure: false, 45 | }, 46 | }, 47 | https: { 48 | key: fs.readFileSync('./src/keys/server.key'), 49 | cert: fs.readFileSync('./src/keys/server.crt'), 50 | }, 51 | }, 52 | plugins: [ 53 | vue(), 54 | vueJsx(), 55 | eslintPlugin(), 56 | createSvgIconsPlugin({ 57 | iconDirs: [pathResolve('src/assets/svg-icon')], 58 | // 指定symbolId格式 59 | symbolId: 'icon-[dir]-[name]', 60 | }), 61 | // basicSsl(), 62 | // mkcert({ 63 | // source: 'coding', 64 | // }), 65 | ], 66 | }); 67 | -------------------------------------------------------------------------------- /svr/src/socket/rtcSubscribe/subscribe.js: -------------------------------------------------------------------------------- 1 | const utils = require("./../../utils/utils"); 2 | const daoDog = require("./../../dao/dog/dog") 3 | const daoUser = require("./../../dao/user/user") 4 | const rtcConstant = require("../rtcConstant"); 5 | const rtcClientEvent = rtcConstant.rtcClientEvent 6 | const daoRelation = require("../../dao/relation/relation"); 7 | 8 | 9 | /** 10 | * 订阅网站通知 11 | * @param {*} io socketio对象 12 | * @param {*} socket 单个socket连接 13 | * @param {*} tables 数据表对象 14 | * @param {*} dbClient sequelize-orm对象 15 | * @param {*} data event参数 16 | * @returns 17 | */ 18 | async function subscribeNofity(io, socket, tables, dbClient, data){ 19 | let { roomId = '', email = '' } = data; 20 | 21 | let { handshake, userAgent, ip } = utils.getSocketClientInfo(socket); 22 | 23 | socket.emit(rtcClientEvent.subscribeNofity, data); 24 | 25 | //控制操作事件入库 26 | let recoderId = await daoDog.addDogData({ 27 | name: "订阅网站通知", 28 | roomId: roomId, 29 | socketId: socket.id, 30 | device: userAgent, 31 | flag: 0, 32 | content: JSON.stringify(data), 33 | handshake: JSON.stringify(handshake), 34 | ip: ip 35 | }, tables, dbClient); 36 | 37 | //更新用户订阅标识 38 | const { userId, flag = 0 } = socket.userInfo; 39 | if(userId){ 40 | await daoUser.updateUserFlag({ 41 | id : userId, 42 | flag : utils.setBit(flag, tables.UserOther.Flag.IS_SUBSCRIBE_WEBSITE_NOTIFY) 43 | }, tables, dbClient); 44 | } 45 | 46 | //添加用户-操作关联记录 47 | if(socket.userId){ 48 | daoRelation.addUserDogRelation({ 49 | dogId : recoderId, 50 | userId : socket.userId, 51 | }, tables, dbClient); 52 | } 53 | } 54 | 55 | module.exports = { 56 | subscribeNofity 57 | } -------------------------------------------------------------------------------- /client/packages/rtc-web/src/components/base/toast.vue: -------------------------------------------------------------------------------- 1 | 55 | 56 | 74 | -------------------------------------------------------------------------------- /client/packages/rtc-web/src/views/video/video.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 57 | -------------------------------------------------------------------------------- /client/packages/rtc-web/src/views/chat/chat.vue: -------------------------------------------------------------------------------- 1 | 42 | 43 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /docker/docker-compose-build-code.yml: -------------------------------------------------------------------------------- 1 | ## build the image from the Dockerfile in the current directory 2 | ## 用于自己通过代码构建镜像并启动 3 | 4 | version: '3' 5 | services: 6 | 7 | #api服务 8 | api: 9 | container_name: api 10 | build: 11 | context: ./../ 12 | dockerfile: ./Dockerfile 13 | env_file: 14 | - ../tlrtcfile.env 15 | command: 16 | - tlapi 17 | ports: 18 | - 9092:9092 19 | links: 20 | - mysql 21 | depends_on: 22 | - mysql 23 | - coturn 24 | volumes: 25 | - ../tlrtcfile.env:/tlrtcfile/tlrtcfile.env 26 | 27 | #socket服务 28 | socket: 29 | container_name: socket 30 | build: 31 | context: ./../ 32 | dockerfile: ./Dockerfile 33 | command: 34 | - tlsocket 35 | env_file: 36 | - ../tlrtcfile.env 37 | ports: 38 | - 8444:8444 39 | links: 40 | - mysql 41 | depends_on: 42 | - mysql 43 | - coturn 44 | volumes: 45 | - ../tlrtcfile.env:/tlrtcfile/tlrtcfile.env 46 | 47 | #mysql服务 48 | mysql: 49 | container_name: mysql 50 | restart: always 51 | env_file: 52 | - ./mysql/mysql.env 53 | build: 54 | context: ./mysql/ 55 | dockerfile: ./Dockerfile 56 | ports: 57 | - 3306:3306 58 | volumes: 59 | - ./mysql/data/mysql.env:/tlrtcfile/docker/mysql/mysql.env 60 | - ./mysql/data/db:/var/lib/mysql 61 | - ./mysql/data/my.cnf:/etc/mysql/conf.d/my.cnf 62 | - ./mysql/data/log:/var/log/mysql 63 | - ./mysql/data/init.sql:/docker-entrypoint-initdb.d/init.sql 64 | 65 | #coturn服务 66 | coturn: 67 | container_name: coturn 68 | env_file: 69 | - ./coturn/coturn.env 70 | build: 71 | context: ./coturn/ 72 | dockerfile: ./Dockerfile 73 | ports: 74 | - "3478:3478/udp" 75 | - "3478:3478/tcp" 76 | volumes: 77 | - ./coturn/turnserver-with-secret-user.conf:/etc/turnserver.conf -------------------------------------------------------------------------------- /bin/auto-push-amd64-image-to-hub.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ######################### 3 | # 一键推送dockerhub的脚本 4 | # for arm64 5 | # @auther: iamtsm 6 | # @version: v1.2.0 7 | ######################### 8 | 9 | build_and_push_image() { 10 | local image_name=$1 11 | local tag=$2 12 | local target_name=$3 13 | local image_prefix="iamtsm/tl-rtc-file" 14 | local arch="amd64" 15 | 16 | echo "###################################### build $image_prefix-$target_name-$arch:$tag" 17 | ## build by docker-compose-build-code.yml 18 | docker-compose -f ../docker/docker-compose-build-code.yml build $image_name 19 | 20 | echo "###################################### tag $image_prefix-$target_name-$arch:$tag" 21 | docker tag docker-$image_name:$tag $image_prefix-$target_name-$arch:$tag 22 | 23 | echo "###################################### push $image_prefix-$target_name-$arch:$tag" 24 | docker push $image_prefix-$target_name-$arch:$tag 25 | 26 | echo "###################################### del $image_prefix-$target_name-$arch:$tag" 27 | ## del build version 28 | docker rmi docker-$image_name:$tag 29 | ## del tag build version 30 | docker rmi $image_prefix-$target_name-$arch:$tag 31 | } 32 | 33 | latest_version=latest 34 | 35 | if [ $# -eq 0 ]; then 36 | # 如果没有传入参数,默认执行所有镜像的打包发布逻辑 37 | echo "Please input args" 38 | else 39 | # 有传入参数时,遍历处理每个参数 40 | for image_arg in "$@"; do 41 | case $image_arg in 42 | api) 43 | build_and_push_image "api" $latest_version "api" 44 | ;; 45 | socket) 46 | build_and_push_image "socket" $latest_version "socket" 47 | ;; 48 | mysql) 49 | build_and_push_image "mysql" $latest_version "mysql" 50 | ;; 51 | coturn) 52 | build_and_push_image "coturn" $latest_version "coturn" 53 | ;; 54 | *) 55 | echo "Invalid argument: $image_arg" 56 | ;; 57 | esac 58 | done 59 | fi -------------------------------------------------------------------------------- /bin/auto-push-arm64-image-to-hub.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ######################### 3 | # 一键推送dockerhub的脚本 4 | # for arm64 5 | # @auther: iamtsm 6 | # @version: v1.2.0 7 | ######################### 8 | 9 | build_and_push_image() { 10 | local image_name=$1 11 | local tag=$2 12 | local target_name=$3 13 | local image_prefix="iamtsm/tl-rtc-file" 14 | local arch="arm64" 15 | 16 | echo "###################################### build $image_prefix-$target_name-$arch:$tag" 17 | ## build by docker-compose-build-code.yml 18 | docker-compose -f ../docker/docker-compose-build-code.yml build $image_name 19 | 20 | echo "###################################### tag $image_prefix-$target_name-$arch:$tag" 21 | docker tag docker-$image_name:$tag $image_prefix-$target_name-$arch:$tag 22 | 23 | echo "###################################### push $image_prefix-$target_name-$arch:$tag" 24 | docker push $image_prefix-$target_name-$arch:$tag 25 | 26 | echo "###################################### del $image_prefix-$target_name-$arch:$tag" 27 | ## del build version 28 | docker rmi docker-$image_name:$tag 29 | ## del tag build version 30 | docker rmi $image_prefix-$target_name-$arch:$tag 31 | } 32 | 33 | latest_version=latest 34 | 35 | if [ $# -eq 0 ]; then 36 | # 如果没有传入参数,默认执行所有镜像的打包发布逻辑 37 | echo "Please input args" 38 | else 39 | # 有传入参数时,遍历处理每个参数 40 | for image_arg in "$@"; do 41 | case $image_arg in 42 | api) 43 | build_and_push_image "api" $latest_version "api" 44 | ;; 45 | socket) 46 | build_and_push_image "socket" $latest_version "socket" 47 | ;; 48 | mysql) 49 | build_and_push_image "mysql" $latest_version "mysql" 50 | ;; 51 | coturn) 52 | build_and_push_image "coturn" $latest_version "coturn" 53 | ;; 54 | *) 55 | echo "Invalid argument: $image_arg" 56 | ;; 57 | esac 58 | done 59 | fi -------------------------------------------------------------------------------- /client/packages/rtc-web/src/assets/svg-icon/hang-up.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /doc/gitbook/CONF_ALL.md: -------------------------------------------------------------------------------- 1 | # 配置统计汇总 2 | 3 | - `tlrtcfile.env` [ 主要的项目配置文件 ] 4 | - `docker-compose.yml` [ 使用官方镜像启动的docker-compose文件 ] 5 | - `docker-compose-with-all-env.yml` [ 使用官方镜像启动的,内置了所有tlrtcfile.env配置的 docker-compose文件 ] 6 | - `docker/docker-compose-build-code.yml` [ 可以自己编译打包镜像的docker-compose文件 ] 7 | - `docker/mysql/mysql.env` [ mysql的外部挂载配置文件 ] 8 | - `docker/coturn/coturn.env` [ coturn的外部挂载配置文件 ] 9 | - `docker/coturn/turnserver-with-fixed-user.conf` [ coturn的项目默认内置的固定帐号模式的配置文件 ] 10 | - `docker/coturn/turnserver-with-secret-user.conf` [ coturn的项目默认内置的有时效性帐号模式的配置文件 ] 11 | 12 | 配置不算多,以上是算上所有服务后 (`api服务`, `socket服务`, `mysql服务`, `coturn服务`)的配置汇总 13 | 14 | 进行分类到不同服务后,每个服务可能需要修改的配置划分如下 15 | 16 | ### 部署api服务相关配置 17 | 18 | 可能需要修改 19 | 20 | - `tlrtcfile.env` 21 | - `docker-compose.yml` 22 | - `docker-compose-with-all-env.yml` 23 | - `docker/docker-compose-build-code.yml` 24 | - `docker/mysql/mysql.env` 25 | - `docker/coturn/coturn.env` 26 | - `docker/coturn/turnserver-with-fixed-user.conf` 27 | - `docker/coturn/turnserver-with-secret-user.conf` 28 | 29 | ### 部署socket服务相关配置 30 | 31 | 可能需要修改 32 | 33 | - `tlrtcfile.env` 34 | - `docker-compose.yml` 35 | - `docker-compose-with-all-env.yml` 36 | - `docker/docker-compose-build-code.yml` 37 | - `docker/mysql/mysql.env` 38 | - `docker/coturn/coturn.env` 39 | - `docker/coturn/turnserver-with-fixed-user.conf` 40 | - `docker/coturn/turnserver-with-secret-user.conf` 41 | 42 | ### 部署mysql服务相关配置 43 | 44 | 可能需要修改 45 | 46 | - `tlrtcfile.env` 47 | - `docker-compose.yml` 48 | - `docker-compose-with-all-env.yml` 49 | - `docker/docker-compose-build-code.yml` 50 | - `docker/mysql/mysql.env` 51 | 52 | ### 部署coturn服务相关配置 53 | 54 | 可能需要修改 55 | 56 | - `tlrtcfile.env` 57 | - `docker-compose.yml` 58 | - `docker-compose-with-all-env.yml` 59 | - `docker/docker-compose-build-code.yml` 60 | - `docker/coturn/coturn.env` 61 | - `docker/coturn/turnserver-with-fixed-user.conf` 62 | - `docker/coturn/turnserver-with-secret-user.conf` 63 | 64 | 我知道,看到这么多配置,有些小伙伴已经开始急了,但是先别急,不是每一个都需要改的,而是选择不同的部署模式,部署不同的服务,只是改对应的配置就好。不是全部要改,后面的文档,我将对不同部署模式需要修改的配置进行说明。 65 | -------------------------------------------------------------------------------- /svr/src/bussiness/check/core.js: -------------------------------------------------------------------------------- 1 | class TrieNode { 2 | constructor() { 3 | this.children = new Map(); 4 | this.isEndOfWord = false; 5 | } 6 | } 7 | 8 | class Trie { 9 | constructor() { 10 | this.root = new TrieNode(); 11 | } 12 | 13 | insert(word) { 14 | let node = this.root; 15 | for (const char of word) { 16 | if (!node.children.has(char)) { 17 | node.children.set(char, new TrieNode()); 18 | } 19 | node = node.children.get(char); 20 | } 21 | node.isEndOfWord = true; 22 | } 23 | 24 | search(word) { 25 | let node = this.root; 26 | let match = ''; 27 | for (const char of word) { 28 | if (node.children.has(char)) { 29 | match += char; 30 | node = node.children.get(char); 31 | if (node.isEndOfWord) { 32 | return match; 33 | } 34 | } else { 35 | break; 36 | } 37 | } 38 | return null; 39 | } 40 | 41 | toJson() { 42 | //将树转换为json 43 | return JSON.stringify(this.root, (key, value) => { 44 | if (key === 'children') { 45 | return [...value]; 46 | } 47 | return value; 48 | }); 49 | } 50 | } 51 | 52 | // 敏感词库 53 | let sensitiveWords = require("./words") 54 | 55 | // 将敏感词库转换为前缀树 56 | let trie = null; 57 | (function buildTrie(){ 58 | if(!trie){ 59 | trie = new Trie(); 60 | for (const word of sensitiveWords) { 61 | trie.insert(word); 62 | } 63 | let mem = process.memoryUsage(); 64 | let format = function (bytes) { 65 | return (bytes / 1024 / 1024).toFixed(4) + 'MB'; 66 | }; 67 | 68 | console.log({ 69 | heapTotal : format(mem.heapTotal), 70 | heapUsed : format(mem.heapUsed), 71 | rss : format(mem.rss) 72 | }); 73 | } 74 | })() 75 | 76 | 77 | module.exports = { 78 | trie 79 | } -------------------------------------------------------------------------------- /client/packages/rtc-web/src/components/menu-action.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | 76 | -------------------------------------------------------------------------------- /svr/src/dao/user/user.js: -------------------------------------------------------------------------------- 1 | const {inject_env_config} = require("../../../conf/env_config"); 2 | const cfg = inject_env_config(require("../../../conf/cfg.json")); 3 | const sequelizeObj = require('sequelize'); 4 | const utils = require("../../utils/utils"); 5 | const dbOpen = cfg.db.open; 6 | 7 | /** 8 | * 添加微信用户记录 9 | * @param {*} params 10 | * @param {*} tables 11 | * @param {*} dbClient 12 | * @returns 13 | */ 14 | async function addWxUser(params, tables, dbClient) { 15 | try{ 16 | if(!tables || !dbClient){ 17 | return {}; 18 | } 19 | 20 | if(!params){ 21 | params = {}; 22 | } 23 | 24 | let users = await tables.User.findAll({ 25 | where: { 26 | type: 'wx', 27 | openid: params.openid, 28 | } 29 | }); 30 | 31 | if(users && users.length === 0){ 32 | let data = await tables.User.create({ 33 | type: 'wx', 34 | openid: params.openid, 35 | avatar: params.avatar, 36 | uname : params.uname, 37 | pwd : params.pwd, 38 | solt : params.solt, 39 | role: params.role, 40 | }); 41 | 42 | return data && data.dataValues ? dataValues : { id : 0 }; 43 | } 44 | 45 | if(users && users.length === 1){ 46 | return users[0].dataValues; 47 | } 48 | 49 | return { id : 0 }; 50 | }catch(e){ 51 | console.error(e); 52 | return {}; 53 | } 54 | } 55 | 56 | 57 | /** 58 | * 更新用户标识 59 | * @param {*} params 60 | * @param {*} tables 61 | * @param {*} dbClient 62 | */ 63 | async function updateUserFlag(params, tables, dbClient){ 64 | if(!tables || !dbClient){ 65 | return {}; 66 | } 67 | 68 | if(!params){ 69 | params = {}; 70 | } 71 | 72 | if(!params.id){ 73 | return {}; 74 | } 75 | 76 | let data = await tables.User.update({ 77 | flag: params.flag, 78 | }, { 79 | where: { 80 | id: params.id 81 | } 82 | }); 83 | 84 | utils.tlConsole("更新用户标识 : ", params, data) 85 | 86 | return data; 87 | } 88 | 89 | 90 | module.exports = dbOpen ? { 91 | addWxUser, updateUserFlag 92 | } : { 93 | addWxUser : function(){ 94 | return {} 95 | }, 96 | updateUserFlag : function(){ 97 | return {} 98 | } 99 | } -------------------------------------------------------------------------------- /svr/src/tables/db.js: -------------------------------------------------------------------------------- 1 | const sequelizeObj = require('sequelize'); 2 | const fs = require('fs'); 3 | const utils = require("../../src/utils/utils"); 4 | //db connect retry times 5 | let connectRetryTimes = 0; 6 | 7 | async function excute(config) { 8 | let dbConf = config.db.mysql; 9 | 10 | let dbClient = new sequelizeObj( 11 | dbConf.dbName, 12 | dbConf.user, 13 | dbConf.password, 14 | { 15 | "dialect": "mysql", 16 | "host": dbConf.host, 17 | "port": dbConf.port, 18 | "logging": false, 19 | "pool": { 20 | "max": 5, 21 | "min": 0, 22 | "acquire": 30000, 23 | "idle": 10000 24 | }, 25 | "timezone": "+08:00", 26 | "define": { 27 | "freezeTableName": true, 28 | "underscored": true, 29 | "charset": "utf8", 30 | "collate": "utf8_general_ci", 31 | "timestamps": false, 32 | "paranoid": true 33 | } 34 | } 35 | ); 36 | 37 | let tables = {} 38 | 39 | async function connectDb(){ 40 | try { 41 | let connect = await dbClient.authenticate(); 42 | utils.tlConsole('db connect ok ... '); 43 | 44 | let files = fs.readdirSync(__dirname); 45 | for (let f of files) { 46 | if (f[0] == '.' || f == 'db.js') continue; 47 | try { 48 | let fn = require('./' + f); 49 | if (typeof fn == 'function') { 50 | let ms = fn(dbClient, sequelizeObj); 51 | for (let k in ms) { 52 | tables[k] = ms[k]; 53 | } 54 | } 55 | } catch (e) { 56 | utils.tlConsole(e); 57 | } 58 | } 59 | 60 | try { 61 | await dbClient.sync({ force: false }); 62 | 63 | utils.tlConsole("db sync ok ..."); 64 | } catch (e) { 65 | utils.tlConsole("db sync err : ",e); 66 | } 67 | } catch (e) { 68 | if(connectRetryTimes++ < 8){ 69 | utils.tlConsole('db connect err, retrying ... ',e.message); 70 | await new Promise(resolve => setTimeout(resolve, connectRetryTimes * 3000)); 71 | await connectDb(); 72 | return; 73 | } 74 | utils.tlConsole('db connect err ',e); 75 | } 76 | } 77 | 78 | await connectDb(); 79 | 80 | return { 81 | tables, 82 | dbClient, 83 | }; 84 | } 85 | 86 | 87 | module.exports = { 88 | excute 89 | } 90 | -------------------------------------------------------------------------------- /doc/gitbook/install/INSTALL_BY_COMMAND_SHELL.md: -------------------------------------------------------------------------------- 1 | # 通过命令一键脚本部署 2 | 3 | 由于每个人的机器/环境都是有细微区别的,但是脚本能处理的情况有限,所以选择这种模式,有一定几率不能正常运行。但是可以遇到具体情况具体分析,或者可以加群反馈问题或者建议, QQ群 : 624214498 4 | 5 | 目前支持 `ubuntu16`, `ubuntu18`, `ubuntu20`, `windows`, `centeros`, `macos` 这几种自动脚本。 6 | 7 | ### ubuntu16/18/20/macos 8 | 9 | 脚本放在 bin/ubuntu相关目录下,进入 bin/ubuntu对应的目录,有五个脚本,分别作用如下: 10 | 11 | - `auto-check-install-http.sh` 自动检测环境 + 安装环境 + 检测端口占用 + 调用 **`auto-start-http.sh`** 服务脚本 12 | - `auto-check-install-https.sh` 自动检测环境 + 安装环境 + 检测端口占用 + 调用 **`auto-start-https.sh`** 服务脚本 13 | - `auto-start-http.sh` pm2后台启动 **http** 服务脚本 14 | - `auto-start-https.sh` pm2后台启动 **https** 服务脚本 15 | - `auto-stop.sh` pm2删除服务进程 16 | 17 | 这里相应的 **http** 和 **https** 脚本选一种执行就好,下面的文档以 **http** 模式进行说明,如果需要以 **https** 脚本操作,**http** 替换为 **https** 即可 18 | 19 | #### 初次安装启动 20 | 21 | 使用 `auto-check-install-http.sh` 脚本 22 | 23 | ``` 24 | ./auto-check-install-http.sh 25 | ``` 26 | 27 | 如果没有执行权限,先给脚本添加权限 `chmod +x ./auto-check-install-http.sh` 28 | 29 | ![初次安装启动](https://qnproxy.iamtsm.cn/image-13.png "初次安装启动") 30 | 31 | #### 停止服务 32 | 33 | 使用 `auto-stop.sh` 脚本 34 | 35 | ``` 36 | ./auto-stop.sh 37 | ``` 38 | 39 | 如果没有执行权限,先给脚本添加权限 `chmod +x ./auto-stop.sh` 40 | 41 | ![停止服务](https://qnproxy.iamtsm.cn/image-14.png "停止服务") 42 | 43 | #### 非初次安装启动 44 | 45 | 使用 `auto-start-http.sh` 脚本, 也可以沿用之前的 `auto-check-install-http.sh` 脚本 46 | 47 | ``` 48 | ./auto-check-install-http.sh 49 | ``` 50 | 51 | ### windows 52 | 53 | - `auto-check-install-http.bat` 自动检测环境 + 安装环境 + 检测端口占用 + 调用 **`auto-start-http.bat`** 服务脚本 54 | - `auto-check-install-https.bat` 自动检测环境 + 安装环境 + 检测端口占用 + 调用 **`auto-start-https.bat`** 服务脚本 55 | - `auto-start-http.bat` pm2后台启动 **http** 服务脚本 56 | - `auto-start-https.bat` pm2后台启动 **https** 服务脚本 57 | 58 | 具体操作如ubuntu所示例,脚本内容如有问题,请反馈 59 | 60 | ### centeros 61 | 62 | - `auto-check-install-http.sh` 自动检测环境 + 安装环境 + 检测端口占用 + 调用 **`auto-start-http.sh`** 服务脚本 63 | - `auto-check-install-https.sh` 自动检测环境 + 安装环境 + 检测端口占用 + 调用 **`auto-start-https.sh`** 服务脚本 64 | - `auto-start-http.sh` pm2后台启动 **http** 服务脚本 65 | - `auto-start-https.sh` pm2后台启动 **https** 服务脚本 66 | - `auto-stop.sh` pm2删除服务进程 67 | 68 | 具体操作如ubuntu所示例,脚本内容如有问题,请反馈 -------------------------------------------------------------------------------- /svr/conf/env_config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const dotEnv = require("dotenv"); 3 | 4 | /** 5 | * 从.env文件中加载环境变量 6 | * docker环境下,使用docker-compose.yml中指定的env逻辑,不使用主动加载的.env文件中的环境变量 7 | * docker环境下,使用docker run 中指定的 -e 逻辑,不使用主动加载的.env文件中的环境变量 8 | * 非docker环境下,使用.env文件中的环境变量 9 | */ 10 | const load_env_config = function(){ 11 | if(process.env.tl_rtc_file_node_load_env === 'false'){ 12 | return 13 | } 14 | const pathsEnv = path.resolve(__dirname, "../../") 15 | dotEnv.config({ path: `${pathsEnv}/tlrtcfile.env` }) 16 | console.log(`load env config from .env file ${pathsEnv}/tlrtcfile.env`) 17 | } 18 | 19 | /** 20 | * 从环境变量中注入配置 21 | * 1. 适配容器环境 22 | * 2. 数据格式转换 23 | * 3. 统一管理 24 | * defaultConfJson : conf/cfg.json 25 | * @param {object} defaultConfJson 配置对象 26 | */ 27 | const inject_env_config = function (defaultConfJson) { 28 | Object.keys(process.env).filter( key => key.startsWith("tl_rtc_file_") ).map(key => { 29 | let value = process.env[key] 30 | key = key.replace("tl_rtc_file_","") 31 | 32 | //端口相关的配置转换为数字 33 | if (key.endsWith('_port')) { 34 | value = parseInt(value) 35 | } 36 | 37 | //过期时间相关的配置转换为数字 38 | if(key.endsWith("_expire")){ 39 | value = parseInt(value) 40 | } 41 | 42 | //开关相关的配置转换为boolean 43 | if (key.endsWith('_open')) { 44 | value = value === 'true' 45 | } 46 | 47 | //openai keys转换为数组 48 | if(key === 'openai_keys'){ 49 | value = value.split(',') 50 | } 51 | 52 | //企业微信通知 keys转换为数组 53 | if(key === 'notify_qiwei_normal' || key === 'notify_qiwei_error'){ 54 | value = value.split(',') 55 | } 56 | 57 | let curr = defaultConfJson; 58 | const paths = key.split('_'); 59 | const last = paths.pop() 60 | for (const path of paths) { 61 | curr = curr[path] 62 | } 63 | if (curr) { 64 | // console.log(`config inject ${paths.join('.')}.${last} to ${value}`) 65 | curr[last] = value 66 | } 67 | }) 68 | return defaultConfJson 69 | } 70 | 71 | module.exports = { 72 | inject_env_config, 73 | load_env_config 74 | } 75 | -------------------------------------------------------------------------------- /client/packages/rtc-web/src/components/chat-room/chat-room-user.vue: -------------------------------------------------------------------------------- 1 | 55 | 56 | 87 | -------------------------------------------------------------------------------- /svr/tlapi.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const bodyParser = require('body-parser'); 3 | const cookieParser = require('cookie-parser'); 4 | const { inject_env_config, load_env_config } = require("./conf/env_config"); 5 | //加载环境变量 6 | load_env_config(); 7 | //加载环境变量完毕后,注入配置 8 | const conf = inject_env_config(require("./conf/cfg.json")); 9 | const fileApiRouters = require("./src/controller/router")(); 10 | const db = require("./src/tables/db"); //db 11 | const utils = require("./src/utils/utils"); 12 | const fs = require('fs'); 13 | const https = require('https'); 14 | const resRouter = conf.api.router.res; 15 | 16 | //打印logo 17 | utils.tlConsoleIcon() 18 | 19 | async function start() { 20 | let app = express(); 21 | 22 | app.use(bodyParser.json()); 23 | app.use(cookieParser()); 24 | 25 | utils.tlConsole("api init start ...") 26 | 27 | if (!conf.db.open) {// 没开db 28 | app.use(async function (req, res, next) { 29 | req.ctx = {}; 30 | req.ctx.tables = {}; 31 | req.ctx.dbClient = {}; 32 | await next(); 33 | }) 34 | utils.tlConsole("db not open ...") 35 | }else{ 36 | let { tables, dbClient } = await db.excute(conf) 37 | app.use(async function (req, res, next) { 38 | req.ctx = {}; 39 | req.ctx.tables = tables; 40 | req.ctx.dbClient = dbClient; 41 | await next(); 42 | }) 43 | utils.tlConsole("db init done ...") 44 | } 45 | 46 | //file api 47 | for (let key in fileApiRouters) app.use(key, fileApiRouters[key]) 48 | 49 | //res api v1 50 | for (let key in resRouter) app.use(key, express.static(resRouter[key])); 51 | 52 | //start server 53 | if(process.env.tl_rtc_file_env_mode === 'http'){ 54 | app.listen(conf.api.port); 55 | }else { 56 | let options = { 57 | key: fs.readFileSync('./conf/keys/server.key'), 58 | cert: fs.readFileSync('./conf/keys/server.crt') 59 | } 60 | https.createServer(options,app).listen(conf.api.port); 61 | } 62 | 63 | utils.tlConsole("express init done ...") 64 | utils.tlConsole("api ",process.env.tl_rtc_file_env_mode," server runing on ", conf.api.port, " successful") 65 | } 66 | 67 | 68 | start(); -------------------------------------------------------------------------------- /client/packages/rtc-web/src/components/base/modal.vue: -------------------------------------------------------------------------------- 1 | 62 | 63 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /client/packages/rtc-web/src/utils/common.ts: -------------------------------------------------------------------------------- 1 | import { CommonFnType } from '@/types'; 2 | 3 | export function withBtnClickEvent(fn: CommonFnType) { 4 | return (e: Event) => { 5 | e.preventDefault(); 6 | fn(); 7 | }; 8 | } 9 | 10 | export function preventDefault(e: Event) { 11 | e.preventDefault(); 12 | } 13 | 14 | export function safenExecuteConditioFn(condition: boolean, fn: CommonFnType) { 15 | if (condition) { 16 | fn(); 17 | } 18 | } 19 | 20 | // 将 socket on 转换为 promisify 21 | export function transformSocketListenEvent(socket: any, ev: string) { 22 | return new Promise((resolve) => { 23 | const cb = (...args: any[]) => { 24 | resolve(args); 25 | }; 26 | socket.on(ev, cb); 27 | }); 28 | } 29 | 30 | // 转义字符串 31 | export function escapeStr(str: string) { 32 | const entityMap: any = { 33 | '&': '&', 34 | '<': '<', 35 | '>': '>', 36 | '"': '"', 37 | "'": ''', 38 | '/': '/', 39 | '`': '`', 40 | '=': '=', 41 | }; 42 | 43 | const encodedMap: any = { 44 | '%': '%25', 45 | '!': '%21', 46 | "'": '%27', 47 | '(': '%28', 48 | ')': '%29', 49 | '*': '%2A', 50 | '-': '%2D', 51 | '.': '%2E', 52 | _: '%5F', 53 | '~': '%7E', 54 | }; 55 | 56 | return String(str).replace(/[&<>"'`=/%!'()*\-._~]/g, function (s) { 57 | return entityMap[s] || encodedMap[s] || ''; 58 | }); 59 | } 60 | 61 | export function unescapeStr(str: string) { 62 | const entityMap: any = { 63 | '&': '&', 64 | '<': '<', 65 | '>': '>', 66 | '"': '"', 67 | ''': "'", 68 | '/': '/', 69 | '`': '`', 70 | '=': '=', 71 | }; 72 | const encodedMap: any = { 73 | '%25': '%', 74 | '%21': '!', 75 | '%27': "'", 76 | '%28': '(', 77 | '%29': ')', 78 | '%2A': '*', 79 | '%2D': '-', 80 | '%2E': '.', 81 | '%5F': '_', 82 | '%7E': '~', 83 | }; 84 | return String(str).replace( 85 | /&(amp|lt|gt|quot|#39|#x2F|#x60|#x3D);|%(25|21|27|28|29|2A|2D|2E|5F|7E)/g, 86 | function (s) { 87 | return entityMap[s] || encodedMap[s] || ''; 88 | } 89 | ); 90 | } 91 | 92 | export const isItBetween = (num: number, arr: number[]) => { 93 | return num >= arr[0] && num <= arr[1]; 94 | }; 95 | 96 | export const resetUrl = () => window.location.replace('/'); 97 | -------------------------------------------------------------------------------- /client/packages/rtc-web/src/components/lib.tsx: -------------------------------------------------------------------------------- 1 | import { SetupContext } from 'vue'; 2 | import { SvgIcon, NavIcons } from './base'; 3 | import { MenuSide } from './menu'; 4 | import { resetUrl } from '@/utils'; 5 | 6 | export const NavHeader = ( 7 | { showSiderbar }: Partial<{ showSiderbar: boolean }>, 8 | ctx: SetupContext 9 | ) => { 10 | return ( 11 |
12 | 13 |
14 | {/* Navbar */} 15 | 38 |
39 | 40 | {showSiderbar ? ( 41 |
42 | 47 | 48 |
49 | ) : undefined} 50 |
51 | ); 52 | }; 53 | 54 | export type FullHeightFlexBoxProps = Partial<{ 55 | dire: 'col' | 'row'; 56 | type: 'full' | 'screen'; 57 | }>; 58 | 59 | export const FullHeightFlexBox = ( 60 | props: FullHeightFlexBoxProps, 61 | ctx: SetupContext 62 | ) => { 63 | const { dire = 'row', type = 'screen' } = props; 64 | 65 | const height: Record<'full' | 'screen', string> = { 66 | full: 'h-full', 67 | screen: 'h-screen', 68 | }; 69 | 70 | return ( 71 |
72 | {ctx.slots.default?.()} 73 |
74 | ); 75 | }; 76 | -------------------------------------------------------------------------------- /client/packages/rtc-web/src/components/menu/menu-list.vue: -------------------------------------------------------------------------------- 1 | 56 | 57 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /svr/web-res/disclaimer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | tl-rtc-file 6 | 7 | 8 | 9 | 10 | 33 | 34 | 35 |

tl-rtc-file开源项目/演示网站服务条款

36 |

免责声明

37 |

1.1 如使用本演示网站/开源项目,即表示您已同意本条款。我们 (开源项目维护团队) 始终在不断更改和改进我们的服务。我们可能会增加或删除功能,也可能暂停或彻底停止某项服务。您可以随时停止使用我们的服务,我们也可以随时对我们的服务增加新的限制。

38 |

1.2 该网站/开源项目的使用者在使用本演示网站/开源项目前,应仔细阅读以下条款并同意遵守。使用本演示网站/开源项目将被视为对本免责协议的接受和遵守。

39 | 40 |

演示网站/开源项目的性质

41 |

2.1 本演示网站/开源项目旨在帮助大家学习webrtc,提供一个学习/演示demo环境,和基础功能代码入门教学

42 |

2.2 本网站/开源项目提供的服务旨在为用户提供数据传输功能演示,包括但不限于文件传输、直播、音视频通话、远程屏幕共享、聊天等功能的演示。

43 |

2.3 使用者应理解,本演示网站/开源项目仅提供技术支持演示,不对数据传输的准确性、及时性、完整性、安全性或合法性负责。使用者应自行承担使用本演示网站/开源项目的风险。

44 | 45 |

使用者责任

46 |

3.1 使用者应承担使用本演示网站/开源项目的全部责任和风险。对于使用者通过本演示网站/开源项目进行的任何操作、行为或后果,本网站/开源项目不承担任何责任。

47 |

3.2 使用者不得利用本演示网站/开源项目进行任何违法、侵权、威胁、诽谤、骚扰、破坏性或其他不当行为。若使用者违反上述规定,本网站/开源项目有权立即终止或限制使用者的访问权限。

48 |

3.3 本网站使用者严禁上传违法违规内容,包括但不限于:色情低俗、政治敏感、、暴力、恶意软件、诈骗、贩卖公民隐私,上传上述内容将永久封禁账号或 IP 地址,并可能承担承担刑事责任。

49 | 50 |

免责条款

51 |

4.1 本网站/开源项目不对任何由于使用本演示网站/开源项目而引起的任何直接、间接、附带、特殊、惩罚性或后续的损害承担责任,包括但不限于利润损失、商业中断、计算机系统故障、数据丢失或其他经济损失。

52 |

4.2 本网站/开源项目不对任何第三方通过本演示网站/开源项目发布、提供或传输的任何内容的准确性、合法性、安全性或可靠性负责。

53 | 54 |

免责声明的范围

55 |

本免责协议适用于使用本网站/开源项目的所有用户,无论其身份或目的。该免责协议的内容适用于因使用本演示网站/开源项目而产生的一切事项,包括但不限于使用的内容、服务或信息所引起的任何纠纷、损失或损害。

56 | 57 | 58 | -------------------------------------------------------------------------------- /svr/src/bussiness/cache/scan/scanCache.js: -------------------------------------------------------------------------------- 1 | const { StateKey, InfoKey, CookieKey } = require("../key"); 2 | const cache = require("../cache"); 3 | 4 | /** 5 | * 设置扫码状态 : 五分钟 6 | * @param {*} key 7 | * @param {*} value 8 | */ 9 | const setScanState = (key, value) => { 10 | cache.setShortTimeCache(StateKey.SCAN_STATE_KEY + key, value); 11 | } 12 | 13 | /** 14 | * 获取扫码状态 : 五分钟 15 | * @param {*} key 16 | * @returns 17 | * 'scan': 已扫码 18 | * 'auth_succ': 授权成功 19 | * 'auth_fail': 授权失败 20 | * '' 21 | * */ 22 | const getScanState = (key) => { 23 | return cache.getShortTimeCache(StateKey.SCAN_STATE_KEY + key) || ""; 24 | } 25 | 26 | /** 27 | * 设置登录状态 : 一小时 28 | * @param {*} key 29 | * @param {*} value 30 | * @returns 31 | * */ 32 | const setTokenState = (key, value) => { 33 | cache.setLongTimeCache(StateKey.TOKEN_STATE_KEY + key, value); 34 | } 35 | 36 | /** 37 | * 获取登录状态 : 一小时 38 | * @param {*} key 39 | * @returns 40 | * { 41 | * token : token 42 | * } 43 | * */ 44 | const getTokenState = (key) => { 45 | return cache.getLongTimeCache(StateKey.TOKEN_STATE_KEY + key) || ""; 46 | } 47 | 48 | 49 | /** 50 | * 获取登录信息 : 一小时 51 | * @param {*} key 52 | * @returns 53 | * { 54 | * openId : openId, 55 | * loginTime: loginTime 56 | * } 57 | * */ 58 | const getLoginInfo = (key) => { 59 | return cache.getLongTimeCache(InfoKey.LOGIN_INFO_KEY + key) || {}; 60 | } 61 | 62 | 63 | /** 64 | * 设置登录信息 : 一小时 65 | * @param {*} key 66 | * @param {*} value 67 | * @returns 68 | * */ 69 | const setLoginInfo = (key, value) => { 70 | cache.setLongTimeCache(InfoKey.LOGIN_INFO_KEY + key, value); 71 | } 72 | 73 | 74 | /** 75 | * 获取用户信息 : 一天 76 | * @param {*} key 77 | * @returns 78 | * { 79 | * id: id, 80 | * openid: openid, 81 | * avatar: userInfo.avatarUrl, 82 | * uname: userInfo.nickName, 83 | * ... 84 | * } 85 | */ 86 | const getUserInfo = (key) => { 87 | return cache.getDayTimeCache(InfoKey.USER_INFO_KEY + key) || {}; 88 | } 89 | 90 | /** 91 | * 设置用户信息 : 一天 92 | * @param {*} key 93 | * @param {*} value 94 | */ 95 | const setUserInfo = (key, value) => { 96 | cache.setDayTimeCache(InfoKey.USER_INFO_KEY + key, value); 97 | } 98 | 99 | 100 | module.exports = { 101 | setScanState, getScanState, 102 | setTokenState, getTokenState, 103 | setLoginInfo, getLoginInfo, 104 | setUserInfo, getUserInfo 105 | } -------------------------------------------------------------------------------- /svr/conf/cfg.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "10.5.2", 3 | "socket": { 4 | "port": "请到 tlrtcfile.env 中进行配置", 5 | "host": "请到 tlrtcfile.env 中进行配置" 6 | }, 7 | "api": { 8 | "port": "请到 tlrtcfile.env 中进行配置", 9 | "router": { 10 | "filter": { 11 | "whiteDir": [], 12 | "whiteFile": [ 13 | "router.js" 14 | ] 15 | }, 16 | "res" : { 17 | "/": "web-res/dist/" 18 | } 19 | } 20 | }, 21 | "login": { 22 | "appId" : "请到 tlrtcfile.env 中进行配置", 23 | "appSecret" : "请到 tlrtcfile.env 中进行配置", 24 | "token" : { 25 | "url" : "请到 tlrtcfile.env 中进行配置", 26 | "key" : "请到 tlrtcfile.env 中进行配置" 27 | } 28 | }, 29 | "manage": { 30 | "room": "请到 tlrtcfile.env 中进行配置", 31 | "password": "请到 tlrtcfile.env 中进行配置" 32 | }, 33 | "webrtc": { 34 | "turn": { 35 | "host": "请到 tlrtcfile.env 中进行配置", 36 | "secret": "请到 tlrtcfile.env 中进行配置", 37 | "username": "请到 tlrtcfile.env 中进行配置", 38 | "credential": "请到 tlrtcfile.env 中进行配置", 39 | "expire" : "请到 tlrtcfile.env 中进行配置" 40 | }, 41 | "stun": { 42 | "host": "请到 tlrtcfile.env 中进行配置" 43 | }, 44 | "iceTransportPolicy": "all", 45 | "options": { 46 | "offerToReceiveAudio": 1, 47 | "offerToReceiveVideo": 1 48 | } 49 | }, 50 | "openai": { 51 | "keys": [ "请到 tlrtcfile.env 中进行配置" ] 52 | }, 53 | "oss": { 54 | "seafile": { 55 | "repoid": "请到 tlrtcfile.env 中进行配置", 56 | "host": "请到 tlrtcfile.env 中进行配置", 57 | "username": "请到 tlrtcfile.env 中进行配置", 58 | "password": "请到 tlrtcfile.env 中进行配置" 59 | }, 60 | "alyun": { 61 | "AccessKey": "请到 tlrtcfile.env 中进行配置", 62 | "SecretKey": "请到 tlrtcfile.env 中进行配置", 63 | "bucket": "请到 tlrtcfile.env 中进行配置" 64 | }, 65 | "txyun": { 66 | "AccessKey": "请到 tlrtcfile.env 中进行配置", 67 | "SecretKey": "请到 tlrtcfile.env 中进行配置", 68 | "bucket": "请到 tlrtcfile.env 中进行配置" 69 | }, 70 | "qiniuyun": { 71 | "AccessKey": "请到 tlrtcfile.env 中进行配置", 72 | "SecretKey": "请到 tlrtcfile.env 中进行配置", 73 | "bucket": "请到 tlrtcfile.env 中进行配置" 74 | } 75 | }, 76 | "notify": { 77 | "open": "请到 tlrtcfile.env 中进行配置", 78 | "qiwei": { 79 | "normal": [ "请到 tlrtcfile.env 中进行配置" ], 80 | "error": [ "请到 tlrtcfile.env 中进行配置" ] 81 | } 82 | }, 83 | "db": { 84 | "open": "请到 tlrtcfile.env 中进行配置", 85 | "mysql": { 86 | "host": "请到 tlrtcfile.env 中进行配置", 87 | "port": "请到 tlrtcfile.env 中进行配置", 88 | "dbName": "请到 tlrtcfile.env 中进行配置", 89 | "user": "请到 tlrtcfile.env 中进行配置", 90 | "password": "请到 tlrtcfile.env 中进行配置" 91 | } 92 | } 93 | } -------------------------------------------------------------------------------- /svr/web-res/pay.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | tl-rtc-file 6 | 7 | 8 | 9 | 10 | 42 | 43 | 44 |

开源项目定制服务收费标准 (v1.0.3.2023)

45 | 46 |

联系方式:

47 |

QQ : 1905333456

48 |

GitHub:https://github.com/iamtsm

49 | 50 |

项目定制服务

51 | 52 |

1. 定制开发支持

53 |
    54 |
  • 服务内容:提供定制开发技术支持,可协助部署安装相关事项,或协助自行开发,细节问题答疑
  • 55 |
  • 免费:非常乐意为各位使用的小伙伴提供帮助,大家觉得好用的同时可以点击start支持下
  • 56 |
57 | 58 |

2. 项目功能定制

59 |
    60 |
  • 服务内容:对项目进行功能定制或扩展
  • 61 |
  • 价格:按功能大小,紧急程度,耗时,定制内容是否允许开源,等情况收费
  • 62 |
63 | 64 |

收费模式

65 |
    66 |
  • 按小时计费:根据实际工作时间计算费用,目前个人定制功能2小时起,每小时200~300
  • 67 |
  • 阶段性付费:确认开发前,预付1/3,开发完成付1/3,交付上线1/3
  • 68 |
69 | 70 |

付款方式

71 |
    72 |
  • 微信支付
  • 73 |
  • 赞赏码支付
  • 74 |
  • 支付宝支付
  • 75 |
76 | 77 |

交付时间

78 |

根据项目的复杂性和工作量,具体的交付时间将在项目定制阶段与客户协商确定,一般会在预估的时间内,如有延期会提前告知。

79 | 80 |

其他备注

81 |

此收费标准仅限开发功能,以及交付上线收取的费用,不包括后期维护以及后续功能迭代。

82 | 83 | 84 | -------------------------------------------------------------------------------- /svr/src/socket/rtcExit/exit.js: -------------------------------------------------------------------------------- 1 | const daoRoom = require("./../../dao/room/room") 2 | const bussinessNotify = require("./../../bussiness/notify/notifyHandler") 3 | const rtcCount = require("./../rtcCount/count"); 4 | const utils = require("./../../utils/utils"); 5 | const rtcConstant = require("../rtcConstant"); 6 | const rtcClientEvent = rtcConstant.rtcClientEvent; 7 | const rtcLocalNetRoom = require("../rtcLocalNetRoom/localNetRoom"); 8 | 9 | /** 10 | * 退出房间 11 | * @param {*} io socketio对象 12 | * @param {*} socket 单个socket连接 13 | * @param {*} tables 数据表对象 14 | * @param {*} dbClient sequelize-orm对象 15 | * @param {*} data event参数 16 | * @returns 17 | */ 18 | async function exit(io, socket, tables, dbClient, data){ 19 | try { 20 | let room = data.room; 21 | 22 | socket.leave(room); 23 | 24 | clientsInRoom = io.sockets.adapter.rooms[room]; 25 | if (clientsInRoom) { 26 | let otherSocketIds = Object.keys(clientsInRoom.sockets); 27 | for (let i = 0; i < otherSocketIds.length; i++) { 28 | let otherSocket = io.sockets.connected[otherSocketIds[i]]; 29 | otherSocket.emit(rtcClientEvent.exit, data); 30 | } 31 | } 32 | 33 | let recoderId = data.recoderId; 34 | if (recoderId != undefined) { 35 | await daoRoom.exitRoomBySid({ socket_id: socket.id },tables, dbClient) 36 | 37 | let {handshake, userAgent, ip} = utils.getSocketClientInfo(socket); 38 | 39 | bussinessNotify.sendExitRoomNotify({ 40 | title: "退出房间", 41 | room: data.room, 42 | socketId: socket.id, 43 | recoderId: data.recoderId, 44 | userAgent: userAgent, 45 | ip: ip 46 | }) 47 | } 48 | 49 | rtcCount.count(io, socket, tables, dbClient, data); 50 | 51 | //局域网房间变动通知 52 | rtcLocalNetRoom.localNetRoomForExit(io, socket, tables, dbClient, { room }); 53 | 54 | } catch (e) { 55 | socket.emit(rtcClientEvent.tips, { 56 | room: data.room, 57 | to: socket.id, 58 | msg: "系统错误" 59 | }); 60 | bussinessNotify.sendSystemErrorMsg({ 61 | title: "socket-exit", 62 | data: JSON.stringify(data), 63 | room: data.room, 64 | from : socket.id, 65 | msg : JSON.stringify({ 66 | message: e.message, 67 | fileName: e.fileName, 68 | lineNumber: e.lineNumber, 69 | stack: e.stack, 70 | name: e.name 71 | }, null, '\t') 72 | }) 73 | } 74 | } 75 | 76 | module.exports = { 77 | exit 78 | } -------------------------------------------------------------------------------- /tlrtcfile.env: -------------------------------------------------------------------------------- 1 | #-----------------以下为基础配置-----------------# 2 | ## api服务端口 3 | tl_rtc_file_api_port=9092 4 | ## websocket服务端口 5 | tl_rtc_file_socket_port=8444 6 | ## websocket服务地址 7 | tl_rtc_file_socket_host=127.0.0.1:8444 8 | 9 | 10 | #-----------------以下为webrtc相关配置-----------------# 11 | ## webrtc-stun中继服务地址 12 | tl_rtc_file_webrtc_stun_host=stun:127.0.0.1:3478 13 | ## webrtc-turn中继服务地址 14 | tl_rtc_file_webrtc_turn_host=turn:127.0.0.1:3478?transport=udp 15 | ## webrtc中继服务用户名 16 | tl_rtc_file_webrtc_turn_username=tlrtcfile 17 | ## webrtc中继服务密码 18 | tl_rtc_file_webrtc_turn_credential=tlrtcfile 19 | ## webrtc中继服务Secret 20 | tl_rtc_file_webrtc_turn_secret=tlrtcfile 21 | ## webrtc中继服务帐号过期时间 (毫秒) 22 | tl_rtc_file_webrtc_turn_expire=86400000 23 | 24 | 25 | #-----------------以下为mysql数据库相关配置-----------------# 26 | ## 是否开启数据库 27 | tl_rtc_file_db_open=false 28 | ## 数据库地址 29 | tl_rtc_file_db_mysql_host=mysql 30 | ## 数据库端口 31 | tl_rtc_file_db_mysql_port=3306 32 | ## 数据库名称 33 | tl_rtc_file_db_mysql_dbName=webchat 34 | ## 数据库用户名 35 | tl_rtc_file_db_mysql_user=tlrtcfile 36 | ## 数据库密码 37 | tl_rtc_file_db_mysql_password=tlrtcfile 38 | 39 | #-----------------以下为oss相关配置-----------------# 40 | ## oss-seafile存储库ID 41 | tl_rtc_file_oss_seafile_repoid= 42 | ## oss-seafile地址 43 | tl_rtc_file_oss_seafile_host= 44 | ## oss-seafile用户名 45 | tl_rtc_file_oss_seafile_username= 46 | ## oss-seafile密码 47 | tl_rtc_file_oss_seafile_password= 48 | 49 | ## oss-alyun存储accessKey 50 | tl_rtc_file_oss_alyun_AccessKey= 51 | ## oss-aly存储SecretKey 52 | tl_rtc_file_oss_alyun_Secretkey= 53 | ## oss-aly存储bucket 54 | tl_rtc_file_oss_alyun_bucket= 55 | 56 | ## oss-txyun存储accessKey 57 | tl_rtc_file_oss_txyun_AccessKey= 58 | ## oss-txyunt存储SecretKey 59 | tl_rtc_file_oss_txyun_Secretkey= 60 | ## oss-txyun存储bucket 61 | tl_rtc_file_oss_txyun_bucket= 62 | 63 | ## oss-qiniuyun存储accessKey 64 | tl_rtc_file_oss_qiniuyun_AccessKey= 65 | ## oss-qiniuyunt存储SecretKey 66 | tl_rtc_file_oss_qiniuyun_Secretkey= 67 | ## oss-qiniuyun存储bucket 68 | tl_rtc_file_oss_qiniuyun_bucket= 69 | 70 | 71 | #-----------------以下为管理后台相关配置-----------------# 72 | ## 管理后台房间号 73 | tl_rtc_file_manage_room=tlrtcfile 74 | ## 管理后台密码 75 | tl_rtc_file_manage_password=tlrtcfile 76 | 77 | 78 | #-----------------以下为openai相关配置-----------------# 79 | ## openai-key,如果有多个key,逗号分隔 80 | tl_rtc_file_openai_keys= 81 | 82 | 83 | #-----------------以下为企业微信通知相关配置-----------------# 84 | ## 企业微信通知开关 85 | tl_rtc_file_notify_open=false 86 | ## 企业微信通知机器人KEY,正常通知,如果有多个key,逗号分隔 87 | tl_rtc_file_notify_qiwei_normal= 88 | ## 企业微信通知机器人KEY,错误通知,如果有多个key,逗号分隔 89 | tl_rtc_file_notify_qiwei_error= 90 | 91 | 92 | #-----------------以下为微信小程序授权登录相关配置-----------------# 93 | ## appId 94 | tl_rtc_file_login_appId= 95 | ## appSecret 96 | tl_rtc_file_login_appSecret= 97 | ## token url 98 | tl_rtc_file_login_token_url=http://127.0.0.1:9092 99 | ## token api key 100 | tl_rtc_file_login_token_key=tlrtcfile --------------------------------------------------------------------------------