Compare commits

...

54 Commits

Author SHA1 Message Date
ljw
6a5408f9b8 up gorm logger & add share to guest by web client 2024-10-09 15:53:08 +08:00
ljw
9aad62d1e4 build tag 2024-09-29 12:47:04 +08:00
ljw
867eab40f8 build default push to docker 2024-09-29 12:39:33 +08:00
ljw
eb5c7efc4c fix build 2024-09-29 12:23:34 +08:00
857abc16e7 Merge pull request #5 from gigaion/new-build-1
build.yml - Add GHCR & Dynamic Inputs
2024-09-29 12:12:17 +08:00
ljw
28b9866c42 upgrade: init by i18n
add: batch delete peer
add: batch peer to addressbook
2024-09-29 11:53:58 +08:00
Gigaion
a27deb0a41 build.yml - Add GHCR & Dynamic Inputs
build.yml - Add GHCR & Dynamic Inputs
2024-09-28 13:30:44 -07:00
ljw
8e026de20b test 2024-09-28 11:01:23 +08:00
ljw
718ecc2372 test 2024-09-28 10:12:56 +08:00
ljw
56d46722f4 test 2024-09-28 10:05:04 +08:00
ljw
b6463cd715 test 2024-09-28 09:57:32 +08:00
ljw
bd3ae0cbfe fix build docker image 2024-09-28 09:39:26 +08:00
ljw
83c3aa894f fix build docker image 2024-09-28 09:33:59 +08:00
ljw
1b88d26fea up build 2024-09-27 22:08:36 +08:00
ljw
588287fdb4 up build 2024-09-27 22:06:04 +08:00
ljw
688e544b07 up build 2024-09-27 22:03:06 +08:00
ljw
3e3f812e83 up build 2024-09-27 21:48:48 +08:00
ljw
b551c7abe4 up build 2024-09-27 21:37:12 +08:00
ljw
6d1e7a4c05 up build 2024-09-27 21:29:19 +08:00
ljw
3341a4bc8e up build docker echo manifest 2024-09-27 21:20:54 +08:00
ljw
1c84980d36 up build docker 2024-09-27 20:10:17 +08:00
ljw
833b25881d up build docker 2024-09-27 20:06:49 +08:00
ljw
9dbf58903c up build docker 2024-09-27 19:40:48 +08:00
ljw
ad007f0d91 up build docker 2024-09-27 18:49:22 +08:00
ljw
4b06973a52 up build docker 2024-09-27 18:48:09 +08:00
ljw
159a67f15d up build docker 2024-09-27 17:49:17 +08:00
ljw
7c03b9953b up build docker 2024-09-27 17:32:25 +08:00
ljw
f90987de8d up build docker 2024-09-27 17:26:40 +08:00
ljw
70e4ff7820 up build docker 2024-09-27 17:25:30 +08:00
ljw
a99356f54b up build docker 2024-09-27 17:24:25 +08:00
ljw
c5bc9534cc up build docker 2024-09-27 17:15:06 +08:00
ljw
a40733424f up build docker 2024-09-27 16:52:02 +08:00
ljw
a937efc60e up build docker 2024-09-27 16:07:06 +08:00
ljw
6adb0e8415 up build docker 2024-09-27 15:59:12 +08:00
ljw
ff9ffb2f12 up build docker 2024-09-27 15:31:43 +08:00
ljw
9be4f472ae up build docker 2024-09-27 15:23:19 +08:00
ljw
8581d74b08 up build docker 2024-09-27 15:18:45 +08:00
ljw
dafe9bd6b6 up build docker 2024-09-27 14:58:17 +08:00
ljw
3ae5772360 up build docker 2024-09-27 14:54:11 +08:00
ljw
4628dbccfb up build docker 2024-09-27 14:46:39 +08:00
ljw
572b1d4c14 up build docker 2024-09-27 14:35:43 +08:00
ljw
bdb70e9859 up build docker 2024-09-27 14:29:55 +08:00
ljw
38bda17271 up build docker 2024-09-27 14:24:17 +08:00
ljw
455e1d2e5b up build docker 2024-09-27 14:19:56 +08:00
ljw
89cd724bab up build docker 2024-09-27 14:18:36 +08:00
ljw
b9109b4d0e up build docker 2024-09-27 14:16:59 +08:00
ljw
945958f552 up build docker 2024-09-27 14:10:50 +08:00
ljw
78eb0d5c06 up release_arm64.yml 2024-09-27 10:43:48 +08:00
ljw
bc6eae711e up release_arm64.yml 2024-09-27 10:38:14 +08:00
ljw
f0a4bf6164 up docker_arm64.yml 2024-09-26 14:36:15 +08:00
ljw
fc3b5e3ac3 up docker_arm64.yml 2024-09-26 14:33:01 +08:00
ljw
f7235ac847 up docker_arm64.yml 2024-09-26 14:26:15 +08:00
ljw
231f4ddb7f up docker_arm64.yml 2024-09-26 14:24:14 +08:00
ljw
3cad3994cb add docker_arm64.yml 2024-09-26 14:18:57 +08:00
40 changed files with 1086 additions and 1260 deletions

290
.github/workflows/build.yml vendored Normal file
View File

@@ -0,0 +1,290 @@
name: Build
on:
workflow_dispatch:
inputs:
BASE_IMAGE_NAMESPACE:
description: 'Base image namespace (Default: Your Github username)'
required: false
default: ''
DOCKERHUB_IMAGE_NAMESPACE:
description: 'Docker Hub image namespace (Default: Your Github username)'
required: false
default: ''
GHCR_IMAGE_NAMESPACE:
description: 'GitHub Container Registry image namespace (Default: Your Github username)'
required: false
default: ''
SKIP_DOCKER_HUB:
description: 'Set to true to skip pushing to Docker Hub (default: false)'
required: false
default: 'false'
SKIP_GHCR:
description: 'Set to true to skip pushing to GHCR (default: false)'
required: false
default: 'false'
WEBCLIENT_SOURCE_LOCATION:
description: 'Web Client API Repository'
required: true
default: 'https://github.com/lejianwen/rustdesk-api-web'
push:
tags:
- 'v*.*.*' # 当推送带有版本号的 tag例如 v1.0.0)时触发工作流
- 'test*'
env:
LATEST_TAG: latest
WEBCLIENT_SOURCE_LOCATION: ${{ github.event.inputs.WEBCLIENT_SOURCE_LOCATION || 'https://github.com/lejianwen/rustdesk-api-web' }}
BASE_IMAGE_NAMESPACE: ${{ github.event.inputs.BASE_IMAGE_NAMESPACE || github.actor }}
DOCKERHUB_IMAGE_NAMESPACE: ${{ github.event.inputs.DOCKERHUB_IMAGE_NAMESPACE || github.actor }}
GHCR_IMAGE_NAMESPACE: ${{ github.event.inputs.GHCR_IMAGE_NAMESPACE || github.actor }}
SKIP_DOCKER_HUB: ${{ github.event.inputs.SKIP_DOCKER_HUB || 'false' }}
SKIP_GHCR: ${{ github.event.inputs.SKIP_GHCR }}
jobs:
build:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
job:
- { platform: "amd64", goos: "linux" }
- { platform: "arm64", goos: "linux" }
- { platform: "amd64", goos: "windows" }
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Go environment
uses: actions/setup-go@v4
with:
go-version: '1.22' # 选择 Go 版本
- name: Set up npm
uses: actions/setup-node@v2
with:
node-version: '20'
- name: build rustdesk-api-web
run: |
git clone ${{ env.WEBCLIENT_SOURCE_LOCATION }}
cd rustdesk-api-web
npm install
npm run build
mkdir ../resources/admin/ -p
cp -ar dist/* ../resources/admin/
- name: tidy
run: go mod tidy
- name: swag
run: |
go install github.com/swaggo/swag/cmd/swag@latest
swag init -g cmd/apimain.go --output docs/api --instanceName api --exclude http/controller/admin
swag init -g cmd/apimain.go --output docs/admin --instanceName admin --exclude http/controller/api
- name: Build for ${{ matrix.job.goos }}-${{ matrix.job.platform }}
run: |
mkdir release -p
cp -ar resources release/
cp -ar docs release/
cp -ar conf release/
mkdir -p release/data
mkdir -p release/runtime
if [ "${{ matrix.job.goos }}" = "windows" ]; then
sudo apt-get install gcc-mingw-w64-x86-64 zip -y
GOOS=${{ matrix.job.goos }} GOARCH=${{ matrix.job.platform }} CC=x86_64-w64-mingw32-gcc CGO_LDFLAGS="-static" CGO_ENABLED=1 go build -ldflags "-s -w" -o ./release/apimain.exe ./cmd/apimain.go
zip -r ${{ matrix.job.goos}}-${{ matrix.job.platform }}.zip ./release
else
if [ "${{ matrix.job.platform }}" = "arm64" ]; then
wget https://musl.cc/aarch64-linux-musl-cross.tgz
tar -xf aarch64-linux-musl-cross.tgz
export PATH=$PATH:$PWD/aarch64-linux-musl-cross/bin
GOOS=${{ matrix.job.goos }} GOARCH=${{ matrix.job.platform }} CC=aarch64-linux-musl-gcc CGO_LDFLAGS="-static" CGO_ENABLED=1 go build -ldflags "-s -w" -o ./release/apimain ./cmd/apimain.go
else
sudo apt-get install musl musl-dev musl-tools -y
GOOS=${{ matrix.job.goos }} GOARCH=${{ matrix.job.platform }} CC=musl-gcc CGO_LDFLAGS="-static" CGO_ENABLED=1 go build -ldflags "-s -w" -o ./release/apimain ./cmd/apimain.go
fi
tar -czf ${{ matrix.job.goos}}-${{ matrix.job.platform }}.tar.gz ./release
fi
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: rustdesk-api-${{ matrix.job.goos }}-${{ matrix.job.platform }}
path: |
${{ matrix.job.goos}}-${{ matrix.job.platform }}.tar.gz
${{ matrix.job.goos}}-${{ matrix.job.platform }}.zip
- name: Upload to GitHub Release
uses: softprops/action-gh-release@v2
with:
files: |
${{ matrix.job.goos}}-${{ matrix.job.platform }}.tar.gz
${{ matrix.job.goos}}-${{ matrix.job.platform }}.zip
# tag_name: ${{ env.LATEST_TAG }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
docker:
name: Push Docker Image
needs: build
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
job:
- { platform: "amd64", goos: "linux", docker_platform: "linux/amd64" }
- { platform: "arm64", goos: "linux", docker_platform: "linux/arm64" }
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Log in to Docker Hub
if: ${{ env.SKIP_DOCKER_HUB == 'false' }} # Only log in if SKIP_DOCKER_HUB is false
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
- name: Log in to GitHub Container Registry
if: ${{ env.SKIP_GHCR == 'false' }} # Only log in if GHCR push is enabled
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract version from tag
id: vars
run: |
if [[ "${GITHUB_REF}" == refs/tags/* ]]; then
echo "TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
else
echo "TAG=latest" >> $GITHUB_ENV # Default to 'latest' if not a tag
fi
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v4
with:
images: ${{ env.BASE_IMAGE_NAMESPACE }}/rustdesk-api
- name: Download binaries
uses: actions/download-artifact@v4
with:
name: rustdesk-api-${{ matrix.job.goos }}-${{ matrix.job.platform }}
path: ./
- name: Unzip binaries
run: |
mkdir -p ${{ matrix.job.platform }}
tar -xzf ${{ matrix.job.goos }}-${{ matrix.job.platform }}.tar.gz -C ${{ matrix.job.platform }}
file ${{ matrix.job.platform }}/apimain
- name: Build and push Docker image to Docker Hub ${{ matrix.job.platform }}
if: ${{ env.SKIP_DOCKER_HUB == 'false' }} # Only run this step if SKIP_DOCKER_HUB is false
uses: docker/build-push-action@v5
with:
context: "."
file: ./Dockerfile
platforms: ${{ matrix.job.docker_platform }}
push: true
provenance: false
build-args: |
BUILDARCH=${{ matrix.job.platform }}
tags: |
${{ env.DOCKERHUB_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.LATEST_TAG }}-${{ matrix.job.platform }},
${{ env.DOCKERHUB_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}-${{ matrix.job.platform }}
labels: ${{ steps.meta.outputs.labels }}
- name: Build and push Docker image to GHCR ${{ matrix.job.platform }}
if: ${{ env.SKIP_GHCR == 'false' }} # Only run this step if SKIP_GHCR is false
uses: docker/build-push-action@v5
with:
context: "."
file: ./Dockerfile
platforms: ${{ matrix.job.docker_platform }}
push: true
provenance: false
build-args: |
BUILDARCH=${{ matrix.job.platform }}
tags: |
ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.LATEST_TAG }}-${{ matrix.job.platform }},
ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}-${{ matrix.job.platform }}
labels: ${{ steps.meta.outputs.labels }}
#
docker-manifest:
name: Push Docker Manifest
needs: docker
runs-on: ubuntu-latest
steps:
- name: Extract version from tag
id: vars
run: |
if [[ "${GITHUB_REF}" == refs/tags/* ]]; then
echo "TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
else
echo "TAG=latest" >> $GITHUB_ENV # Default to 'latest' if not a tag
fi
- name: Log in to Docker Hub
if: ${{ env.SKIP_DOCKER_HUB == 'false' }} # Only log in if Docker Hub push is enabled
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
- name: Log in to GitHub Container Registry
if: ${{ env.SKIP_GHCR == 'false' }} # Only log in if GHCR push is enabled
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Create and push manifest Docker Hub (:version)
if: ${{ env.SKIP_DOCKER_HUB == 'false' }}
uses: Noelware/docker-manifest-action@master
with:
base-image: ${{ env.BASE_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}
extra-images: ${{ env.DOCKERHUB_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}-amd64,
${{ env.DOCKERHUB_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}-arm64
push: true
- name: Create and push manifest GHCR (:version)
if: ${{ env.SKIP_GHCR == 'false' }}
uses: Noelware/docker-manifest-action@master
with:
base-image: ghcr.io/${{ env.BASE_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}
extra-images: ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}-amd64,
ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}-arm64
push: true
amend: true
- name: Create and push manifest Docker Hub (:latest)
if: ${{ env.SKIP_DOCKER_HUB == 'false' }}
uses: Noelware/docker-manifest-action@master
with:
base-image: ${{ env.BASE_IMAGE_NAMESPACE }}/rustdesk-api:latest
extra-images: ${{ env.DOCKERHUB_IMAGE_NAMESPACE }}/rustdesk-api:latest-amd64,
${{ env.DOCKERHUB_IMAGE_NAMESPACE }}/rustdesk-api:latest-arm64
push: true
- name: Create and push manifest GHCR (:latest)
if: ${{ env.SKIP_GHCR == 'false' }}
uses: Noelware/docker-manifest-action@master
with:
base-image: ghcr.io/${{ env.BASE_IMAGE_NAMESPACE }}/rustdesk-api:latest
extra-images: ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:latest-amd64,
ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:latest-arm64
push: true
amend: true

View File

@@ -1,38 +0,0 @@
name: Build and Push Docker Image
on:
push:
tags:
- 'v*.*.*' # 仅当推送标签(例如 v1.0.0)时触发
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
- name: Extract version from tag
id: vars
run: echo "TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
- name: Build and push Docker image
id: push
uses: docker/build-push-action@v6
with:
context: .
file: ./Dockerfile
push: true
tags: lejianwen/rustdesk-api:latest, lejianwen/rustdesk-api:${{ env.TAG }}

View File

@@ -1,27 +0,0 @@
# This workflow will build a golang project
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go
name: Go
on:
pull_request:
branches: [ "master" ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.22'
- name: mod tidy
run: go mod tidy
- name: Build
run: go build -v -o release/apimain cmd/apimain.go
- name: Test
run: go test -v cmd/apimain.go

View File

@@ -1,9 +1,9 @@
name: Build and Release
on:
push:
tags:
- 'v*.*.*' # 当推送带有版本号的 tag例如 v1.0.0)时触发工作流
workflow_dispatch:
# tags:
# - 'v*.*.*' # 当推送带有版本号的 tag例如 v1.0.0)时触发工作流
#on:
# push:
# branches: [ "master" ]

View File

@@ -1,84 +0,0 @@
name: Build and Release Arm64
on:
push:
tags:
- 'v*.*.*' # 当推送带有版本号的 tag例如 v1.0.0)时触发工作流
#on:
# push:
# branches: [ "master" ]
# pull_request:
# branches: [ "master" ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
goos: [ linux ] # 指定要构建的操作系统
goarch: [ arm64 ] # 指定架构
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Go environment
uses: actions/setup-go@v4
with:
go-version: '1.22' # 选择 Go 版本
- name: Set up npm
uses: actions/setup-node@v2
with:
node-version: '20'
- name: install gcc
run: |
sudo apt-get install gcc-aarch64-linux-gnu -y
- name: build rustdesk-api-web
run: |
git clone https://github.com/lejianwen/rustdesk-api-web
cd rustdesk-api-web
npm install
npm run build
mkdir ../resources/admin/ -p
cp -ar dist/* ../resources/admin/
- name: tidy
run: go mod tidy
- name: swag
run: |
go install github.com/swaggo/swag/cmd/swag@latest
swag init -g cmd/apimain.go --output docs/api --instanceName api --exclude http/controller/admin
swag init -g cmd/apimain.go --output docs/admin --instanceName admin --exclude http/controller/api
- name: Build for ${{ matrix.goos }}-${{ matrix.goarch }}
run: |
mkdir release -p
cp -ar resources release/
cp -ar docs release/
cp -ar conf release/
mkdir -p release/data
mkdir -p release/runtime
GOOS=${{ matrix.goos }} GOARCH=${{ matrix.goarch }} CC=aarch64-linux-gnu-gcc CGO_LDFLAGS="-static" CGO_ENABLED=1 go build -ldflags "-s -w" -o ./release/apimain ./cmd/apimain.go
tar -czf ${{ matrix.goos}}-${{ matrix.goarch }}.tar.gz ./release
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: myapp-${{ matrix.goos }}-${{ matrix.goarch }}
path: |
${{ matrix.goos}}-${{ matrix.goarch }}.tar.gz
- name: Upload to GitHub Release
uses: softprops/action-gh-release@v2
with:
files: |
${{ matrix.goos}}-${{ matrix.goarch }}.tar.gz
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -1,35 +1,10 @@
FROM golang:1.22-alpine as builder
RUN set -eux; \
apk add --no-cache git gcc build-base sqlite-dev npm nodejs; \
git clone https://github.com/lejianwen/rustdesk-api-web; \
git clone https://github.com/lejianwen/rustdesk-api; \
#先编译后台
cd rustdesk-api-web; \
npm install; \
npm run build; \
cd ..; \
mkdir -p rustdesk-api/resources/admin; \
cp -ar rustdesk-api-web/dist/* rustdesk-api/resources/admin; \
cd rustdesk-api; \
go mod tidy; \
go install github.com/swaggo/swag/cmd/swag@latest; \
swag init -g cmd/apimain.go --output docs/api --instanceName api --exclude http/controller/admin; \
swag init -g cmd/apimain.go --output docs/admin --instanceName admin --exclude http/controller/api; \
go env -w GO111MODULE=on;\
CGO_LDFLAGS="-static" CGO_ENABLED=1 go build -ldflags "-s -w" -o ./release/apimain ./cmd/apimain.go; \
cp -ar resources release/; \
mkdir -p release/resources/public; \
cp -ar docs release/; \
cp -ar conf release/; \
mkdir -p release/data; \
mkdir -p release/runtime;
FROM alpine
ARG BUILDARCH
WORKDIR /app
RUN apk add --no-cache tzdata
COPY --from=builder /go/rustdesk-api/release /app/
RUN apk add --no-cache tzdata file
COPY ./${BUILDARCH}/release /app/
RUN file /app/apimain
VOLUME /app/data
EXPOSE 21114

View File

@@ -9,8 +9,7 @@
<img src="https://img.shields.io/badge/gin-v1.9.0-lightBlue"/>
<img src="https://img.shields.io/badge/gorm-v1.25.7-green"/>
<img src="https://img.shields.io/badge/swag-v1.16.3-yellow"/>
<img src="https://github.com/lejianwen/rustdesk-api/actions/workflows/release.yml/badge.svg"/>
<img src="https://github.com/lejianwen/rustdesk-api/actions/workflows/docker.yml/badge.svg"/>
<img src="https://github.com/lejianwen/rustdesk-api/actions/workflows/build.yml/badge.svg"/>
</div>
# 特性
@@ -222,7 +221,8 @@ lejianwen/rustdesk-api
- 21115:21115
- 21116:21116 # 自定义 hbbs 映射端口
- 21116:21116/udp # 自定义 hbbs 映射端口
- 21118:21118 # web client 需要
- 21118:21118 # web client
- 21119:21119 # web client
image: rustdesk/rustdesk-server
command: hbbs -r <relay-server-ip[:port]> -k 123456789 # 填入个人域名或 IP + hbbr 暴露端口
volumes:

View File

@@ -8,8 +8,7 @@ desktop software that provides self-hosted solutions.
<img src="https://img.shields.io/badge/gin-v1.9.0-lightBlue"/>
<img src="https://img.shields.io/badge/gorm-v1.25.7-green"/>
<img src="https://img.shields.io/badge/swag-v1.16.3-yellow"/>
<img src="https://github.com/lejianwen/rustdesk-api/actions/workflows/release.yml/badge.svg"/>
<img src="https://github.com/lejianwen/rustdesk-api/actions/workflows/docker.yml/badge.svg"/>
<img src="https://github.com/lejianwen/rustdesk-api/actions/workflows/build.yml/badge.svg"/>
</div>
# Features
@@ -229,7 +228,8 @@ lejianwen/rustdesk-api
- 21115:21115
- 21116:21116 # 自定义 hbbs 映射端口
- 21116:21116/udp # 自定义 hbbs 映射端口
- 21118:21118 # web client 需要
- 21118:21118 # web client
- 21119:21119 # web client
image: rustdesk/rustdesk-server
command: hbbs -r <relay-server-ip[:port]> -k 123456789 # 填入个人域名或 IP + hbbr 暴露端口
volumes:

View File

@@ -23,6 +23,7 @@ import (
"github.com/go-redis/redis/v8"
"github.com/nicksnyder/go-i18n/v2/i18n"
"golang.org/x/text/language"
nethttp "net/http"
"reflect"
)
@@ -47,6 +48,8 @@ func main() {
ReportCaller: global.Config.Logger.ReportCaller,
})
InitI18n()
//redis
global.Redis = redis.NewClient(&redis.Options{
Addr: global.Config.Redis.Addr,
@@ -103,7 +106,6 @@ func main() {
//locker
global.Lock = lock.NewLocal()
InitI18n()
//gin
http.ApiInit()
@@ -198,7 +200,7 @@ func getTranslatorForLang(lang string) ut.Translator {
}
}
func DatabaseAutoUpdate() {
version := 126
version := 220
db := global.DB
@@ -259,6 +261,7 @@ func Migrate(version uint) {
&model.UserThird{},
&model.Oauth{},
&model.LoginLog{},
&model.ShareRecord{},
)
if err != nil {
fmt.Println("migrate err :=>", err)
@@ -268,13 +271,23 @@ func Migrate(version uint) {
var vc int64
global.DB.Model(&model.Version{}).Count(&vc)
if vc == 1 {
localizer := global.Localizer(&gin.Context{
Request: &nethttp.Request{},
})
defaultGroup, _ := localizer.LocalizeMessage(&i18n.Message{
ID: "DefaultGroup",
})
group := &model.Group{
Name: "默认组",
Name: defaultGroup,
Type: model.GroupTypeDefault,
}
service.AllService.GroupService.Create(group)
shareGroup, _ := localizer.LocalizeMessage(&i18n.Message{
ID: "ShareGroup",
})
groupShare := &model.Group{
Name: "共享组",
Name: shareGroup,
Type: model.GroupTypeShare,
}
service.AllService.GroupService.Create(groupShare)
@@ -282,7 +295,7 @@ func Migrate(version uint) {
is_admin := true
admin := &model.User{
Username: "admin",
Nickname: "管理员",
Nickname: "Admin",
Status: model.COMMON_STATUS_ENABLE,
IsAdmin: &is_admin,
GroupId: 1,

View File

@@ -22,7 +22,7 @@ const docTemplateadmin = `{
"token": []
}
],
"description": "创建地址簿",
"description": "批量创建地址簿",
"consumes": [
"application/json"
],
@@ -32,7 +32,7 @@ const docTemplateadmin = `{
"tags": [
"地址簿"
],
"summary": "创建地址簿",
"summary": "批量创建地址簿",
"parameters": [
{
"description": "地址簿信息",
@@ -1242,7 +1242,7 @@ const docTemplateadmin = `{
"token": []
}
],
"description": "设备删除",
"description": "批量设备删除",
"consumes": [
"application/json"
],
@@ -1252,15 +1252,15 @@ const docTemplateadmin = `{
"tags": [
"设备"
],
"summary": "设备删除",
"summary": "批量设备删除",
"parameters": [
{
"description": "设备信息",
"description": "设备id",
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/admin.PeerForm"
"$ref": "#/definitions/admin.PeerBatchDeleteForm"
}
}
],
@@ -1365,6 +1365,12 @@ const docTemplateadmin = `{
"description": "页大小",
"name": "page_size",
"in": "query"
},
{
"type": "integer",
"description": "时间",
"name": "time_ago",
"in": "query"
}
],
"responses": {
@@ -2311,6 +2317,12 @@ const docTemplateadmin = `{
"user_id": {
"type": "integer"
},
"user_ids": {
"type": "array",
"items": {
"type": "integer"
}
},
"username": {
"type": "string"
}
@@ -2398,6 +2410,20 @@ const docTemplateadmin = `{
}
}
},
"admin.PeerBatchDeleteForm": {
"type": "object",
"required": [
"row_ids"
],
"properties": {
"row_ids": {
"type": "array",
"items": {
"type": "integer"
}
}
}
},
"admin.PeerForm": {
"type": "object",
"properties": {
@@ -2759,6 +2785,9 @@ const docTemplateadmin = `{
"id": {
"type": "string"
},
"last_online_time": {
"type": "integer"
},
"memory": {
"type": "string"
},

View File

@@ -15,7 +15,7 @@
"token": []
}
],
"description": "创建地址簿",
"description": "批量创建地址簿",
"consumes": [
"application/json"
],
@@ -25,7 +25,7 @@
"tags": [
"地址簿"
],
"summary": "创建地址簿",
"summary": "批量创建地址簿",
"parameters": [
{
"description": "地址簿信息",
@@ -1235,7 +1235,7 @@
"token": []
}
],
"description": "设备删除",
"description": "批量设备删除",
"consumes": [
"application/json"
],
@@ -1245,15 +1245,15 @@
"tags": [
"设备"
],
"summary": "设备删除",
"summary": "批量设备删除",
"parameters": [
{
"description": "设备信息",
"description": "设备id",
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/admin.PeerForm"
"$ref": "#/definitions/admin.PeerBatchDeleteForm"
}
}
],
@@ -1358,6 +1358,12 @@
"description": "页大小",
"name": "page_size",
"in": "query"
},
{
"type": "integer",
"description": "时间",
"name": "time_ago",
"in": "query"
}
],
"responses": {
@@ -2304,6 +2310,12 @@
"user_id": {
"type": "integer"
},
"user_ids": {
"type": "array",
"items": {
"type": "integer"
}
},
"username": {
"type": "string"
}
@@ -2391,6 +2403,20 @@
}
}
},
"admin.PeerBatchDeleteForm": {
"type": "object",
"required": [
"row_ids"
],
"properties": {
"row_ids": {
"type": "array",
"items": {
"type": "integer"
}
}
}
},
"admin.PeerForm": {
"type": "object",
"properties": {
@@ -2752,6 +2778,9 @@
"id": {
"type": "string"
},
"last_online_time": {
"type": "integer"
},
"memory": {
"type": "string"
},

View File

@@ -46,6 +46,10 @@ definitions:
type: array
user_id:
type: integer
user_ids:
items:
type: integer
type: array
username:
type: string
required:
@@ -107,6 +111,15 @@ definitions:
- op
- redirect_url
type: object
admin.PeerBatchDeleteForm:
properties:
row_ids:
items:
type: integer
type: array
required:
- row_ids
type: object
admin.PeerForm:
properties:
cpu:
@@ -346,6 +359,8 @@ definitions:
type: string
id:
type: string
last_online_time:
type: integer
memory:
type: string
os:
@@ -471,7 +486,7 @@ paths:
post:
consumes:
- application/json
description: 创建地址簿
description: 批量创建地址簿
parameters:
- description: 地址簿信息
in: body
@@ -497,7 +512,7 @@ paths:
$ref: '#/definitions/response.Response'
security:
- token: []
summary: 创建地址簿
summary: 批量创建地址簿
tags:
- 地址簿
/admin/address_book/delete:
@@ -1194,14 +1209,14 @@ paths:
post:
consumes:
- application/json
description: 设备删除
description: 批量设备删除
parameters:
- description: 设备信息
- description: 设备id
in: body
name: body
required: true
schema:
$ref: '#/definitions/admin.PeerForm'
$ref: '#/definitions/admin.PeerBatchDeleteForm'
produces:
- application/json
responses:
@@ -1215,7 +1230,7 @@ paths:
$ref: '#/definitions/response.Response'
security:
- token: []
summary: 设备删除
summary: 批量设备删除
tags:
- 设备
/admin/peer/detail/{id}:
@@ -1264,6 +1279,10 @@ paths:
in: query
name: page_size
type: integer
- description: 时间
in: query
name: time_ago
type: integer
produces:
- application/json
responses:

1
go.mod
View File

@@ -13,6 +13,7 @@ require (
github.com/go-playground/validator/v10 v10.11.2
github.com/go-redis/redis/v8 v8.11.4
github.com/golang-jwt/jwt/v5 v5.2.1
github.com/google/uuid v1.1.2
github.com/nicksnyder/go-i18n/v2 v2.4.0
github.com/sirupsen/logrus v1.8.1
github.com/spf13/viper v1.9.0

View File

@@ -4,6 +4,7 @@ import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/model"
"Gwen/service"
_ "encoding/json"
"github.com/gin-gonic/gin"
@@ -69,6 +70,12 @@ func (ct *AddressBook) Create(c *gin.Context) {
if !service.AllService.UserService.IsAdmin(u) || t.UserId == 0 {
t.UserId = u.Id
}
ex := service.AllService.AddressBookService.InfoByUserIdAndId(t.UserId, t.Id)
if ex.RowId > 0 {
response.Fail(c, 101, response.TranslateMsg(c, "ItemExist"))
return
}
err := service.AllService.AddressBookService.Create(t)
if err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
@@ -77,6 +84,58 @@ func (ct *AddressBook) Create(c *gin.Context) {
response.Success(c, u)
}
// BatchCreate 批量创建地址簿
// @Tags 地址簿
// @Summary 批量创建地址簿
// @Description 批量创建地址簿
// @Accept json
// @Produce json
// @Param body body admin.AddressBookForm true "地址簿信息"
// @Success 200 {object} response.Response{data=model.AddressBook}
// @Failure 500 {object} response.Response
// @Router /admin/address_book/create [post]
// @Security token
func (ct *AddressBook) BatchCreate(c *gin.Context) {
f := &admin.AddressBookForm{}
if err := c.ShouldBindJSON(f); err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
errList := global.Validator.ValidStruct(c, f)
if len(errList) > 0 {
response.Fail(c, 101, errList[0])
return
}
//创建标签
for _, fu := range f.UserIds {
if fu == 0 {
continue
}
for _, ft := range f.Tags {
exTag := service.AllService.TagService.InfoByUserIdAndName(fu, ft)
if exTag.Id == 0 {
service.AllService.TagService.Create(&model.Tag{
UserId: fu,
Name: ft,
})
}
}
}
ts := f.ToAddressBooks()
for _, t := range ts {
if t.UserId == 0 {
continue
}
ex := service.AllService.AddressBookService.InfoByUserIdAndId(t.UserId, t.Id)
if ex.RowId == 0 {
service.AllService.AddressBookService.Create(t)
}
}
response.Success(c, nil)
}
// List 列表
// @Tags 地址簿
// @Summary 地址簿列表
@@ -102,9 +161,18 @@ func (ct *AddressBook) List(c *gin.Context) {
query.UserId = int(u.Id)
}
res := service.AllService.AddressBookService.List(query.Page, query.PageSize, func(tx *gorm.DB) {
if query.Id != "" {
tx.Where("id like ?", "%"+query.Id+"%")
}
if query.UserId > 0 {
tx.Where("user_id = ?", query.UserId)
}
if query.Username != "" {
tx.Where("username like ?", "%"+query.Username+"%")
}
if query.Hostname != "" {
tx.Where("hostname like ?", "%"+query.Hostname+"%")
}
})
response.Success(c, res)
}
@@ -189,3 +257,44 @@ func (ct *AddressBook) Delete(c *gin.Context) {
}
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
}
// ShareByWebClient
// @Tags 地址簿
// @Summary 地址簿分享
// @Description 地址簿分享
// @Accept json
// @Produce json
// @Param body body admin.ShareByWebClientForm true "地址簿信息"
// @Success 200 {object} response.Response
// @Failure 500 {object} response.Response
// @Router /admin/address_book/share [post]
// @Security token
func (ct *AddressBook) ShareByWebClient(c *gin.Context) {
f := &admin.ShareByWebClientForm{}
if err := c.ShouldBindJSON(f); err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
errList := global.Validator.ValidStruct(c, f)
if len(errList) > 0 {
response.Fail(c, 101, errList[0])
return
}
u := service.AllService.UserService.CurUser(c)
ab := service.AllService.AddressBookService.InfoByUserIdAndId(u.Id, f.Id)
if ab.RowId == 0 {
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
return
}
m := f.ToShareRecord()
m.UserId = u.Id
err := service.AllService.AddressBookService.ShareByWebClient(m)
if err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
return
}
response.Success(c, &gin.H{
"share_token": m.ShareToken,
})
}

View File

@@ -6,7 +6,9 @@ import (
"Gwen/http/response"
"Gwen/service"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
"strconv"
"time"
)
type Peer struct {
@@ -74,17 +76,27 @@ func (ct *Peer) Create(c *gin.Context) {
// @Produce json
// @Param page query int false "页码"
// @Param page_size query int false "页大小"
// @Param time_ago query int false "时间"
// @Success 200 {object} response.Response{data=model.PeerList}
// @Failure 500 {object} response.Response
// @Router /admin/peer/list [get]
// @Security token
func (ct *Peer) List(c *gin.Context) {
query := &admin.PageQuery{}
query := &admin.PeerQuery{}
if err := c.ShouldBindQuery(query); err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
res := service.AllService.PeerService.List(query.Page, query.PageSize, nil)
res := service.AllService.PeerService.List(query.Page, query.PageSize, func(tx *gorm.DB) {
if query.TimeAgo > 0 {
lt := time.Now().Unix() - int64(query.TimeAgo)
tx.Where("last_online_time < ?", lt)
}
if query.TimeAgo < 0 {
lt := time.Now().Unix() + int64(query.TimeAgo)
tx.Where("last_online_time > ?", lt)
}
})
response.Success(c, res)
}
@@ -158,3 +170,32 @@ func (ct *Peer) Delete(c *gin.Context) {
}
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
}
// BatchDelete 批量删除
// @Tags 设备
// @Summary 批量设备删除
// @Description 批量设备删除
// @Accept json
// @Produce json
// @Param body body admin.PeerBatchDeleteForm true "设备id"
// @Success 200 {object} response.Response
// @Failure 500 {object} response.Response
// @Router /admin/peer/delete [post]
// @Security token
func (ct *Peer) BatchDelete(c *gin.Context) {
f := &admin.PeerBatchDeleteForm{}
if err := c.ShouldBindJSON(f); err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
if len(f.RowIds) == 0 {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
return
}
err := service.AllService.PeerService.BatchDelete(f.RowIds)
if err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
return
}
response.Success(c, nil)
}

View File

@@ -1,9 +1,12 @@
package api
import (
requstform "Gwen/http/request/api"
"Gwen/http/response"
"Gwen/service"
"github.com/gin-gonic/gin"
"net/http"
"time"
)
type Index struct {
@@ -35,10 +38,22 @@ func (i *Index) Index(c *gin.Context) {
// @Failure 500 {object} response.Response
// @Router /heartbeat [post]
func (i *Index) Heartbeat(c *gin.Context) {
//b := &gin.H{}
//err := c.BindJSON(b)
//body : &map[id:xxx modified_at:0 uuid:NGIxZTZjM2YtNmNkMy00YTMwLWFiNjQtMzQ0MTA0NGE5ZDgz ver:1.003e+06]
//fmt.Println(b, err, c.Request.Header)
//header : map[Accept:[*/*] Accept-Encoding:[gzip] Content-Length:[105] Content-Type:[application/json]]
info := &requstform.PeerInfoInHeartbeat{}
err := c.ShouldBindJSON(info)
if err != nil {
c.JSON(http.StatusOK, gin.H{})
return
}
if info.Uuid == "" {
c.JSON(http.StatusOK, gin.H{})
return
}
peer := service.AllService.PeerService.FindByUuid(info.Uuid)
if peer == nil {
c.JSON(http.StatusOK, gin.H{})
return
}
peer.LastOnlineTime = time.Now().Unix()
service.AllService.PeerService.Update(peer)
c.JSON(http.StatusOK, gin.H{})
}

View File

@@ -6,6 +6,7 @@ import (
"Gwen/http/response/api"
"Gwen/service"
"github.com/gin-gonic/gin"
"time"
)
type WebClient struct {
@@ -40,3 +41,47 @@ func (i *WebClient) ServerConfig(c *gin.Context) {
},
)
}
// SharedPeer 分享的peer
// @Tags WEBCLIENT
// @Summary 分享的peer
// @Description 分享的peer
// @Accept json
// @Produce json
// @Success 200 {object} response.Response
// @Failure 500 {object} response.Response
// @Router /shared-peer [post]
func (i *WebClient) SharedPeer(c *gin.Context) {
j := &gin.H{}
c.ShouldBindJSON(j)
t := (*j)["share_token"].(string)
if t == "" {
response.Fail(c, 101, "share_token is required")
return
}
sr := service.AllService.AddressBookService.SharedPeer(t)
if sr == nil || sr.Id == 0 {
response.Fail(c, 101, "share not found")
return
}
//判断是否过期,created_at + expire > now
ca := time.Time(sr.CreatedAt)
if ca.Add(time.Second * time.Duration(sr.Expire)).Before(time.Now()) {
response.Fail(c, 101, "share expired")
return
}
ab := service.AllService.AddressBookService.InfoByUserIdAndId(sr.UserId, sr.PeerId)
if ab.RowId == 0 {
response.Fail(c, 101, "peer not found")
return
}
pp := &api.WebClientPeerPayload{}
pp.FromShareRecord(sr)
pp.Info.Username = ab.Username
pp.Info.Hostname = ab.Hostname
response.Success(c, gin.H{
"id_server": global.Config.Rustdesk.IdServer,
"key": global.Config.Rustdesk.Key,
"peer": pp,
})
}

View File

@@ -16,85 +16,7 @@ func (i *Index) ConfigJs(c *gin.Context) {
apiServer := global.Config.Rustdesk.ApiServer
tmp := `
function stringToUint8Array(str){
var arr = [];
for (var i = 0, j = str.length; i < j; ++i) {
arr.push(str.charCodeAt(i));
}
var tmpUint8Array = new Uint8Array(arr);
return tmpUint8Array
}
window._gwen = {}
window._gwen.kv = {}
function getQueryVariable() {
const query = window.location.hash.substring(3);
const vars = query.split("&");
for (var i = 0; i < vars.length; i++) {
var pair = vars[i].split("=");
window._gwen.kv[pair[0]] = pair[1]
}
}
getQueryVariable()
const id = window._gwen.kv.id || ''
if (id) {
localStorage.setItem('remote-id', id)
}
window._gwen.hosts = [
"rs-sg.rustdesk.com",
"rs-cn.rustdesk.com",
"rs-us.rustdesk.com",
]
localStorage.setItem('api-server', "` + apiServer + `")
const autoWriteServer = () => {
return setTimeout(() => {
const token = localStorage.getItem('access_token')
const apiserver = localStorage.getItem('api-server')
if (token && apiserver) {
fetch(apiserver + "/api/server-config", {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + token
}
}
).then(res => res.json()).then(res => {
if (res.code === 0) {
if(!localStorage.getItem('custom-rendezvous-server') || !localStorage.getItem('key') ) {
localStorage.setItem('custom-rendezvous-server', res.data.id_server)
localStorage.setItem('key', res.data.key)
}
if (res.data.peers) {
oldPeers = JSON.parse(localStorage.getItem('peers')) || {}
let needUpdate = false
Object.keys(res.data.peers).forEach(k => {
if(!oldPeers[k]) {
oldPeers[k] = res.data.peers[k]
needUpdate = true
}else{
oldPeers[k].info = res.data.peers[k].info
}
if (oldPeers[k].info && oldPeers[k].info.hash&&!oldPeers[k].password ) {
let p1 = window.atob(oldPeers[k].info.hash)
const pwd = stringToUint8Array(p1)
oldPeers[k].password = pwd.toString()
oldPeers[k].remember = true
}
})
localStorage.setItem('peers', JSON.stringify(oldPeers))
if(needUpdate) {
window.location.reload()
}
}
}
})
} else {
autoWriteServer()
}
}, 1000)
}
autoWriteServer()
`
c.String(200, tmp)
}

View File

@@ -16,6 +16,7 @@ type AddressBookForm struct {
Tags []string `json:"tags"`
Hash string `json:"hash"`
UserId uint `json:"user_id"`
UserIds []uint `json:"user_ids"`
ForceAlwaysRelay bool `json:"forceAlwaysRelay"`
RdpPort string `json:"rdpPort"`
RdpUsername string `json:"rdpUsername"`
@@ -48,9 +49,56 @@ func (a AddressBookForm) ToAddressBook() *model.AddressBook {
}
}
func (a AddressBookForm) ToAddressBooks() []*model.AddressBook {
//tags转换
tags, _ := json.Marshal(a.Tags)
abs := make([]*model.AddressBook, 0, len(a.UserIds))
for _, userId := range a.UserIds {
abs = append(abs, &model.AddressBook{
RowId: a.RowId,
Id: a.Id,
Username: a.Username,
Password: a.Password,
Hostname: a.Hostname,
Alias: a.Alias,
Platform: a.Platform,
Tags: tags,
Hash: a.Hash,
UserId: userId,
ForceAlwaysRelay: a.ForceAlwaysRelay,
RdpPort: a.RdpPort,
RdpUsername: a.RdpUsername,
Online: a.Online,
LoginName: a.LoginName,
SameServer: a.SameServer,
})
}
return abs
}
type AddressBookQuery struct {
UserId int `form:"user_id"`
IsMy int `form:"is_my"`
UserId int `form:"user_id"`
IsMy int `form:"is_my"`
Username string `form:"username"`
Hostname string `form:"hostname"`
Id string `form:"id"`
PageQuery
}
type ShareByWebClientForm struct {
Id string `json:"id" validate:"required"`
PasswordType string `json:"password_type" validate:"required,oneof=once fixed"` //只能是once,fixed
Password string `json:"password" validate:"required"`
Expire int64 `json:"expire"`
}
func (sbwcf ShareByWebClientForm) ToShareRecord() *model.ShareRecord {
return &model.ShareRecord{
UserId: 0,
PeerId: sbwcf.Id,
PasswordType: sbwcf.PasswordType,
Password: sbwcf.Password,
Expire: sbwcf.Expire,
}
}

View File

@@ -14,6 +14,10 @@ type PeerForm struct {
Version string `json:"version"`
}
type PeerBatchDeleteForm struct {
RowIds []uint `json:"row_ids" validate:"required"`
}
// ToPeer
func (f *PeerForm) ToPeer() *model.Peer {
return &model.Peer{
@@ -28,3 +32,8 @@ func (f *PeerForm) ToPeer() *model.Peer {
Version: f.Version,
}
}
type PeerQuery struct {
PageQuery
TimeAgo int `json:"time_ago" form:"time_ago"`
}

View File

@@ -71,3 +71,9 @@ type TagColorForm struct {
Name string `json:"name"`
Color uint `json:"color"`
}
type PeerInfoInHeartbeat struct {
Id string `json:"id"`
Uuid string `json:"uuid"`
Ver int `json:"ver"`
}

View File

@@ -9,6 +9,7 @@ type WebClientPeerPayload struct {
ViewStyle string `json:"view-style"`
Tm int64 `json:"tm"`
Info WebClientPeerInfoPayload `json:"info"`
Tmppwd string `json:"tmppwd"`
}
type WebClientPeerInfoPayload struct {
@@ -16,6 +17,7 @@ type WebClientPeerInfoPayload struct {
Hostname string `json:"hostname"`
Platform string `json:"platform"`
Hash string `json:"hash"`
Id string `json:"id"`
}
func (wcpp *WebClientPeerPayload) FromAddressBook(a *model.AddressBook) {
@@ -29,3 +31,16 @@ func (wcpp *WebClientPeerPayload) FromAddressBook(a *model.AddressBook) {
Hash: a.Hash,
}
}
func (wcpp *WebClientPeerPayload) FromShareRecord(sr *model.ShareRecord) {
wcpp.ViewStyle = "shrink"
//24小时前
wcpp.Tm = time.Now().UnixNano()
wcpp.Tmppwd = sr.Password
wcpp.Info = WebClientPeerInfoPayload{
Username: "",
Hostname: "",
Platform: "",
Id: sr.PeerId,
}
}

View File

@@ -93,6 +93,10 @@ func AddressBookBind(rg *gin.RouterGroup) {
aR.POST("/create", cont.Create)
aR.POST("/update", cont.Update)
aR.POST("/delete", cont.Delete)
aR.POST("/shareByWebClient", cont.ShareByWebClient)
arp := aR.Use(middleware.AdminPrivilege())
arp.POST("/batchCreate", cont.BatchCreate)
}
}
func PeerBind(rg *gin.RouterGroup) {
@@ -104,6 +108,9 @@ func PeerBind(rg *gin.RouterGroup) {
aR.POST("/create", cont.Create)
aR.POST("/update", cont.Update)
aR.POST("/delete", cont.Delete)
arp := aR.Use(middleware.AdminPrivilege())
arp.POST("/batchDelete", cont.BatchDelete)
}
}

View File

@@ -46,6 +46,12 @@ func ApiInit(g *gin.Engine) {
//提交系统信息
frg.POST("/sysinfo", pe.SysInfo)
}
{
w := &api.WebClient{}
frg.POST("/shared-peer", w.SharedPeer)
}
frg.Use(middleware.RustAuth())
{
w := &api.WebClient{}

View File

@@ -1,9 +1,12 @@
package orm
import (
"Gwen/global"
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"time"
)
type MysqlConfig struct {
@@ -22,6 +25,16 @@ func NewMysql(mysqlConf *MysqlConfig) *gorm.DB {
//SkipInitializeWithVersion: false, // 根据当前 MySQL 版本自动配置
}), &gorm.Config{
DisableForeignKeyConstraintWhenMigrating: true,
Logger: logger.New(
global.Logger, // io writer
logger.Config{
SlowThreshold: time.Second, // Slow SQL threshold
LogLevel: logger.Warn, // Log level
IgnoreRecordNotFoundError: true, // Ignore ErrRecordNotFound error for logger
ParameterizedQueries: true, // Don't include params in the SQL log
Colorful: true,
},
),
})
if err != nil {
fmt.Println(err)

View File

@@ -1,9 +1,12 @@
package orm
import (
"Gwen/global"
"fmt"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"time"
)
type SqliteConfig struct {
@@ -12,7 +15,18 @@ type SqliteConfig struct {
}
func NewSqlite(sqliteConf *SqliteConfig) *gorm.DB {
db, err := gorm.Open(sqlite.Open("./data/rustdeskapi.db"), &gorm.Config{})
db, err := gorm.Open(sqlite.Open("./data/rustdeskapi.db"), &gorm.Config{
Logger: logger.New(
global.Logger, // io writer
logger.Config{
SlowThreshold: time.Second, // Slow SQL threshold
LogLevel: logger.Warn, // Log level
IgnoreRecordNotFoundError: true, // Ignore ErrRecordNotFound error for logger
ParameterizedQueries: true, // Don't include params in the SQL log
Colorful: true,
},
),
})
if err != nil {
fmt.Println(err)
}

View File

@@ -1,17 +1,18 @@
package model
type Peer struct {
RowId uint `json:"row_id" gorm:"primaryKey;"`
Id string `json:"id" gorm:"default:'';not null;index"`
Cpu string `json:"cpu" gorm:"default:'';not null;"`
Hostname string `json:"hostname" gorm:"default:'';not null;"`
Memory string `json:"memory" gorm:"default:'';not null;"`
Os string `json:"os" gorm:"default:'';not null;"`
Username string `json:"username" gorm:"default:'';not null;"`
Uuid string `json:"uuid" gorm:"default:'';not null;index"`
Version string `json:"version" gorm:"default:'';not null;"`
UserId uint `json:"user_id" gorm:"default:0;not null;index"`
User User `json:"user,omitempty" gorm:""`
RowId uint `json:"row_id" gorm:"primaryKey;"`
Id string `json:"id" gorm:"default:'';not null;index"`
Cpu string `json:"cpu" gorm:"default:'';not null;"`
Hostname string `json:"hostname" gorm:"default:'';not null;"`
Memory string `json:"memory" gorm:"default:'';not null;"`
Os string `json:"os" gorm:"default:'';not null;"`
Username string `json:"username" gorm:"default:'';not null;"`
Uuid string `json:"uuid" gorm:"default:'';not null;index"`
Version string `json:"version" gorm:"default:'';not null;"`
UserId uint `json:"user_id" gorm:"default:0;not null;index"`
User User `json:"user,omitempty" gorm:""`
LastOnlineTime int64 `json:"last_online_time" gorm:"default:0;not null;"`
TimeModel
}

12
model/shareRecord.go Normal file
View File

@@ -0,0 +1,12 @@
package model
type ShareRecord struct {
IdModel
UserId uint `json:"user_id" gorm:"default:0;not null;index"`
PeerId string `json:"peer_id" gorm:"default:'';not null;index"`
ShareToken string `json:"share_token" gorm:"default:'';not null;index"`
PasswordType string `json:"password_type" gorm:"default:'';not null;"`
Password string `json:"password" gorm:"default:'';not null;"`
Expire int64 `json:"expire" gorm:"default:0;not null;"`
TimeModel
}

View File

@@ -109,3 +109,13 @@ other = "Decode oauth user info error."
description = "Old password error."
one = "Old password error."
other = "Old password error."
[DefaultGroup]
description = "Default group"
one = "Default Group"
other = "Default Group"
[ShareGroup]
description = "Share group"
one = "Share Group"
other = "Share Group"

View File

@@ -109,4 +109,15 @@ other = "解析授权用户信息失败。"
[OldPasswordError]
description = "Old password error."
one = "旧密码错误。"
other = "旧密码错误。"
other = "旧密码错误。"
[DefaultGroup]
description = "Default group."
one = "默认组"
other = "默认组"
[ShareGroup]
description = "Share group."
one = "共享组"
other = "共享组"

View File

@@ -2,188 +2,188 @@
<html>
<head>
<!--
If you are serving your web app in a path other than the root, change the
href value below to reflect the base path you are serving from.
<!--
If you are serving your web app in a path other than the root, change the
href value below to reflect the base path you are serving from.
The path provided below has to start and end with a slash "/" in order for
it to work correctly.
The path provided below has to start and end with a slash "/" in order for
it to work correctly.
For more details:
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
For more details:
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
This is a placeholder for base href that will be replaced by the value of
the `--base-href` argument provided to `flutter build`.
-->
<base href="/webclient/">
This is a placeholder for base href that will be replaced by the value of
the `--base-href` argument provided to `flutter build`.
-->
<base href="/webclient/">
<meta charset="UTF-8">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<meta name="description" content="Remote Desktop.">
<meta charset="UTF-8">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<meta name="description" content="Remote Desktop.">
<!-- iOS meta tags & icons -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="RustDesk">
<link rel="apple-touch-icon" href="icons/Icon-192.png">
<!-- iOS meta tags & icons -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="RustDesk">
<link rel="apple-touch-icon" href="icons/Icon-192.png">
<!-- Favicon -->
<link rel="icon" type="image/svg+xml" href="favicon.svg" />
<!-- Favicon -->
<link rel="icon" type="image/svg+xml" href="favicon.svg"/>
<title>RustDesk</title>
<link rel="manifest" href="manifest.json">
<script src="/webclient-config/index.js"></script>
<script>
<title>RustDesk</title>
<link rel="manifest" href="manifest.json">
<script src="/webclient-config/index.js"></script>
<script src="ogvjs-1.8.6/ogv.js"></script>
<script type="module" crossorigin src="js/dist/index.js"></script>
<script>
</script>
<link rel="modulepreload" href="js/dist/vendor.js">
<script src="yuv-canvas-1.2.6.js"></script>
<style>
.loading {
display: flex;
justify-content: center;
align-items: center;
margin: 0;
position: absolute;
top: 50%;
left: 50%;
-ms-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
}
</script>
<script src="ogvjs-1.8.6/ogv.js"></script>
<script type="module" crossorigin src="js/dist/index.js"></script>
<link rel="modulepreload" href="js/dist/vendor.js">
<script src="yuv-canvas-1.2.6.js"></script>
.loader {
border: 16px solid #f3f3f3;
border-radius: 50%;
border: 15px solid;
border-top: 16px solid #024eff;
border-right: 16px solid white;
border-bottom: 16px solid #024eff;
border-left: 16px solid white;
width: 120px;
height: 120px;
-webkit-animation: spin 2s linear infinite;
animation: spin 2s linear infinite;
}
<style>
.loading {
display: flex;
justify-content: center;
align-items: center;
margin: 0;
position: absolute;
top: 50%;
left: 50%;
-ms-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
}
@-webkit-keyframes spin {
0% {
-webkit-transform: rotate(0deg);
}
.loader {
border: 16px solid #f3f3f3;
border-radius: 50%;
border: 15px solid;
border-top: 16px solid #024eff;
border-right: 16px solid white;
border-bottom: 16px solid #024eff;
border-left: 16px solid white;
width: 120px;
height: 120px;
-webkit-animation: spin 2s linear infinite;
animation: spin 2s linear infinite;
}
100% {
-webkit-transform: rotate(360deg);
}
}
@-webkit-keyframes spin {
0% {
-webkit-transform: rotate(0deg);
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
}
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>
100% {
transform: rotate(360deg);
}
}
</style>
</head>
<body>
<div class="loading">
<div class="loading">
<div class="loader"></div>
</div>
<!-- This script installs service_worker.js to provide PWA functionality to
application. For more information, see:
https://developers.google.com/web/fundamentals/primers/service-workers -->
<script>
</div>
<!-- This script installs service_worker.js to provide PWA functionality to
application. For more information, see:
https://developers.google.com/web/fundamentals/primers/service-workers -->
<script>
var serviceWorkerVersion = '1200232272';
var scriptLoaded = false;
function loadMainDartJs() {
if (scriptLoaded) {
return;
}
scriptLoaded = true;
var scriptTag = document.createElement('script');
scriptTag.src = 'main.dart.js';
scriptTag.type = 'application/javascript';
document.body.append(scriptTag);
if (scriptLoaded) {
return;
}
scriptLoaded = true;
var scriptTag = document.createElement('script');
scriptTag.src = 'main.dart.js';
scriptTag.type = 'application/javascript';
document.body.append(scriptTag);
}
if ('serviceWorker' in navigator) {
// Service workers are supported. Use them.
window.addEventListener('load', function () {
// Wait for registration to finish before dropping the <script> tag.
// Otherwise, the browser will load the script multiple times,
// potentially different versions.
var serviceWorkerUrl = 'flutter_service_worker.js?v=' + serviceWorkerVersion;
navigator.serviceWorker.register(serviceWorkerUrl)
.then((reg) => {
function waitForActivation(serviceWorker) {
serviceWorker.addEventListener('statechange', () => {
if (serviceWorker.state == 'activated') {
console.log('Installed new service worker.');
loadMainDartJs();
// Service workers are supported. Use them.
window.addEventListener('load', function () {
// Wait for registration to finish before dropping the <script> tag.
// Otherwise, the browser will load the script multiple times,
// potentially different versions.
var serviceWorkerUrl = 'flutter_service_worker.js?v=' + serviceWorkerVersion;
navigator.serviceWorker.register(serviceWorkerUrl)
.then((reg) => {
function waitForActivation(serviceWorker) {
serviceWorker.addEventListener('statechange', () => {
if (serviceWorker.state == 'activated') {
console.log('Installed new service worker.');
loadMainDartJs();
}
});
}
if (!reg.active && (reg.installing || reg.waiting)) {
// No active web worker and we have installed or are installing
// one for the first time. Simply wait for it to activate.
waitForActivation(reg.installing || reg.waiting);
} else if (!reg.active.scriptURL.endsWith(serviceWorkerVersion)) {
// When the app updates the serviceWorkerVersion changes, so we
// need to ask the service worker to update.
console.log('New service worker available.');
reg.update();
waitForActivation(reg.installing);
} else {
// Existing service worker is still good.
console.log('Loading app from service worker.');
loadMainDartJs();
}
});
// If service worker doesn't succeed in a reasonable amount of time,
// fallback to plaint <script> tag.
setTimeout(() => {
if (!scriptLoaded) {
console.warn(
'Failed to load app from service worker. Falling back to plain <script> tag.',
);
loadMainDartJs();
}
});
}
if (!reg.active && (reg.installing || reg.waiting)) {
// No active web worker and we have installed or are installing
// one for the first time. Simply wait for it to activate.
waitForActivation(reg.installing || reg.waiting);
} else if (!reg.active.scriptURL.endsWith(serviceWorkerVersion)) {
// When the app updates the serviceWorkerVersion changes, so we
// need to ask the service worker to update.
console.log('New service worker available.');
reg.update();
waitForActivation(reg.installing);
} else {
// Existing service worker is still good.
console.log('Loading app from service worker.');
loadMainDartJs();
}
});
// If service worker doesn't succeed in a reasonable amount of time,
// fallback to plaint <script> tag.
setTimeout(() => {
if (!scriptLoaded) {
console.warn(
'Failed to load app from service worker. Falling back to plain <script> tag.',
);
loadMainDartJs();
}
}, 4000);
});
}, 4000);
});
} else {
// Service workers not supported. Just drop the <script> tag.
loadMainDartJs();
// Service workers not supported. Just drop the <script> tag.
loadMainDartJs();
}
</script>
<script src="libs/firebase-app.js?8.10.1"></script>
<script src="libs/firebase-analytics.js?8.10.1"></script>
</script>
<script src="libs/firebase-app.js?8.10.1"></script>
<script src="libs/firebase-analytics.js?8.10.1"></script>
<script>
<script>
// Your web app's Firebase configuration
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
const firebaseConfig = {
apiKey: "AIzaSyCgehIZk1aFP0E7wZtYRRqrfvNiNAF39-A",
authDomain: "rustdesk.firebaseapp.com",
databaseURL: "https://rustdesk.firebaseio.com",
projectId: "rustdesk",
storageBucket: "rustdesk.appspot.com",
messagingSenderId: "768133699366",
appId: "1:768133699366:web:d50faf0792cb208d7993e7",
measurementId: "G-9PEH85N6ZQ"
apiKey: "AIzaSyCgehIZk1aFP0E7wZtYRRqrfvNiNAF39-A",
authDomain: "rustdesk.firebaseapp.com",
databaseURL: "https://rustdesk.firebaseio.com",
projectId: "rustdesk",
storageBucket: "rustdesk.appspot.com",
messagingSenderId: "768133699366",
appId: "1:768133699366:web:d50faf0792cb208d7993e7",
measurementId: "G-9PEH85N6ZQ"
};
// Initialize Firebase
firebase.initializeApp(firebaseConfig);
firebase.analytics();
</script>
</script>
</body>
</html>

View File

@@ -7,9 +7,12 @@
<script src="ogvjs-1.8.6/ogv.js"></script>
<script src="./yuv-canvas-1.2.6.js"></script>
<title>Vite App</title>
<script type="module" crossorigin src="/index.js"></script>
<link rel="modulepreload" href="/vendor.js">
<link rel="stylesheet" href="/index.css">
</head>
<body>
<div id="app"></div>
</body>
</html>

File diff suppressed because one or more lines are too long

797
resources/web/js/package-lock.json generated vendored
View File

@@ -1,803 +1,8 @@
{
"name": "web_hbb",
"version": "1.0.0",
"lockfileVersion": 2,
"lockfileVersion": 1,
"requires": true,
"packages": {
"": {
"name": "web_hbb",
"version": "1.0.0",
"dependencies": {
"fast-sha256": "^1.3.0",
"libsodium": "^0.7.9",
"libsodium-wrappers": "^0.7.9",
"pcm-player": "^0.0.11",
"ts-proto": "^1.141.1",
"wasm-feature-detect": "^1.2.11",
"zstddec": "^0.0.2"
},
"devDependencies": {
"typescript": "4.4.4",
"vite": "2.8"
}
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.14.54",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.14.54.tgz",
"integrity": "sha512-bZBrLAIX1kpWelV0XemxBZllyRmM6vgFQQG2GdNb+r3Fkp0FOh1NJSvekXDs7jq70k4euu1cryLMfU+mTXlEpw==",
"cpu": [
"loong64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@protobufjs/aspromise": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
"integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ=="
},
"node_modules/@protobufjs/base64": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
"integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="
},
"node_modules/@protobufjs/codegen": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz",
"integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg=="
},
"node_modules/@protobufjs/eventemitter": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz",
"integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q=="
},
"node_modules/@protobufjs/fetch": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz",
"integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==",
"dependencies": {
"@protobufjs/aspromise": "^1.1.1",
"@protobufjs/inquire": "^1.1.0"
}
},
"node_modules/@protobufjs/float": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
"integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ=="
},
"node_modules/@protobufjs/inquire": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz",
"integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q=="
},
"node_modules/@protobufjs/path": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
"integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA=="
},
"node_modules/@protobufjs/pool": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
"integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw=="
},
"node_modules/@protobufjs/utf8": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
"integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="
},
"node_modules/@types/long": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz",
"integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA=="
},
"node_modules/@types/node": {
"version": "18.7.6",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.6.tgz",
"integrity": "sha512-EdxgKRXgYsNITy5mjjXjVE/CS8YENSdhiagGrLqjG0pvA2owgJ6i4l7wy/PFZGC0B1/H20lWKN7ONVDNYDZm7A=="
},
"node_modules/@types/object-hash": {
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/@types/object-hash/-/object-hash-1.3.4.tgz",
"integrity": "sha512-xFdpkAkikBgqBdG9vIlsqffDV8GpvnPEzs0IUtr1v3BEB97ijsFQ4RXVbUZwjFThhB4MDSTUfvmxUD5PGx0wXA=="
},
"node_modules/case-anything": {
"version": "2.1.10",
"resolved": "https://registry.npmjs.org/case-anything/-/case-anything-2.1.10.tgz",
"integrity": "sha512-JczJwVrCP0jPKh05McyVsuOg6AYosrB9XWZKbQzXeDAm2ClE/PJE/BcrrQrVyGYH7Jg8V/LDupmyL4kFlVsVFQ==",
"engines": {
"node": ">=12.13"
},
"funding": {
"url": "https://github.com/sponsors/mesqueeb"
}
},
"node_modules/dataloader": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/dataloader/-/dataloader-1.4.0.tgz",
"integrity": "sha512-68s5jYdlvasItOJnCuI2Q9s4q98g0pCyL3HrcKJu8KNugUl8ahgmZYg38ysLTgQjjXX3H8CJLkAvWrclWfcalw=="
},
"node_modules/detect-libc": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
"integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==",
"bin": {
"detect-libc": "bin/detect-libc.js"
},
"engines": {
"node": ">=0.10"
}
},
"node_modules/dprint-node": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/dprint-node/-/dprint-node-1.0.7.tgz",
"integrity": "sha512-NTZOW9A7ipb0n7z7nC3wftvsbceircwVHSgzobJsEQa+7RnOMbhrfX5IflA6CtC4GA63DSAiHYXa4JKEy9F7cA==",
"dependencies": {
"detect-libc": "^1.0.3"
}
},
"node_modules/esbuild": {
"version": "0.14.54",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.54.tgz",
"integrity": "sha512-Cy9llcy8DvET5uznocPyqL3BFRrFXSVqbgpMJ9Wz8oVjZlh/zUSNbPRbov0VX7VxN2JH1Oa0uNxZ7eLRb62pJA==",
"dev": true,
"hasInstallScript": true,
"bin": {
"esbuild": "bin/esbuild"
},
"engines": {
"node": ">=12"
},
"optionalDependencies": {
"@esbuild/linux-loong64": "0.14.54",
"esbuild-android-64": "0.14.54",
"esbuild-android-arm64": "0.14.54",
"esbuild-darwin-64": "0.14.54",
"esbuild-darwin-arm64": "0.14.54",
"esbuild-freebsd-64": "0.14.54",
"esbuild-freebsd-arm64": "0.14.54",
"esbuild-linux-32": "0.14.54",
"esbuild-linux-64": "0.14.54",
"esbuild-linux-arm": "0.14.54",
"esbuild-linux-arm64": "0.14.54",
"esbuild-linux-mips64le": "0.14.54",
"esbuild-linux-ppc64le": "0.14.54",
"esbuild-linux-riscv64": "0.14.54",
"esbuild-linux-s390x": "0.14.54",
"esbuild-netbsd-64": "0.14.54",
"esbuild-openbsd-64": "0.14.54",
"esbuild-sunos-64": "0.14.54",
"esbuild-windows-32": "0.14.54",
"esbuild-windows-64": "0.14.54",
"esbuild-windows-arm64": "0.14.54"
}
},
"node_modules/esbuild-android-64": {
"version": "0.14.54",
"resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.54.tgz",
"integrity": "sha512-Tz2++Aqqz0rJ7kYBfz+iqyE3QMycD4vk7LBRyWaAVFgFtQ/O8EJOnVmTOiDWYZ/uYzB4kvP+bqejYdVKzE5lAQ==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-android-arm64": {
"version": "0.14.54",
"resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.54.tgz",
"integrity": "sha512-F9E+/QDi9sSkLaClO8SOV6etqPd+5DgJje1F9lOWoNncDdOBL2YF59IhsWATSt0TLZbYCf3pNlTHvVV5VfHdvg==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-darwin-64": {
"version": "0.14.54",
"resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.54.tgz",
"integrity": "sha512-jtdKWV3nBviOd5v4hOpkVmpxsBy90CGzebpbO9beiqUYVMBtSc0AL9zGftFuBon7PNDcdvNCEuQqw2x0wP9yug==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-darwin-arm64": {
"version": "0.14.54",
"resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.54.tgz",
"integrity": "sha512-OPafJHD2oUPyvJMrsCvDGkRrVCar5aVyHfWGQzY1dWnzErjrDuSETxwA2HSsyg2jORLY8yBfzc1MIpUkXlctmw==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-freebsd-64": {
"version": "0.14.54",
"resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.54.tgz",
"integrity": "sha512-OKwd4gmwHqOTp4mOGZKe/XUlbDJ4Q9TjX0hMPIDBUWWu/kwhBAudJdBoxnjNf9ocIB6GN6CPowYpR/hRCbSYAg==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-freebsd-arm64": {
"version": "0.14.54",
"resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.54.tgz",
"integrity": "sha512-sFwueGr7OvIFiQT6WeG0jRLjkjdqWWSrfbVwZp8iMP+8UHEHRBvlaxL6IuKNDwAozNUmbb8nIMXa7oAOARGs1Q==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-linux-32": {
"version": "0.14.54",
"resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.54.tgz",
"integrity": "sha512-1ZuY+JDI//WmklKlBgJnglpUL1owm2OX+8E1syCD6UAxcMM/XoWd76OHSjl/0MR0LisSAXDqgjT3uJqT67O3qw==",
"cpu": [
"ia32"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-linux-64": {
"version": "0.14.54",
"resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.54.tgz",
"integrity": "sha512-EgjAgH5HwTbtNsTqQOXWApBaPVdDn7XcK+/PtJwZLT1UmpLoznPd8c5CxqsH2dQK3j05YsB3L17T8vE7cp4cCg==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-linux-arm": {
"version": "0.14.54",
"resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.54.tgz",
"integrity": "sha512-qqz/SjemQhVMTnvcLGoLOdFpCYbz4v4fUo+TfsWG+1aOu70/80RV6bgNpR2JCrppV2moUQkww+6bWxXRL9YMGw==",
"cpu": [
"arm"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-linux-arm64": {
"version": "0.14.54",
"resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.54.tgz",
"integrity": "sha512-WL71L+0Rwv+Gv/HTmxTEmpv0UgmxYa5ftZILVi2QmZBgX3q7+tDeOQNqGtdXSdsL8TQi1vIaVFHUPDe0O0kdig==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-linux-mips64le": {
"version": "0.14.54",
"resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.54.tgz",
"integrity": "sha512-qTHGQB8D1etd0u1+sB6p0ikLKRVuCWhYQhAHRPkO+OF3I/iSlTKNNS0Lh2Oc0g0UFGguaFZZiPJdJey3AGpAlw==",
"cpu": [
"mips64el"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-linux-ppc64le": {
"version": "0.14.54",
"resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.54.tgz",
"integrity": "sha512-j3OMlzHiqwZBDPRCDFKcx595XVfOfOnv68Ax3U4UKZ3MTYQB5Yz3X1mn5GnodEVYzhtZgxEBidLWeIs8FDSfrQ==",
"cpu": [
"ppc64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-linux-riscv64": {
"version": "0.14.54",
"resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.54.tgz",
"integrity": "sha512-y7Vt7Wl9dkOGZjxQZnDAqqn+XOqFD7IMWiewY5SPlNlzMX39ocPQlOaoxvT4FllA5viyV26/QzHtvTjVNOxHZg==",
"cpu": [
"riscv64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-linux-s390x": {
"version": "0.14.54",
"resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.54.tgz",
"integrity": "sha512-zaHpW9dziAsi7lRcyV4r8dhfG1qBidQWUXweUjnw+lliChJqQr+6XD71K41oEIC3Mx1KStovEmlzm+MkGZHnHA==",
"cpu": [
"s390x"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-netbsd-64": {
"version": "0.14.54",
"resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.54.tgz",
"integrity": "sha512-PR01lmIMnfJTgeU9VJTDY9ZerDWVFIUzAtJuDHwwceppW7cQWjBBqP48NdeRtoP04/AtO9a7w3viI+PIDr6d+w==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-openbsd-64": {
"version": "0.14.54",
"resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.54.tgz",
"integrity": "sha512-Qyk7ikT2o7Wu76UsvvDS5q0amJvmRzDyVlL0qf5VLsLchjCa1+IAvd8kTBgUxD7VBUUVgItLkk609ZHUc1oCaw==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-sunos-64": {
"version": "0.14.54",
"resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.54.tgz",
"integrity": "sha512-28GZ24KmMSeKi5ueWzMcco6EBHStL3B6ubM7M51RmPwXQGLe0teBGJocmWhgwccA1GeFXqxzILIxXpHbl9Q/Kw==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"sunos"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-windows-32": {
"version": "0.14.54",
"resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.54.tgz",
"integrity": "sha512-T+rdZW19ql9MjS7pixmZYVObd9G7kcaZo+sETqNH4RCkuuYSuv9AGHUVnPoP9hhuE1WM1ZimHz1CIBHBboLU7w==",
"cpu": [
"ia32"
],
"dev": true,
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-windows-64": {
"version": "0.14.54",
"resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.54.tgz",
"integrity": "sha512-AoHTRBUuYwXtZhjXZbA1pGfTo8cJo3vZIcWGLiUcTNgHpJJMC1rVA44ZereBHMJtotyN71S8Qw0npiCIkW96cQ==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-windows-arm64": {
"version": "0.14.54",
"resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.54.tgz",
"integrity": "sha512-M0kuUvXhot1zOISQGXwWn6YtS+Y/1RT9WrVIOywZnJHo3jCDyewAc79aKNQWFCQm+xNHVTq9h8dZKvygoXQQRg==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/fast-sha256": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/fast-sha256/-/fast-sha256-1.3.0.tgz",
"integrity": "sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ=="
},
"node_modules/fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"dev": true,
"hasInstallScript": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
"dev": true
},
"node_modules/has": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
"dev": true,
"dependencies": {
"function-bind": "^1.1.1"
},
"engines": {
"node": ">= 0.4.0"
}
},
"node_modules/is-core-module": {
"version": "2.10.0",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz",
"integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==",
"dev": true,
"dependencies": {
"has": "^1.0.3"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/libsodium": {
"version": "0.7.10",
"resolved": "https://registry.npmjs.org/libsodium/-/libsodium-0.7.10.tgz",
"integrity": "sha512-eY+z7hDrDKxkAK+QKZVNv92A5KYkxfvIshtBJkmg5TSiCnYqZP3i9OO9whE79Pwgm4jGaoHgkM4ao/b9Cyu4zQ=="
},
"node_modules/libsodium-wrappers": {
"version": "0.7.10",
"resolved": "https://registry.npmjs.org/libsodium-wrappers/-/libsodium-wrappers-0.7.10.tgz",
"integrity": "sha512-pO3F1Q9NPLB/MWIhehim42b/Fwb30JNScCNh8TcQ/kIc+qGLQch8ag8wb0keK3EP5kbGakk1H8Wwo7v+36rNQg==",
"dependencies": {
"libsodium": "^0.7.0"
}
},
"node_modules/long": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
"integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="
},
"node_modules/nanoid": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
"integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
"dev": true,
"bin": {
"nanoid": "bin/nanoid.cjs"
},
"engines": {
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
"node_modules/object-hash": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-1.3.1.tgz",
"integrity": "sha512-OSuu/pU4ENM9kmREg0BdNrUDIl1heYa4mBZacJc+vVWz4GtAwu7jO8s4AIt2aGRUTqxykpWzI3Oqnsm13tTMDA==",
"engines": {
"node": ">= 0.10.0"
}
},
"node_modules/path-parse": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
"dev": true
},
"node_modules/pcm-player": {
"version": "0.0.11",
"resolved": "https://registry.npmjs.org/pcm-player/-/pcm-player-0.0.11.tgz",
"integrity": "sha512-+FmX62jiqZa7wDCqSRQ1g3DuU6JNgpymgOLCWhmiE/Lj/M+rOUNqgNwVQX509LdA9dtBtVD3EQQUSp9JqU6upw=="
},
"node_modules/picocolors": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
"dev": true
},
"node_modules/postcss": {
"version": "8.4.16",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.16.tgz",
"integrity": "sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ==",
"dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/postcss"
}
],
"dependencies": {
"nanoid": "^3.3.4",
"picocolors": "^1.0.0",
"source-map-js": "^1.0.2"
},
"engines": {
"node": "^10 || ^12 || >=14"
}
},
"node_modules/protobufjs": {
"version": "6.11.3",
"resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.3.tgz",
"integrity": "sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg==",
"hasInstallScript": true,
"dependencies": {
"@protobufjs/aspromise": "^1.1.2",
"@protobufjs/base64": "^1.1.2",
"@protobufjs/codegen": "^2.0.4",
"@protobufjs/eventemitter": "^1.1.0",
"@protobufjs/fetch": "^1.1.0",
"@protobufjs/float": "^1.0.2",
"@protobufjs/inquire": "^1.1.0",
"@protobufjs/path": "^1.1.2",
"@protobufjs/pool": "^1.1.0",
"@protobufjs/utf8": "^1.1.0",
"@types/long": "^4.0.1",
"@types/node": ">=13.7.0",
"long": "^4.0.0"
},
"bin": {
"pbjs": "bin/pbjs",
"pbts": "bin/pbts"
}
},
"node_modules/resolve": {
"version": "1.22.1",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
"integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==",
"dev": true,
"dependencies": {
"is-core-module": "^2.9.0",
"path-parse": "^1.0.7",
"supports-preserve-symlinks-flag": "^1.0.0"
},
"bin": {
"resolve": "bin/resolve"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/rollup": {
"version": "2.77.3",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.77.3.tgz",
"integrity": "sha512-/qxNTG7FbmefJWoeeYJFbHehJ2HNWnjkAFRKzWN/45eNBBF/r8lo992CwcJXEzyVxs5FmfId+vTSTQDb+bxA+g==",
"dev": true,
"bin": {
"rollup": "dist/bin/rollup"
},
"engines": {
"node": ">=10.0.0"
},
"optionalDependencies": {
"fsevents": "~2.3.2"
}
},
"node_modules/source-map-js": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/supports-preserve-symlinks-flag": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
"dev": true,
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/ts-poet": {
"version": "6.4.1",
"resolved": "https://registry.npmjs.org/ts-poet/-/ts-poet-6.4.1.tgz",
"integrity": "sha512-AjZEs4h2w4sDfwpHMxQKHrTlNh2wRbM5NRXmLz0RiH+yPGtSQFbe9hBpNocU8vqVNgfh0BIOiXR80xDz3kKxUQ==",
"dependencies": {
"dprint-node": "^1.0.7"
}
},
"node_modules/ts-proto": {
"version": "1.141.1",
"resolved": "https://registry.npmjs.org/ts-proto/-/ts-proto-1.141.1.tgz",
"integrity": "sha512-1b7Ka6R96FvFZldHnYPTFy4rzwOo+OTpIP1mBFW0dDwq4WWtSkIVlZ+SokOQSC1TiccNshOJwQC9soVyWfQ7Zg==",
"dependencies": {
"@types/object-hash": "^1.3.0",
"case-anything": "^2.1.10",
"dataloader": "^1.4.0",
"object-hash": "^1.3.1",
"protobufjs": "^6.11.3",
"ts-poet": "^6.2.0",
"ts-proto-descriptors": "1.7.1"
},
"bin": {
"protoc-gen-ts_proto": "protoc-gen-ts_proto"
}
},
"node_modules/ts-proto-descriptors": {
"version": "1.7.1",
"resolved": "https://registry.npmjs.org/ts-proto-descriptors/-/ts-proto-descriptors-1.7.1.tgz",
"integrity": "sha512-oIKUh3K4Xts4v29USGLfUG+2mEk32MsqpgZAOUyUlkrcIdv34yE+k2oZ2Nzngm6cV/JgFdOxRCqeyvmWHuYAyw==",
"dependencies": {
"long": "^4.0.0",
"protobufjs": "^6.8.8"
}
},
"node_modules/typescript": {
"version": "4.4.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz",
"integrity": "sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==",
"dev": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=4.2.0"
}
},
"node_modules/vite": {
"version": "2.8.6",
"resolved": "https://registry.npmjs.org/vite/-/vite-2.8.6.tgz",
"integrity": "sha512-e4H0QpludOVKkmOsRyqQ7LTcMUDF3mcgyNU4lmi0B5JUbe0ZxeBBl8VoZ8Y6Rfn9eFKYtdXNPcYK97ZwH+K2ug==",
"dev": true,
"dependencies": {
"esbuild": "^0.14.14",
"postcss": "^8.4.6",
"resolve": "^1.22.0",
"rollup": "^2.59.0"
},
"bin": {
"vite": "bin/vite.js"
},
"engines": {
"node": ">=12.2.0"
},
"optionalDependencies": {
"fsevents": "~2.3.2"
},
"peerDependencies": {
"less": "*",
"sass": "*",
"stylus": "*"
},
"peerDependenciesMeta": {
"less": {
"optional": true
},
"sass": {
"optional": true
},
"stylus": {
"optional": true
}
}
},
"node_modules/wasm-feature-detect": {
"version": "1.2.11",
"resolved": "https://registry.npmjs.org/wasm-feature-detect/-/wasm-feature-detect-1.2.11.tgz",
"integrity": "sha512-HUqwaodrQGaZgz1lZaNioIkog9tkeEJjrM3eq4aUL04whXOVDRc/o2EGb/8kV0QX411iAYWEqq7fMBmJ6dKS6w=="
},
"node_modules/zstddec": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/zstddec/-/zstddec-0.0.2.tgz",
"integrity": "sha512-DCo0oxvcvOTGP/f5FA6tz2Z6wF+FIcEApSTu0zV5sQgn9hoT5lZ9YRAKUraxt9oP7l4e8TnNdi8IZTCX6WCkwA=="
}
},
"dependencies": {
"@esbuild/linux-loong64": {
"version": "0.14.54",

View File

@@ -3,7 +3,7 @@
"version": "1.0.0",
"scripts": {
"dev": "vite",
"build": "python ./gen_js_from_hbb.py > src/gen_js_from_hbb.ts && python ./ts_proto.py && tsc && yarn vite build",
"build": "vite build",
"preview": "vite preview"
},
"devDependencies": {

View File

@@ -14,7 +14,8 @@ const HOSTS = [
"rs-us.rustdesk.com",
];
let HOST = localStorage.getItem("rendezvous-server") || HOSTS[0];
const SCHEMA = "ws://";
//根据协议设置为ws或wss
const SCHEMA=location.protocol=="https:"?"wss://":"ws://";
type MsgboxCallback = (type: string, title: string, text: string) => void;
type DrawCallback = (data: Uint8Array) => void;
@@ -99,7 +100,7 @@ export default class Connection {
ws.sendRendezvous({ punch_hole_request });
const msg = (await ws.next()) as rendezvous.RendezvousMessage;
ws.close();
console.log(new Date() + ": Got relay response");
console.log(new Date() + ": Got relay response", msg);
const phr = msg.punch_hole_response;
const rr = msg.relay_response;
if (phr) {
@@ -236,8 +237,14 @@ export default class Connection {
async msgLoop() {
while (true) {
const msg = (await this._ws?.next()) as message.Message;
// console.log("msg", msg);
if (msg?.hash) {
this._hash = msg?.hash;
const tmp = this.getOption('tmppwd')
if(!this._password && tmp){
this._password = Uint8Array.from(JSON.parse("[" + tmp + "]"));
this.setOption('tmppwd', '')
}
if (!this._password)
this.msgbox("input-password", "Password Required", "");
this.login();

100
resources/web/js/src/ljw.js vendored Normal file
View File

@@ -0,0 +1,100 @@
window._gwen = {}
window._gwen.kv = {}
const apiserver = localStorage.getItem('api-server')
function stringToUint8Array(str) {
var arr = [];
for (var i = 0, j = str.length; i < j; ++i) {
arr.push(str.charCodeAt(i));
}
var tmpUint8Array = new Uint8Array(arr);
return tmpUint8Array
}
function getQueryVariable() {
const query = window.location.hash.substring(3);
const vars = query.split("&");
for (var i = 0; i < vars.length; i++) {
var pair = vars[i].split("=");
window._gwen.kv[pair[0]] = pair[1]
}
}
getQueryVariable()
const id = window._gwen.kv.id || ''
if (id) {
localStorage.setItem('remote-id', id)
}
const share_token = window._gwen.kv.share_token || ''
if (share_token) {
fetch(apiserver + "/api/shared-peer", {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({share_token})
}).then(res => res.json()).then(res => {
if (res.code === 0) {
localStorage.setItem('custom-rendezvous-server', res.data.id_server)
localStorage.setItem('key', res.data.key)
const peer = res.data.peer
localStorage.setItem('remote-id', peer.info.id)
peer.tmppwd = stringToUint8Array(window.atob(peer.tmppwd)).toString()
const oldPeers = JSON.parse(localStorage.getItem('peers')) || {}
oldPeers[peer.info.id] = peer
localStorage.setItem('peers', JSON.stringify(oldPeers))
}
})
}
const autoWriteServer = () => {
return setTimeout(() => {
const token = localStorage.getItem('access_token')
if (token && apiserver) {
fetch(apiserver + "/api/server-config", {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + token
}
}
).then(res => res.json()).then(res => {
if (res.code === 0) {
if (!localStorage.getItem('custom-rendezvous-server') || !localStorage.getItem('key')) {
localStorage.setItem('custom-rendezvous-server', res.data.id_server)
localStorage.setItem('key', res.data.key)
}
if (res.data.peers) {
const oldPeers = JSON.parse(localStorage.getItem('peers')) || {}
let needUpdate = false
Object.keys(res.data.peers).forEach(k => {
if (!oldPeers[k]) {
oldPeers[k] = res.data.peers[k]
needUpdate = true
} else {
oldPeers[k].info = res.data.peers[k].info
}
if (oldPeers[k].info && oldPeers[k].info.hash && !oldPeers[k].password) {
let p1 = window.atob(oldPeers[k].info.hash)
const pwd = stringToUint8Array(p1)
oldPeers[k].password = pwd.toString()
oldPeers[k].remember = true
}
})
localStorage.setItem('peers', JSON.stringify(oldPeers))
if (needUpdate) {
window.location.reload()
}
}
}
})
} else {
autoWriteServer()
}
}, 1000)
}
autoWriteServer()

View File

@@ -1,2 +1,3 @@
import "./ljw";
import "./globals";
import "./ui";
import "./ui";

View File

@@ -3,6 +3,7 @@ package service
import (
"Gwen/global"
"Gwen/model"
"github.com/google/uuid"
"gorm.io/gorm"
)
@@ -113,3 +114,16 @@ func (t *AddressBookService) Delete(u *model.AddressBook) error {
func (t *AddressBookService) Update(u *model.AddressBook) error {
return global.DB.Model(u).Updates(u).Error
}
// ShareByWebClient 分享
func (t *AddressBookService) ShareByWebClient(m *model.ShareRecord) error {
m.ShareToken = uuid.New().String()
return global.DB.Create(m).Error
}
// SharedPeer
func (t *AddressBookService) SharedPeer(shareToken string) *model.ShareRecord {
m := &model.ShareRecord{}
global.DB.Where("share_token = ?", shareToken).First(m)
return m
}

View File

@@ -71,6 +71,11 @@ func (ps *PeerService) Delete(u *model.Peer) error {
return global.DB.Delete(u).Error
}
// BatchDelete
func (ps *PeerService) BatchDelete(ids []uint) error {
return global.DB.Where("row_id in (?)", ids).Delete(&model.Peer{}).Error
}
// Update 更新
func (ps *PeerService) Update(u *model.Peer) error {
return global.DB.Model(u).Updates(u).Error