Compare commits
45 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a876078a9c | ||
|
|
495f2ae3c6 | ||
|
|
4e6d11baf0 | ||
|
|
a951b982b3 | ||
|
|
a33be66504 | ||
|
|
f41b9d5887 | ||
|
|
3c608463e6 | ||
|
|
eeffbe124a | ||
|
|
d7f2d54faa | ||
|
|
7db4b03634 | ||
|
|
77760a681a | ||
|
|
f9c1447ceb | ||
|
|
fb749c1902 | ||
|
|
240c44aa07 | ||
|
|
92cd8642c8 | ||
|
|
89d90cf919 | ||
|
|
920c6b6d8b | ||
|
|
1dd4df3a1c | ||
|
|
c7d44cc253 | ||
|
|
5082ab1893 | ||
|
|
e8b2425222 | ||
|
|
09d12cefd8 | ||
|
|
5f1166965d | ||
|
|
0dbab182e9 | ||
|
|
512f3f99fd | ||
|
|
6fb4fad705 | ||
|
|
fa92529e9b | ||
|
|
d6c6051a6c | ||
|
|
ce063bd3ac | ||
|
|
4468894dfb | ||
|
|
8b00b919ad | ||
|
|
64f4a6dfac | ||
|
|
6faa5153b6 | ||
|
|
a771b1e9b0 | ||
|
|
7750f9c54d | ||
|
|
b2d24ee67b | ||
|
|
589a2a5123 | ||
|
|
184d3d357d | ||
|
|
50b3d85270 | ||
|
|
09fdd34ba3 | ||
|
|
bba10261c5 | ||
|
|
46bfe54097 | ||
|
|
503e7a307e | ||
|
|
821b0a6faf | ||
|
|
d60fdff179 |
147
.github/workflows/build.yml
vendored
@@ -55,6 +55,13 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
repository: lejianwen/rustdesk-api-web
|
||||||
|
path: rustdesk-api-web
|
||||||
|
ref: master
|
||||||
|
|
||||||
- name: Set up Go environment
|
- name: Set up Go environment
|
||||||
uses: actions/setup-go@v4
|
uses: actions/setup-go@v4
|
||||||
@@ -66,14 +73,12 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
node-version: '20'
|
node-version: '20'
|
||||||
|
|
||||||
|
|
||||||
- name: build rustdesk-api-web
|
- name: build rustdesk-api-web
|
||||||
|
working-directory: rustdesk-api-web
|
||||||
run: |
|
run: |
|
||||||
git clone ${{ env.WEBCLIENT_SOURCE_LOCATION }}
|
|
||||||
cd rustdesk-api-web
|
|
||||||
npm install
|
npm install
|
||||||
npm run build
|
npm run build
|
||||||
mkdir ../resources/admin/ -p
|
mkdir -p ../resources/admin/
|
||||||
cp -ar dist/* ../resources/admin/
|
cp -ar dist/* ../resources/admin/
|
||||||
|
|
||||||
- name: tidy
|
- name: tidy
|
||||||
@@ -96,6 +101,8 @@ jobs:
|
|||||||
if [ "${{ matrix.job.goos }}" = "windows" ]; then
|
if [ "${{ matrix.job.goos }}" = "windows" ]; then
|
||||||
sudo apt-get install gcc-mingw-w64-x86-64 zip -y
|
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
|
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
|
||||||
|
echo @echo off > release/start.bat
|
||||||
|
echo cmd /c \"%~dp0apimain.exe\" >> release/start.bat
|
||||||
zip -r ${{ matrix.job.goos}}-${{ matrix.job.platform }}.${{matrix.job.file_ext}} ./release
|
zip -r ${{ matrix.job.goos}}-${{ matrix.job.platform }}.${{matrix.job.file_ext}} ./release
|
||||||
else
|
else
|
||||||
if [ "${{ matrix.job.platform }}" = "arm64" ]; then
|
if [ "${{ matrix.job.platform }}" = "arm64" ]; then
|
||||||
@@ -121,15 +128,92 @@ jobs:
|
|||||||
name: rustdesk-api-${{ matrix.job.goos }}-${{ matrix.job.platform }}
|
name: rustdesk-api-${{ matrix.job.goos }}-${{ matrix.job.platform }}
|
||||||
path: |
|
path: |
|
||||||
${{ matrix.job.goos}}-${{ matrix.job.platform }}.${{matrix.job.file_ext}}
|
${{ matrix.job.goos}}-${{ matrix.job.platform }}.${{matrix.job.file_ext}}
|
||||||
|
|
||||||
- name: Upload to GitHub Release
|
- name: Upload to GitHub Release
|
||||||
uses: softprops/action-gh-release@v2
|
uses: softprops/action-gh-release@v2
|
||||||
with:
|
with:
|
||||||
files: |
|
files: |
|
||||||
${{ matrix.job.goos}}-${{ matrix.job.platform }}.${{matrix.job.file_ext}}
|
${{ matrix.job.goos}}-${{ matrix.job.platform }}.${{matrix.job.file_ext}}
|
||||||
# tag_name: ${{ env.LATEST_TAG }}
|
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Generate Changelog
|
||||||
|
run: npx changelogithub # or changelogithub@0.12 if ensure the stable result
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
||||||
|
|
||||||
|
deb-package:
|
||||||
|
name: debian package - ${{ matrix.job.platform }}
|
||||||
|
needs: build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
job:
|
||||||
|
- { platform: "amd64", goos: "linux", debian_platform: "amd64", crossbuild_package: ""}
|
||||||
|
- { platform: "arm64", goos: "linux", debian_platform: "arm64", crossbuild_package: "crossbuild-essential-arm64" }
|
||||||
|
- { platform: "armv7l", goos: "linux", debian_platform: "armhf", crossbuild_package: "crossbuild-essential-armhf" }
|
||||||
|
steps:
|
||||||
|
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v2
|
||||||
|
|
||||||
|
- name: Create packaging env
|
||||||
|
run: |
|
||||||
|
sudo apt update
|
||||||
|
DEBIAN_FRONTEND=noninteractive sudo apt install -y devscripts build-essential debhelper pkg-config ${{ matrix.job.crossbuild_package }}
|
||||||
|
mkdir -p debian-build/${{ matrix.job.platform }}/bin
|
||||||
|
|
||||||
|
- name: Get tag version
|
||||||
|
id: get_tag
|
||||||
|
run: |
|
||||||
|
TAG_VERSION="${GITHUB_REF##*/}"
|
||||||
|
VERSION="${TAG_VERSION#v}"
|
||||||
|
echo "TAG_VERSION=$TAG_VERSION" >> $GITHUB_ENV
|
||||||
|
echo "VERSION=$VERSION" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Update changelog
|
||||||
|
run: |
|
||||||
|
DATE=$(date -R)
|
||||||
|
sed -i "1i rustdesk-api-server (${VERSION}) stable; urgency=medium\n\n * Automatically generated release for version ${VERSION}.\n\n -- GitHub Actions <actions@github.com> ${DATE}\n" debian/changelog
|
||||||
|
|
||||||
|
- 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 }}
|
||||||
|
|
||||||
|
- name: Build package for ${{ matrix.job.platform }} arch
|
||||||
|
run: |
|
||||||
|
mv ${{ matrix.job.platform }}/release/apimain debian-build/${{ matrix.job.platform }}/bin/rustdesk-api
|
||||||
|
chmod -v a+x debian-build/${{ matrix.job.platform }}/bin/*
|
||||||
|
mkdir -p data
|
||||||
|
cp -vr debian systemd conf data resources runtime debian-build/${{ matrix.job.platform }}/
|
||||||
|
cat debian/control.tpl | sed 's/{{ ARCH }}/${{ matrix.job.debian_platform }}/' > debian-build/${{ matrix.job.platform }}/debian/control
|
||||||
|
cd debian-build/${{ matrix.job.platform }}/
|
||||||
|
debuild -i -us -uc -b -a${{ matrix.job.debian_platform}}
|
||||||
|
|
||||||
|
- name: Upload artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: rustdesk-api-${{ matrix.job.debian_platform }}
|
||||||
|
path: |
|
||||||
|
debian-build/*.deb
|
||||||
|
|
||||||
|
- name: Create Release
|
||||||
|
uses: softprops/action-gh-release@v2
|
||||||
|
with:
|
||||||
|
files: |
|
||||||
|
debian-build/rustdesk-api-server_*_${{ matrix.job.debian_platform }}.deb
|
||||||
|
|
||||||
docker:
|
docker:
|
||||||
name: Push Docker Image
|
name: Push Docker Image
|
||||||
needs: build
|
needs: build
|
||||||
@@ -191,7 +275,6 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
mkdir -p ${{ matrix.job.platform }}
|
mkdir -p ${{ matrix.job.platform }}
|
||||||
tar -xzf ${{ matrix.job.goos }}-${{ matrix.job.platform }}.tar.gz -C ${{ 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 }}
|
- 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
|
if: ${{ env.SKIP_DOCKER_HUB == 'false' }} # Only run this step if SKIP_DOCKER_HUB is false
|
||||||
@@ -209,6 +292,21 @@ jobs:
|
|||||||
${{ env.DOCKERHUB_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}-${{ matrix.job.platform }}
|
${{ env.DOCKERHUB_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}-${{ matrix.job.platform }}
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
|
||||||
|
- name: Build and push Docker Full S6 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_full_s6
|
||||||
|
platforms: ${{ matrix.job.docker_platform }}
|
||||||
|
push: true
|
||||||
|
provenance: false
|
||||||
|
build-args: |
|
||||||
|
BUILDARCH=${{ matrix.job.platform }}
|
||||||
|
tags: |
|
||||||
|
${{ env.DOCKERHUB_IMAGE_NAMESPACE }}/rustdesk-api:full-s6-${{ matrix.job.platform }}
|
||||||
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
|
||||||
- name: Build and push Docker image to GHCR ${{ matrix.job.platform }}
|
- 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
|
if: ${{ env.SKIP_GHCR == 'false' }} # Only run this step if SKIP_GHCR is false
|
||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v5
|
||||||
@@ -225,6 +323,21 @@ jobs:
|
|||||||
ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}-${{ matrix.job.platform }}
|
ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}-${{ matrix.job.platform }}
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
|
||||||
|
- name: Build and push Docker Full S6 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:full-s6-${{ matrix.job.platform }}
|
||||||
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
|
||||||
#
|
#
|
||||||
docker-manifest:
|
docker-manifest:
|
||||||
name: Push Docker Manifest
|
name: Push Docker Manifest
|
||||||
@@ -295,4 +408,26 @@ jobs:
|
|||||||
ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:latest-armv7l,
|
ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:latest-armv7l,
|
||||||
ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:latest-arm64
|
ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:latest-arm64
|
||||||
push: true
|
push: true
|
||||||
|
amend: true
|
||||||
|
|
||||||
|
- name: Create and push Full S6 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:full-s6
|
||||||
|
extra-images: ${{ env.DOCKERHUB_IMAGE_NAMESPACE }}/rustdesk-api:full-s6-amd64,
|
||||||
|
${{ env.DOCKERHUB_IMAGE_NAMESPACE }}/rustdesk-api:full-s6-armv7l,
|
||||||
|
${{ env.DOCKERHUB_IMAGE_NAMESPACE }}/rustdesk-api:full-s6-arm64
|
||||||
|
push: true
|
||||||
|
amend: true
|
||||||
|
|
||||||
|
- name: Create and push Full S6 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:full-s6
|
||||||
|
extra-images: ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:full-s6-amd64,
|
||||||
|
ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:full-s6-armv7l,
|
||||||
|
ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:full-s6-arm64
|
||||||
|
push: true
|
||||||
amend: true
|
amend: true
|
||||||
76
.github/workflows/build_test.yml
vendored
@@ -52,6 +52,12 @@ jobs:
|
|||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
repository: lejianwen/rustdesk-api-web
|
||||||
|
path: rustdesk-api-web
|
||||||
|
ref: master
|
||||||
|
|
||||||
- name: Set up Go environment
|
- name: Set up Go environment
|
||||||
uses: actions/setup-go@v4
|
uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
@@ -62,14 +68,12 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
node-version: '20'
|
node-version: '20'
|
||||||
|
|
||||||
|
|
||||||
- name: build rustdesk-api-web
|
- name: build rustdesk-api-web
|
||||||
|
working-directory: rustdesk-api-web
|
||||||
run: |
|
run: |
|
||||||
git clone ${{ env.WEBCLIENT_SOURCE_LOCATION }}
|
|
||||||
cd rustdesk-api-web
|
|
||||||
npm install
|
npm install
|
||||||
npm run build
|
npm run build
|
||||||
mkdir ../resources/admin/ -p
|
mkdir -p ../resources/admin/
|
||||||
cp -ar dist/* ../resources/admin/
|
cp -ar dist/* ../resources/admin/
|
||||||
|
|
||||||
- name: tidy
|
- name: tidy
|
||||||
@@ -92,6 +96,8 @@ jobs:
|
|||||||
if [ "${{ matrix.job.goos }}" = "windows" ]; then
|
if [ "${{ matrix.job.goos }}" = "windows" ]; then
|
||||||
sudo apt-get install gcc-mingw-w64-x86-64 zip -y
|
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
|
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
|
||||||
|
echo @echo off > release/start.bat
|
||||||
|
echo cmd /c \"%~dp0apimain.exe\" >> release/start.bat
|
||||||
zip -r ${{ matrix.job.goos}}-${{ matrix.job.platform }}.${{matrix.job.file_ext}} ./release
|
zip -r ${{ matrix.job.goos}}-${{ matrix.job.platform }}.${{matrix.job.file_ext}} ./release
|
||||||
else
|
else
|
||||||
if [ "${{ matrix.job.platform }}" = "arm64" ]; then
|
if [ "${{ matrix.job.platform }}" = "arm64" ]; then
|
||||||
@@ -117,6 +123,7 @@ jobs:
|
|||||||
name: rustdesk-api-${{ matrix.job.goos }}-${{ matrix.job.platform }}
|
name: rustdesk-api-${{ matrix.job.goos }}-${{ matrix.job.platform }}
|
||||||
path: |
|
path: |
|
||||||
${{ matrix.job.goos}}-${{ matrix.job.platform }}.${{matrix.job.file_ext}}
|
${{ matrix.job.goos}}-${{ matrix.job.platform }}.${{matrix.job.file_ext}}
|
||||||
|
|
||||||
- name: Upload to GitHub Release
|
- name: Upload to GitHub Release
|
||||||
uses: softprops/action-gh-release@v2
|
uses: softprops/action-gh-release@v2
|
||||||
with:
|
with:
|
||||||
@@ -126,6 +133,66 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
deb-package:
|
||||||
|
name: debian package - ${{ matrix.job.platform }}
|
||||||
|
needs: build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
job:
|
||||||
|
- { platform: "amd64", goos: "linux", debian_platform: "amd64", crossbuild_package: ""}
|
||||||
|
- { platform: "arm64", goos: "linux", debian_platform: "arm64", crossbuild_package: "crossbuild-essential-arm64" }
|
||||||
|
- { platform: "armv7l", goos: "linux", debian_platform: "armhf", crossbuild_package: "crossbuild-essential-armhf" }
|
||||||
|
steps:
|
||||||
|
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v2
|
||||||
|
|
||||||
|
- name: Create packaging env
|
||||||
|
run: |
|
||||||
|
sudo apt update
|
||||||
|
DEBIAN_FRONTEND=noninteractive sudo apt install -y devscripts build-essential debhelper pkg-config ${{ matrix.job.crossbuild_package }}
|
||||||
|
mkdir -p debian-build/${{ matrix.job.platform }}/bin
|
||||||
|
|
||||||
|
- 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 }}
|
||||||
|
|
||||||
|
- name: Build package for ${{ matrix.job.platform }} arch
|
||||||
|
run: |
|
||||||
|
mv ${{ matrix.job.platform }}/release/apimain debian-build/${{ matrix.job.platform }}/bin/rustdesk-api
|
||||||
|
chmod -v a+x debian-build/${{ matrix.job.platform }}/bin/*
|
||||||
|
mkdir -p data
|
||||||
|
cp -vr debian systemd conf data resources runtime debian-build/${{ matrix.job.platform }}/
|
||||||
|
cat debian/control.tpl | sed 's/{{ ARCH }}/${{ matrix.job.debian_platform }}/' > debian-build/${{ matrix.job.platform }}/debian/control
|
||||||
|
cd debian-build/${{ matrix.job.platform }}/
|
||||||
|
debuild -i -us -uc -b -a${{ matrix.job.debian_platform}}
|
||||||
|
|
||||||
|
- name: Upload artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: rustdesk-api-${{ matrix.job.debian_platform }}
|
||||||
|
path: |
|
||||||
|
debian-build/*.deb
|
||||||
|
|
||||||
|
- name: Upload to GitHub Release
|
||||||
|
uses: softprops/action-gh-release@v2
|
||||||
|
with:
|
||||||
|
tag_name: test
|
||||||
|
files: |
|
||||||
|
debian-build/rustdesk-api-server_*_${{ matrix.job.debian_platform }}.deb
|
||||||
|
|
||||||
docker:
|
docker:
|
||||||
name: Push Docker Image
|
name: Push Docker Image
|
||||||
needs: build
|
needs: build
|
||||||
@@ -187,7 +254,6 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
mkdir -p ${{ matrix.job.platform }}
|
mkdir -p ${{ matrix.job.platform }}
|
||||||
tar -xzf ${{ matrix.job.goos }}-${{ matrix.job.platform }}.tar.gz -C ${{ 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 }}
|
- 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
|
if: ${{ env.SKIP_DOCKER_HUB == 'false' }} # Only run this step if SKIP_DOCKER_HUB is false
|
||||||
|
|||||||
@@ -2,9 +2,8 @@ FROM alpine
|
|||||||
|
|
||||||
ARG BUILDARCH
|
ARG BUILDARCH
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
RUN apk add --no-cache tzdata file
|
RUN apk add --no-cache tzdata
|
||||||
COPY ./${BUILDARCH}/release /app/
|
COPY ./${BUILDARCH}/release /app/
|
||||||
RUN file /app/apimain
|
|
||||||
VOLUME /app/data
|
VOLUME /app/data
|
||||||
|
|
||||||
EXPOSE 21114
|
EXPOSE 21114
|
||||||
|
|||||||
38
Dockerfile_full_s6
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
FROM rustdesk/rustdesk-server-s6:latest AS server
|
||||||
|
|
||||||
|
FROM alpine
|
||||||
|
|
||||||
|
ARG BUILDARCH
|
||||||
|
WORKDIR /app
|
||||||
|
RUN apk add --no-cache tzdata
|
||||||
|
COPY ./${BUILDARCH}/release /app/
|
||||||
|
|
||||||
|
COPY --from=server /init /init
|
||||||
|
COPY --from=server /etc/s6-overlay /etc/s6-overlay
|
||||||
|
COPY --from=server /package /package
|
||||||
|
COPY --from=server /usr/bin/healthcheck.sh /usr/bin/healthcheck.sh
|
||||||
|
COPY --from=server /usr/bin/hbbr /usr/bin/hbbr
|
||||||
|
COPY --from=server /usr/bin/hbbs /usr/bin/hbbs
|
||||||
|
COPY --from=server /usr/bin/rustdesk-utils /usr/bin/rustdesk-utils
|
||||||
|
COPY --from=server /command /command
|
||||||
|
|
||||||
|
RUN \
|
||||||
|
mkdir -p /etc/s6-overlay/s6-rc.d/api && \
|
||||||
|
echo -e "key-secret\nhbbs" > /etc/s6-overlay/s6-rc.d/api/dependencies && \
|
||||||
|
echo "longrun" > /etc/s6-overlay/s6-rc.d/api/type && \
|
||||||
|
echo "#!/command/with-contenv sh" > /etc/s6-overlay/s6-rc.d/api/run && \
|
||||||
|
echo "cd /app" >> /etc/s6-overlay/s6-rc.d/api/run && \
|
||||||
|
echo "./apimain" >> /etc/s6-overlay/s6-rc.d/api/run && \
|
||||||
|
touch /etc/s6-overlay/s6-rc.d/user/contents.d/api && \
|
||||||
|
echo "/package/admin/s6/command/s6-svstat /run/s6-rc/servicedirs/api || exit 1" >> /usr/bin/healthcheck.sh && \
|
||||||
|
ln -s /run /var/run
|
||||||
|
|
||||||
|
ENV RELAY=relay.example.com
|
||||||
|
ENV ENCRYPTED_ONLY=0
|
||||||
|
|
||||||
|
VOLUME /data
|
||||||
|
VOLUME /app/data
|
||||||
|
|
||||||
|
EXPOSE 21114 21115 21116 21116/udp 21117 21118 21119
|
||||||
|
|
||||||
|
ENTRYPOINT ["/init"]
|
||||||
193
README.md
@@ -34,6 +34,7 @@
|
|||||||
- 快速使用web client
|
- 快速使用web client
|
||||||
- i18n
|
- i18n
|
||||||
- 通过 web client 分享给游客
|
- 通过 web client 分享给游客
|
||||||
|
- server控制(一些官方的简单的指令 [WIKI](https://github.com/lejianwen/rustdesk-api/wiki/Rustdesk-Command))
|
||||||
- Web Client
|
- Web Client
|
||||||
- 自动获取API server
|
- 自动获取API server
|
||||||
- 自动获取ID服务器和KEY
|
- 自动获取ID服务器和KEY
|
||||||
@@ -43,51 +44,6 @@
|
|||||||
- CLI
|
- CLI
|
||||||
- 重置管理员密码
|
- 重置管理员密码
|
||||||
|
|
||||||
## 使用前准备
|
|
||||||
|
|
||||||
### [Rustdesk](https://github.com/rustdesk/rustdesk)
|
|
||||||
|
|
||||||
#### PC客户端使用的是 ***1.3.0***,经测试 ***1.2.6+*** 都可以
|
|
||||||
|
|
||||||
#### 关于PC端链接超时或者链接不上的问题以及解决方案
|
|
||||||
##### 链接不上或者超时
|
|
||||||
因为server端相对于客户端落后版本,server不会响应客户端的`secure_tcp`请求,所以客户端超时。
|
|
||||||
相关代码代码位置在`https://github.com/rustdesk/rustdesk/blob/master/src/client.rs#L322`
|
|
||||||
```rust
|
|
||||||
if !key.is_empty() && !token.is_empty() {
|
|
||||||
// mainly for the security of token
|
|
||||||
allow_err!(secure_tcp(&mut socket, key).await);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
可看到当`key`和`token`都不为空时,会调用`secure_tcp`,但是server端不会响应,所以客户端超时
|
|
||||||
`secure_tcp` 代码位置在 `https://github.com/rustdesk/rustdesk/blob/master/src/common.rs#L1203`
|
|
||||||
|
|
||||||
##### 4种解决方案
|
|
||||||
1. server端指定key。
|
|
||||||
- 优点:简单
|
|
||||||
- 缺点:链接不是加密的
|
|
||||||
```bash
|
|
||||||
hbbs -r <relay-server-ip[:port]> -k <key>
|
|
||||||
hbbr -k <key>
|
|
||||||
```
|
|
||||||
比如
|
|
||||||
```bash
|
|
||||||
hbbs -r <relay-server-ip[:port]> -k abc1234567
|
|
||||||
hbbr -k abc1234567
|
|
||||||
```
|
|
||||||
2. server端使用系统生成的key,或者自定义的密钥对,但如果client已登录,链接时容易超时或者链接不上,可以退出登录后再链接就可以了,webclient可以不用退出登录
|
|
||||||
- 优点:链接加密
|
|
||||||
- 缺点:操作麻烦
|
|
||||||
3. server端使用系统生成的key,或者自定义的密钥对,fork官方客户端的代码将`secure_tcp`修改成直接返回,然后通过`Github Actions`编译,下载编译后的客户端。
|
|
||||||
参考[官方文档](https://rustdesk.com/docs/en/dev/build/all/)
|
|
||||||
- 优点:链接加密,可以自定义客户端一些功能,编译后直接可用
|
|
||||||
- 缺点:需要自己fork代码,编译,有点难度
|
|
||||||
4. 使用[我fork的代码](https://github.com/lejianwen/rustdesk),已经修改了`secure_tcp`,可以直接下载使用,[下载地址](https://github.com/lejianwen/rustdesk/releases)
|
|
||||||
- 优点:代码改动可查看,`Github Actions`编译,链接加密,直接下载使用
|
|
||||||
- 缺点:可能跟不上官方版本更新
|
|
||||||
|
|
||||||
***对链接加密要求不高的可以使用`1`,对链接加密要求高的可以使用`3`或`4`***
|
|
||||||
|
|
||||||
## 功能
|
## 功能
|
||||||
|
|
||||||
### API 服务
|
### API 服务
|
||||||
@@ -120,14 +76,14 @@
|
|||||||
2. 普通用户界面
|
2. 普通用户界面
|
||||||

|

|
||||||
右上角可以更改密码,可以切换语言,可以切换`白天/黑夜`模式
|
右上角可以更改密码,可以切换语言,可以切换`白天/黑夜`模式
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
3. 分组可以自定义,方便管理,暂时支持两种类型: `共享组` 和 `普通组`
|
3. 每个用户可以多个地址簿,也可以将地址簿共享给其他用户
|
||||||

|
4. 分组可以自定义,方便管理,暂时支持两种类型: `共享组` 和 `普通组`
|
||||||
4. 可以直接打开webclient,方便使用;也可以分享给游客,游客可以直接通过webclient远程到设备
|
5. 可以直接打开webclient,方便使用;也可以分享给游客,游客可以直接通过webclient远程到设备
|
||||||
|
|
||||||

|

|
||||||
5. Oauth,支持了`Github`, `Google` 以及 `OIDC`, 需要创建一个`OAuth App`,然后配置到后台
|
6. Oauth,支持了`Github`, `Google` 以及 `OIDC`, 需要创建一个`OAuth App`,然后配置到后台
|
||||||

|

|
||||||
- 对于`Google` 和 `Github`, `Issuer` 和 `Scopes`不需要填写.
|
- 对于`Google` 和 `Github`, `Issuer` 和 `Scopes`不需要填写.
|
||||||
- 对于`OIDC`, `Issuer`是必须的。`Scopes`是可选的,默认为 `openid,profile,email`. 确保可以获取 `sub`,`email` 和`preferred_username`
|
- 对于`OIDC`, `Issuer`是必须的。`Scopes`是可选的,默认为 `openid,profile,email`. 确保可以获取 `sub`,`email` 和`preferred_username`
|
||||||
@@ -135,20 +91,32 @@
|
|||||||
中创建,地址 [https://github.com/settings/developers](https://github.com/settings/developers)
|
中创建,地址 [https://github.com/settings/developers](https://github.com/settings/developers)
|
||||||
- `Authorization callback URL`填写`http://<your server[:port]>/api/oauth/callback`
|
- `Authorization callback URL`填写`http://<your server[:port]>/api/oauth/callback`
|
||||||
,比如`http://127.0.0.1:21114/api/oauth/callback`
|
,比如`http://127.0.0.1:21114/api/oauth/callback`
|
||||||
|
7. 登录日志
|
||||||
|
8. 链接日志
|
||||||
|
9. 文件传输日志
|
||||||
|
10. server控制
|
||||||
|
|
||||||
|
- `简易模式`,已经界面化了一些简单的指令,可以直接在后台执行
|
||||||
|

|
||||||
|
|
||||||
|
- `高级模式`,直接在后台执行指令
|
||||||
|
* 可以官方指令
|
||||||
|
* 可以添加自定义指令
|
||||||
|
* 可以执行自定义指令
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
### Web Client:
|
### Web Client:
|
||||||
|
|
||||||
1. 如果已经登录了后台,web client将自动直接登录
|
1. 如果已经登录了后台,web client将自动直接登录
|
||||||
2. 如果没登录后台,点击右上角登录即可,api server已经自动配置好了
|
2. 如果没登录后台,点击右上角登录即可,api server已经自动配置好了
|
||||||

|
|
||||||
3. 登录后,会自动同步ID服务器和KEY
|
3. 登录后,会自动同步ID服务器和KEY
|
||||||
4. 登录后,会将地址簿自动保存到web client中,方便使用
|
4. 登录后,会将地址簿自动保存到web client中,方便使用
|
||||||
5. 现已支持`v2 Preview`,访问路径是`/webclient2`
|
5. 现已支持`v2 Preview`,访问路径是`/webclient2`
|
||||||

|

|
||||||
6. `v2 preview` 部署
|
6. `v2 preview` 部署,参考[WIKI](https://github.com/lejianwen/rustdesk-api/wiki)
|
||||||
- 如果是通过`443`端口的`https`部署,必须配置反向代理,可以参考[官方文档](https://rustdesk.com/docs/en/self-host/rustdesk-server-pro/faq/#8-add-websocket-secure-wss-support-for-the-id-server-and-relay-server-to-enable-secure-communication-for-the-web-client)
|
|
||||||
- 如果是`http`或者其他的`https`端口部署,则和`v1`一样,配置好`21118`,`21119`即可
|
|
||||||
- 更多参考[Web-Client-V2-Preview-Document](https://github.com/lejianwen/rustdesk-api/wiki/Web-Client-V2-Preview-Document)
|
|
||||||
|
|
||||||
### 自动化文档: 使用 Swag 生成 API 文档,方便开发者理解和使用 API。
|
### 自动化文档: 使用 Swag 生成 API 文档,方便开发者理解和使用 API。
|
||||||
|
|
||||||
@@ -180,6 +148,7 @@ lang: "en"
|
|||||||
app:
|
app:
|
||||||
web-client: 1 # 1:启用 0:禁用
|
web-client: 1 # 1:启用 0:禁用
|
||||||
register: false #是否开启注册
|
register: false #是否开启注册
|
||||||
|
show-swagger: 0 #是否显示swagger文档
|
||||||
gin:
|
gin:
|
||||||
api-addr: "0.0.0.0:21114"
|
api-addr: "0.0.0.0:21114"
|
||||||
mode: "release"
|
mode: "release"
|
||||||
@@ -207,42 +176,50 @@ logger:
|
|||||||
proxy:
|
proxy:
|
||||||
enable: false
|
enable: false
|
||||||
host: ""
|
host: ""
|
||||||
|
jwt:
|
||||||
|
key: ""
|
||||||
|
expire-duration: 360000
|
||||||
```
|
```
|
||||||
|
|
||||||
### 环境变量
|
### 环境变量
|
||||||
变量名前缀是`RUSTDESK_API`,环境变量如果存在将覆盖配置文件中的配置
|
变量名前缀是`RUSTDESK_API`,环境变量如果存在将覆盖配置文件中的配置
|
||||||
|
|
||||||
| 变量名 | 说明 | 示例 |
|
| 变量名 | 说明 | 示例 |
|
||||||
|------------------------------------|---------------------------------------------------------|------------------------------|
|
|---------------------------------------------------|---------------------------------------------------------|------------------------------|
|
||||||
| TZ | 时区 | Asia/Shanghai |
|
| TZ | 时区 | Asia/Shanghai |
|
||||||
| RUSTDESK_API_LANG | 语言 | `en`,`zh-CN` |
|
| RUSTDESK_API_LANG | 语言 | `en`,`zh-CN` |
|
||||||
| RUSTDESK_API_APP_WEB_CLIENT | 是否启用web-client; 1:启用,0:不启用; 默认启用 | 1 |
|
| RUSTDESK_API_APP_WEB_CLIENT | 是否启用web-client; 1:启用,0:不启用; 默认启用 | 1 |
|
||||||
| RUSTDESK_API_APP_REGISTER | 是否开启注册; `true`, `false` 默认`false` | `false` |
|
| RUSTDESK_API_APP_REGISTER | 是否开启注册; `true`, `false` 默认`false` | `false` |
|
||||||
| -----ADMIN配置----- | ---------- | ---------- |
|
| RUSTDESK_API_APP_SHOW_SWAGGER | 是否可见swagger文档;`1`显示,`0`不显示,默认`0`不显示 | `1` |
|
||||||
| RUSTDESK_API_ADMIN_TITLE | 后台标题 | `RustDesk Api Admin` |
|
| -----ADMIN配置----- | ---------- | ---------- |
|
||||||
| RUSTDESK_API_ADMIN_HELLO | 后台欢迎语,可以使用`html` | |
|
| RUSTDESK_API_ADMIN_TITLE | 后台标题 | `RustDesk Api Admin` |
|
||||||
| RUSTDESK_API_ADMIN_HELLO_FILE | 后台欢迎语文件,如果内容多,使用文件更方便。<br>会覆盖`RUSTDESK_API_ADMIN_HELLO` | `./conf/admin/hello.html` |
|
| RUSTDESK_API_ADMIN_HELLO | 后台欢迎语,可以使用`html` | |
|
||||||
| -----GIN配置----- | ---------- | ---------- |
|
| RUSTDESK_API_ADMIN_HELLO_FILE | 后台欢迎语文件,如果内容多,使用文件更方便。<br>会覆盖`RUSTDESK_API_ADMIN_HELLO` | `./conf/admin/hello.html` |
|
||||||
| RUSTDESK_API_GIN_TRUST_PROXY | 信任的代理IP列表,以`,`分割,默认信任所有 | 192.168.1.2,192.168.1.3 |
|
| -----GIN配置----- | ---------- | ---------- |
|
||||||
| -----------GORM配置---------------- | ------------------------------------ | --------------------------- |
|
| RUSTDESK_API_GIN_TRUST_PROXY | 信任的代理IP列表,以`,`分割,默认信任所有 | 192.168.1.2,192.168.1.3 |
|
||||||
| RUSTDESK_API_GORM_TYPE | 数据库类型sqlite或者mysql,默认sqlite | sqlite |
|
| -----------GORM配置---------------- | ------------------------------------ | --------------------------- |
|
||||||
| RUSTDESK_API_GORM_MAX_IDLE_CONNS | 数据库最大空闲连接数 | 10 |
|
| RUSTDESK_API_GORM_TYPE | 数据库类型sqlite或者mysql,默认sqlite | sqlite |
|
||||||
| RUSTDESK_API_GORM_MAX_OPEN_CONNS | 数据库最大打开连接数 | 100 |
|
| RUSTDESK_API_GORM_MAX_IDLE_CONNS | 数据库最大空闲连接数 | 10 |
|
||||||
| RUSTDESK_API_RUSTDESK_PERSONAL | 是否启用个人版API, 1:启用,0:不启用; 默认启用 | 1 |
|
| RUSTDESK_API_GORM_MAX_OPEN_CONNS | 数据库最大打开连接数 | 100 |
|
||||||
| -----MYSQL配置----- | ---------- | ---------- |
|
| RUSTDESK_API_RUSTDESK_PERSONAL | 是否启用个人版API, 1:启用,0:不启用; 默认启用 | 1 |
|
||||||
| RUSTDESK_API_MYSQL_USERNAME | mysql用户名 | root |
|
| -----MYSQL配置----- | ---------- | ---------- |
|
||||||
| RUSTDESK_API_MYSQL_PASSWORD | mysql密码 | 111111 |
|
| RUSTDESK_API_MYSQL_USERNAME | mysql用户名 | root |
|
||||||
| RUSTDESK_API_MYSQL_ADDR | mysql地址 | 192.168.1.66:3306 |
|
| RUSTDESK_API_MYSQL_PASSWORD | mysql密码 | 111111 |
|
||||||
| RUSTDESK_API_MYSQL_DBNAME | mysql数据库名 | rustdesk |
|
| RUSTDESK_API_MYSQL_ADDR | mysql地址 | 192.168.1.66:3306 |
|
||||||
| -----RUSTDESK配置----- | --------------- | ---------- |
|
| RUSTDESK_API_MYSQL_DBNAME | mysql数据库名 | rustdesk |
|
||||||
| RUSTDESK_API_RUSTDESK_ID_SERVER | Rustdesk的id服务器地址 | 192.168.1.66:21116 |
|
| -----RUSTDESK配置----- | --------------- | ---------- |
|
||||||
| RUSTDESK_API_RUSTDESK_RELAY_SERVER | Rustdesk的relay服务器地址 | 192.168.1.66:21117 |
|
| RUSTDESK_API_RUSTDESK_ID_SERVER | Rustdesk的id服务器地址 | 192.168.1.66:21116 |
|
||||||
| RUSTDESK_API_RUSTDESK_API_SERVER | Rustdesk的api服务器地址 | http://192.168.1.66:21114 |
|
| RUSTDESK_API_RUSTDESK_RELAY_SERVER | Rustdesk的relay服务器地址 | 192.168.1.66:21117 |
|
||||||
| RUSTDESK_API_RUSTDESK_KEY | Rustdesk的key | 123456789 |
|
| RUSTDESK_API_RUSTDESK_API_SERVER | Rustdesk的api服务器地址 | http://192.168.1.66:21114 |
|
||||||
| RUSTDESK_API_RUSTDESK_KEY_FILE | Rustdesk存放key的文件 | `./conf/data/id_ed25519.pub` |
|
| RUSTDESK_API_RUSTDESK_KEY | Rustdesk的key | 123456789 |
|
||||||
| ----PROXY配置----- | --------------- | ---------- |
|
| RUSTDESK_API_RUSTDESK_KEY_FILE | Rustdesk存放key的文件 | `./conf/data/id_ed25519.pub` |
|
||||||
| RUSTDESK_API_PROXY_ENABLE | 是否启用代理:`false`, `true` | `false` |
|
| RUSTDESK_API_RUSTDESK_WEBCLIENT_MAGIC_QUERYONLINE | Web client v2 中是否启用新的在线状态查询方法; `1`:启用,`0`:不启用,默认不启用 | `0` |
|
||||||
| RUSTDESK_API_PROXY_HOST | 代理地址 | `http://127.0.0.1:1080` |
|
| ----PROXY配置----- | --------------- | ---------- |
|
||||||
|
| RUSTDESK_API_PROXY_ENABLE | 是否启用代理:`false`, `true` | `false` |
|
||||||
|
| RUSTDESK_API_PROXY_HOST | 代理地址 | `http://127.0.0.1:1080` |
|
||||||
|
| ----JWT配置---- | -------- | -------- |
|
||||||
|
| RUSTDESK_API_JWT_KEY | 自定义JWT KEY,为空则不启用JWT | |
|
||||||
|
| RUSTDESK_API_JWT_EXPIRE_DURATION | JWT有效时间 | 360000 |
|
||||||
|
|
||||||
|
|
||||||
### 运行
|
### 运行
|
||||||
@@ -263,7 +240,7 @@ proxy:
|
|||||||
lejianwen/rustdesk-api
|
lejianwen/rustdesk-api
|
||||||
```
|
```
|
||||||
|
|
||||||
2. 使用`docker compose`,参考[wiki](https://github.com/lejianwen/rustdesk-api/wiki)
|
2. 使用`docker compose`,参考[WIKI](https://github.com/lejianwen/rustdesk-api/wiki)
|
||||||
|
|
||||||
#### 下载release直接运行
|
#### 下载release直接运行
|
||||||
|
|
||||||
@@ -308,7 +285,49 @@ proxy:
|
|||||||
6. 打开浏览器访问`http://<your server[:port]>/_admin/`,默认用户名密码为`admin`,请及时更改密码。
|
6. 打开浏览器访问`http://<your server[:port]>/_admin/`,默认用户名密码为`admin`,请及时更改密码。
|
||||||
|
|
||||||
|
|
||||||
|
#### 使用我fork后的server-s6镜像运行
|
||||||
|
|
||||||
|
- github https://github.com/lejianwen/rustdesk-server
|
||||||
|
- docker hub https://hub.docker.com/r/lejianwen/rustdesk-server-s6
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
networks:
|
||||||
|
rustdesk-net:
|
||||||
|
external: false
|
||||||
|
services:
|
||||||
|
rustdesk:
|
||||||
|
ports:
|
||||||
|
- 21114:21114
|
||||||
|
- 21115:21115
|
||||||
|
- 21116:21116
|
||||||
|
- 21116:21116/udp
|
||||||
|
- 21117:21117
|
||||||
|
- 21118:21118
|
||||||
|
- 21119:21119
|
||||||
|
image: lejianwen/rustdesk-server-s6:latest
|
||||||
|
environment:
|
||||||
|
- RELAY=<relay_server[:port]>
|
||||||
|
- ENCRYPTED_ONLY=1
|
||||||
|
- MUST_LOGIN=N
|
||||||
|
- TZ=Asia/Shanghai
|
||||||
|
- RUSTDESK_API_RUSTDESK_ID_SERVER=<id_server[:21116]>
|
||||||
|
- RUSTDESK_API_RUSTDESK_RELAY_SERVER=<relay_server[:21117]>
|
||||||
|
- RUSTDESK_API_RUSTDESK_API_SERVER=http://<api_server[:21114]>
|
||||||
|
- RUSTDESK_API_KEY_FILE=/data/id_ed25519.pub
|
||||||
|
- RUSTDESK_API_JWT_KEY=xxxxxx # jwt key
|
||||||
|
volumes:
|
||||||
|
- /data/rustdesk/server:/data
|
||||||
|
- /data/rustdesk/api:/app/data #将数据库挂载
|
||||||
|
networks:
|
||||||
|
- rustdesk-net
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
## 其他
|
## 其他
|
||||||
|
|
||||||
|
- [WIKI](https://github.com/lejianwen/rustdesk-api/wiki)
|
||||||
|
- [链接超时问题](https://github.com/lejianwen/rustdesk-api/issues/92)
|
||||||
- [修改客户端ID](https://github.com/abdullah-erturk/RustDesk-ID-Changer)
|
- [修改客户端ID](https://github.com/abdullah-erturk/RustDesk-ID-Changer)
|
||||||
- [webclient来源](https://hub.docker.com/r/keyurbhole/flutter_web_desk)
|
- [webclient来源](https://hub.docker.com/r/keyurbhole/flutter_web_desk)
|
||||||
192
README_EN.md
@@ -33,6 +33,7 @@ desktop software that provides self-hosted solutions.
|
|||||||
- Quick access to web client
|
- Quick access to web client
|
||||||
- i18n
|
- i18n
|
||||||
- Share to guest by web client
|
- Share to guest by web client
|
||||||
|
- Server control (some simple official commands [WIKI](https://github.com/lejianwen/rustdesk-api/wiki/Rustdesk-Command))
|
||||||
- Web Client
|
- Web Client
|
||||||
- Automatically obtain API server
|
- Automatically obtain API server
|
||||||
- Automatically obtain ID server and KEY
|
- Automatically obtain ID server and KEY
|
||||||
@@ -40,52 +41,6 @@ desktop software that provides self-hosted solutions.
|
|||||||
- Visitors are remotely to the device via a temporary sharing link
|
- Visitors are remotely to the device via a temporary sharing link
|
||||||
- CLI
|
- CLI
|
||||||
- Reset admin password
|
- Reset admin password
|
||||||
## Prerequisites
|
|
||||||
|
|
||||||
### [Rustdesk](https://github.com/rustdesk/rustdesk)
|
|
||||||
|
|
||||||
|
|
||||||
#### The PC client uses version ***1.3.0***, and versions ***1.2.6+*** have been tested to work.
|
|
||||||
|
|
||||||
#### Solutions for PC client connection timeout or connection issues
|
|
||||||
##### Connection issues or timeouts
|
|
||||||
Because the server version lags behind the client version, the server does not respond to the client's `secure_tcp` request, causing the client to timeout.
|
|
||||||
Relevant code can be found at `https://github.com/rustdesk/rustdesk/blob/master/src/client.rs#L322`
|
|
||||||
```rust
|
|
||||||
if !key.is_empty() && !token.is_empty() {
|
|
||||||
// mainly for the security of token
|
|
||||||
allow_err!(secure_tcp(&mut socket, key).await);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
As seen, when both `key` and `token` are not empty, `secure_tcp` is called, but the server does not respond, causing the client to timeout.
|
|
||||||
The `secure_tcp` code is located at `https://github.com/rustdesk/rustdesk/blob/master/src/common.rs#L1203`
|
|
||||||
|
|
||||||
##### Four Solutions
|
|
||||||
1. Specify the key on the server.
|
|
||||||
- Advantage: Simple
|
|
||||||
- Disadvantage: The connection is not encrypted
|
|
||||||
```bash
|
|
||||||
hbbs -r <relay-server-ip[:port]> -k <key>
|
|
||||||
hbbr -k <key>
|
|
||||||
```
|
|
||||||
For example
|
|
||||||
```bash
|
|
||||||
hbbs -r <relay-server-ip[:port]> -k abc1234567
|
|
||||||
hbbr -k abc1234567
|
|
||||||
```
|
|
||||||
2. Use a system-generated key or a custom key pair on the server. If the client is already logged in, it may timeout or fail to connect. Logging out and reconnecting usually resolves the issue, and the web client does not need to log out.
|
|
||||||
- Advantage: Encrypted connection
|
|
||||||
- Disadvantage: Complicated operation
|
|
||||||
3. Use a system-generated key or a custom key pair on the server, fork the official client code to modify `secure_tcp` to return directly, then compile using `Github Actions` and download the compiled client.
|
|
||||||
Refer to [official documentation](https://rustdesk.com/docs/en/dev/build/all/)
|
|
||||||
- Advantage: Encrypted connection, customizable client features, ready to use after compilation
|
|
||||||
- Disadvantage: Requires forking code and compiling, which can be challenging
|
|
||||||
4. Use [my forked code](https://github.com/lejianwen/rustdesk), which has already modified `secure_tcp`. You can download and use it directly from [here](https://github.com/lejianwen/rustdesk/releases)
|
|
||||||
- Advantage: Code changes are viewable, compiled with `Github Actions`, encrypted connection, ready to use
|
|
||||||
- Disadvantage: May not keep up with official version updates
|
|
||||||
|
|
||||||
***If encryption is not a high priority, use `1`. If encryption is important, use `3` or `4`.***
|
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
@@ -124,12 +79,11 @@ installation are `admin` `admin`, please change the password immediately.
|
|||||||
In the top right corner, you can change the password, switch languages, and toggle between `day/night` mode.
|
In the top right corner, you can change the password, switch languages, and toggle between `day/night` mode.
|
||||||
|
|
||||||

|

|
||||||
3. Groups can be customized for easy management. Currently, two types are supported: `shared group` and `regular group`.
|
3. Each user can have multiple address books, which can also be shared with other users.
|
||||||

|
4. Groups can be customized for easy management. Currently, two types are supported: `shared group` and `regular group`.
|
||||||
4. You can directly launch the client or open the web client for convenience; you can also share it with guests, who can remotely access the device via the web client.
|
5. You can directly launch the client or open the web client for convenience; you can also share it with guests, who can remotely access the device via the web client.
|
||||||
|
|
||||||

|

|
||||||
5. OAuth support: Currently, `GitHub`, `Google` and `OIDC` are supported. You need to create an `OAuth App` and configure it in
|
6. OAuth support: Currently, `GitHub`, `Google` and `OIDC` are supported. You need to create an `OAuth App` and configure it in
|
||||||
the admin panel.
|
the admin panel.
|
||||||

|

|
||||||
- For `Google` and `Github`, you don't need to fill the `Issuer` and `Scpoes`
|
- For `Google` and `Github`, you don't need to fill the `Issuer` and `Scpoes`
|
||||||
@@ -138,22 +92,34 @@ installation are `admin` `admin`, please change the password immediately.
|
|||||||
at `Settings` -> `Developer settings` -> `OAuth Apps` -> `New OAuth App` [here](https://github.com/settings/developers).
|
at `Settings` -> `Developer settings` -> `OAuth Apps` -> `New OAuth App` [here](https://github.com/settings/developers).
|
||||||
- Set the `Authorization callback URL` to `http://<your server[:port]>/api/oauth/callback`,
|
- Set the `Authorization callback URL` to `http://<your server[:port]>/api/oauth/callback`,
|
||||||
e.g., `http://127.0.0.1:21114/api/oauth/callback`.
|
e.g., `http://127.0.0.1:21114/api/oauth/callback`.
|
||||||
|
|
||||||
|
7. Login logs
|
||||||
|
8. Connection logs
|
||||||
|
9. File transfer logs
|
||||||
|
10. Server control
|
||||||
|
|
||||||
|
- `Simple mode`, some simple commands have been GUI-ized and can be executed directly in the backend
|
||||||
|

|
||||||
|
|
||||||
|
- `Advanced mode`, commands can be executed directly in the backend
|
||||||
|
* Official commands can be used
|
||||||
|
* Custom commands can be added
|
||||||
|
* Custom commands can be executed
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Web Client:
|
### Web Client:
|
||||||
|
|
||||||
1. If you're already logged into the admin panel, the web client will log in automatically.
|
1. If you're already logged into the admin panel, the web client will log in automatically.
|
||||||
2. If you're not logged in, simply click the login button in the top right corner, and the API server will be
|
2. If you're not logged in, simply click the login button in the top right corner, and the API server will be
|
||||||
pre-configured.
|
pre-configured.
|
||||||

|
|
||||||
3. After logging in, the ID server and key will be automatically synced.
|
3. After logging in, the ID server and key will be automatically synced.
|
||||||
4. The address book will also be automatically saved to the web client for convenient use.
|
4. The address book will also be automatically saved to the web client for convenient use.
|
||||||
5. Now supports `v2 Preview`, accessible at `/webclient2`
|
5. Now supports `v2 Preview`, accessible at `/webclient2`
|
||||||

|

|
||||||
6. `v2 preview` deployment
|
6. `v2 preview` deployment, [WIKI](https://github.com/lejianwen/rustdesk-api/wiki)
|
||||||
- If deploying via `https` on port `443`, you must configure a reverse proxy. Refer to the [official documentation](https://rustdesk.com/docs/en/self-host/rustdesk-server-pro/faq/#8-add-websocket-secure-wss-support-for-the-id-server-and-relay-server-to-enable-secure-communication-for-the-web-client)
|
|
||||||
- If deploying via `http` or other `https` ports, configure `21118` and `21119` as with `v1`
|
|
||||||
- More [Web-Client-V2-Preview-Document](https://github.com/lejianwen/rustdesk-api/wiki/Web-Client-V2-Preview-Document)
|
|
||||||
|
|
||||||
|
|
||||||
### Automated Documentation : API documentation is generated using Swag, making it easier for developers to understand and use the API.
|
### Automated Documentation : API documentation is generated using Swag, making it easier for developers to understand and use the API.
|
||||||
|
|
||||||
@@ -185,6 +151,7 @@ lang: "en"
|
|||||||
app:
|
app:
|
||||||
web-client: 1 # web client route 1:open 0:close
|
web-client: 1 # web client route 1:open 0:close
|
||||||
register: false #register enable
|
register: false #register enable
|
||||||
|
show-swagger: 0 #show swagger 1:open 0:close
|
||||||
gin:
|
gin:
|
||||||
api-addr: "0.0.0.0:21114"
|
api-addr: "0.0.0.0:21114"
|
||||||
mode: "release"
|
mode: "release"
|
||||||
@@ -212,42 +179,50 @@ logger:
|
|||||||
proxy:
|
proxy:
|
||||||
enable: false
|
enable: false
|
||||||
host: ""
|
host: ""
|
||||||
|
jwt:
|
||||||
|
key: ""
|
||||||
|
expire-duration: 360000
|
||||||
```
|
```
|
||||||
|
|
||||||
### Environment Variables
|
### Environment Variables
|
||||||
The prefix for variable names is `RUSTDESK_API`. If environment variables exist, they will override the configurations in the configuration file.
|
The prefix for variable names is `RUSTDESK_API`. If environment variables exist, they will override the configurations in the configuration file.
|
||||||
|
|
||||||
| Variable Name | Description | Example |
|
| Variable Name | Description | Example |
|
||||||
|------------------------------------|-------------------------------------------------------------------------|-------------------------------|
|
|---------------------------------------------------|--------------------------------------------------------------------------------------------------------------|-------------------------------|
|
||||||
| TZ | timezone | Asia/Shanghai |
|
| TZ | timezone | Asia/Shanghai |
|
||||||
| RUSTDESK_API_LANG | Language | `en`,`zh-CN` |
|
| RUSTDESK_API_LANG | Language | `en`,`zh-CN` |
|
||||||
| RUSTDESK_API_APP_WEB_CLIENT | web client on/off; 1: on, 0 off, deault 1 | 1 |
|
| RUSTDESK_API_APP_WEB_CLIENT | web client on/off; 1: on, 0 off, default: 1 | 1 |
|
||||||
| RUSTDESK_API_APP_REGISTER | register enable; `true`, `false`; default:`false` | `false` |
|
| RUSTDESK_API_APP_REGISTER | register enable; `true`, `false`; default:`false` | `false` |
|
||||||
| ----- ADMIN Configuration----- | ---------- | ---------- |
|
| RUSTDESK_API_APP_SHOW_SWAGGER | swagger visible; 1: yes, 0: no; default: 0 | `0` |
|
||||||
| RUSTDESK_API_ADMIN_TITLE | Admin Title | `RustDesk Api Admin` |
|
| ----- ADMIN Configuration----- | ---------- | ---------- |
|
||||||
| RUSTDESK_API_ADMIN_HELLO | Admin welcome message, you can use `html` | |
|
| RUSTDESK_API_ADMIN_TITLE | Admin Title | `RustDesk Api Admin` |
|
||||||
| RUSTDESK_API_ADMIN_HELLO_FILE | Admin welcome message file,<br>will override `RUSTDESK_API_ADMIN_HELLO` | `./conf/admin/hello.html` |
|
| RUSTDESK_API_ADMIN_HELLO | Admin welcome message, you can use `html` | |
|
||||||
| ----- GIN Configuration ----- | --------------------------------------- | ----------------------------- |
|
| RUSTDESK_API_ADMIN_HELLO_FILE | Admin welcome message file,<br>will override `RUSTDESK_API_ADMIN_HELLO` | `./conf/admin/hello.html` |
|
||||||
| RUSTDESK_API_GIN_TRUST_PROXY | Trusted proxy IPs, separated by commas. | 192.168.1.2,192.168.1.3 |
|
| ----- GIN Configuration ----- | --------------------------------------- | ----------------------------- |
|
||||||
| ----- GORM Configuration ----- | --------------------------------------- | ----------------------------- |
|
| RUSTDESK_API_GIN_TRUST_PROXY | Trusted proxy IPs, separated by commas. | 192.168.1.2,192.168.1.3 |
|
||||||
| RUSTDESK_API_GORM_TYPE | Database type (`sqlite` or `mysql`). Default is `sqlite`. | sqlite |
|
| ----- GORM Configuration ----- | --------------------------------------- | ----------------------------- |
|
||||||
| RUSTDESK_API_GORM_MAX_IDLE_CONNS | Maximum idle connections | 10 |
|
| RUSTDESK_API_GORM_TYPE | Database type (`sqlite` or `mysql`). Default is `sqlite`. | sqlite |
|
||||||
| RUSTDESK_API_GORM_MAX_OPEN_CONNS | Maximum open connections | 100 |
|
| RUSTDESK_API_GORM_MAX_IDLE_CONNS | Maximum idle connections | 10 |
|
||||||
| RUSTDESK_API_RUSTDESK_PERSONAL | Open Personal Api 1:Enable,0:Disable | 1 |
|
| RUSTDESK_API_GORM_MAX_OPEN_CONNS | Maximum open connections | 100 |
|
||||||
| ----- MYSQL Configuration ----- | --------------------------------------- | ----------------------------- |
|
| RUSTDESK_API_RUSTDESK_PERSONAL | Open Personal Api 1:Enable,0:Disable | 1 |
|
||||||
| RUSTDESK_API_MYSQL_USERNAME | MySQL username | root |
|
| ----- MYSQL Configuration ----- | --------------------------------------- | ----------------------------- |
|
||||||
| RUSTDESK_API_MYSQL_PASSWORD | MySQL password | 111111 |
|
| RUSTDESK_API_MYSQL_USERNAME | MySQL username | root |
|
||||||
| RUSTDESK_API_MYSQL_ADDR | MySQL address | 192.168.1.66:3306 |
|
| RUSTDESK_API_MYSQL_PASSWORD | MySQL password | 111111 |
|
||||||
| RUSTDESK_API_MYSQL_DBNAME | MySQL database name | rustdesk |
|
| RUSTDESK_API_MYSQL_ADDR | MySQL address | 192.168.1.66:3306 |
|
||||||
| ----- RUSTDESK Configuration ----- | --------------------------------------- | ----------------------------- |
|
| RUSTDESK_API_MYSQL_DBNAME | MySQL database name | rustdesk |
|
||||||
| RUSTDESK_API_RUSTDESK_ID_SERVER | Rustdesk ID server address | 192.168.1.66:21116 |
|
| ----- RUSTDESK Configuration ----- | --------------------------------------- | ----------------------------- |
|
||||||
| RUSTDESK_API_RUSTDESK_RELAY_SERVER | Rustdesk relay server address | 192.168.1.66:21117 |
|
| RUSTDESK_API_RUSTDESK_ID_SERVER | Rustdesk ID server address | 192.168.1.66:21116 |
|
||||||
| RUSTDESK_API_RUSTDESK_API_SERVER | Rustdesk API server address | http://192.168.1.66:21114 |
|
| RUSTDESK_API_RUSTDESK_RELAY_SERVER | Rustdesk relay server address | 192.168.1.66:21117 |
|
||||||
| RUSTDESK_API_RUSTDESK_KEY | Rustdesk key | 123456789 |
|
| RUSTDESK_API_RUSTDESK_API_SERVER | Rustdesk API server address | http://192.168.1.66:21114 |
|
||||||
| RUSTDESK_API_RUSTDESK_KEY_FILE | Rustdesk存放key的文件 | `./conf/data/id_ed25519.pub` |
|
| RUSTDESK_API_RUSTDESK_KEY | Rustdesk key | 123456789 |
|
||||||
| ---- PROXY ----- | --------------- | ---------- |
|
| RUSTDESK_API_RUSTDESK_KEY_FILE | Rustdesk key file | `./conf/data/id_ed25519.pub` |
|
||||||
| RUSTDESK_API_PROXY_ENABLE | proxy_enable :`false`, `true` | `false` |
|
| RUSTDESK_API_RUSTDESK_WEBCLIENT_MAGIC_QUERYONLINE | New online query method is enabled in the web client v2; '1': Enabled, '0': Disabled, not enabled by default | `0` |
|
||||||
| RUSTDESK_API_PROXY_HOST | proxy_host | `http://127.0.0.1:1080` |
|
| ---- PROXY ----- | --------------- | ---------- |
|
||||||
|
| RUSTDESK_API_PROXY_ENABLE | proxy_enable :`false`, `true` | `false` |
|
||||||
|
| RUSTDESK_API_PROXY_HOST | proxy_host | `http://127.0.0.1:1080` |
|
||||||
|
| ----JWT---- | -------- | -------- |
|
||||||
|
| RUSTDESK_API_JWT_KEY | JWT KEY. Set empty to disable jwt | |
|
||||||
|
| RUSTDESK_API_JWT_EXPIRE_DURATION | JWT expire duration | 360000 |
|
||||||
|
|
||||||
### Installation Steps
|
### Installation Steps
|
||||||
|
|
||||||
@@ -267,7 +242,7 @@ The prefix for variable names is `RUSTDESK_API`. If environment variables exist,
|
|||||||
lejianwen/rustdesk-api
|
lejianwen/rustdesk-api
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Using `docker-compose`,look [wiki](https://github.com/lejianwen/rustdesk-api/wiki)
|
2. Using `docker-compose`,look [WIKI](https://github.com/lejianwen/rustdesk-api/wiki)
|
||||||
|
|
||||||
#### Running from Release
|
#### Running from Release
|
||||||
|
|
||||||
@@ -316,8 +291,47 @@ Download the release from [release](https://github.com/lejianwen/rustdesk-api/re
|
|||||||
6. Open your browser and visit `http://<your server[:port]>/_admin/`, with default credentials `admin admin`. Please
|
6. Open your browser and visit `http://<your server[:port]>/_admin/`, with default credentials `admin admin`. Please
|
||||||
change the password promptly.
|
change the password promptly.
|
||||||
|
|
||||||
|
#### Running with my forked server-s6 image
|
||||||
|
|
||||||
|
- github https://github.com/lejianwen/rustdesk-server
|
||||||
|
- docker hub https://hub.docker.com/r/lejianwen/rustdesk-server-s6
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
networks:
|
||||||
|
rustdesk-net:
|
||||||
|
external: false
|
||||||
|
services:
|
||||||
|
rustdesk:
|
||||||
|
ports:
|
||||||
|
- 21114:21114
|
||||||
|
- 21115:21115
|
||||||
|
- 21116:21116
|
||||||
|
- 21116:21116/udp
|
||||||
|
- 21117:21117
|
||||||
|
- 21118:21118
|
||||||
|
- 21119:21119
|
||||||
|
image: lejianwen/rustdesk-server-s6:latest
|
||||||
|
environment:
|
||||||
|
- RELAY=<relay_server[:port]>
|
||||||
|
- ENCRYPTED_ONLY=1
|
||||||
|
- MUST_LOGIN=N
|
||||||
|
- TZ=Asia/Shanghai
|
||||||
|
- RUSTDESK_API_RUSTDESK_ID_SERVER=<id_server[:21116]>
|
||||||
|
- RUSTDESK_API_RUSTDESK_RELAY_SERVER=<relay_server[:21117]>
|
||||||
|
- RUSTDESK_API_RUSTDESK_API_SERVER=http://<api_server[:21114]>
|
||||||
|
- RUSTDESK_API_KEY_FILE=/data/id_ed25519.pub
|
||||||
|
- RUSTDESK_API_JWT_KEY=xxxxxx # jwt key
|
||||||
|
volumes:
|
||||||
|
- /data/rustdesk/server:/data
|
||||||
|
- /data/rustdesk/api:/app/data #将数据库挂载
|
||||||
|
networks:
|
||||||
|
- rustdesk-net
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
```
|
||||||
## Others
|
## Others
|
||||||
|
|
||||||
|
- [WIKI](https://github.com/lejianwen/rustdesk-api/wiki)
|
||||||
|
- [Connection Timeout](https://github.com/lejianwen/rustdesk-api/issues/92)
|
||||||
- [Change client ID](https://github.com/abdullah-erturk/RustDesk-ID-Changer)
|
- [Change client ID](https://github.com/abdullah-erturk/RustDesk-ID-Changer)
|
||||||
- [Web client source](https://hub.docker.com/r/keyurbhole/flutter_web_desk)
|
- [Web client source](https://hub.docker.com/r/keyurbhole/flutter_web_desk)
|
||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"Gwen/global"
|
"Gwen/global"
|
||||||
"Gwen/http"
|
"Gwen/http"
|
||||||
"Gwen/lib/cache"
|
"Gwen/lib/cache"
|
||||||
|
"Gwen/lib/jwt"
|
||||||
"Gwen/lib/lock"
|
"Gwen/lib/lock"
|
||||||
"Gwen/lib/logger"
|
"Gwen/lib/logger"
|
||||||
"Gwen/lib/orm"
|
"Gwen/lib/orm"
|
||||||
@@ -17,6 +18,7 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// @title 管理系统API
|
// @title 管理系统API
|
||||||
@@ -100,9 +102,6 @@ func InitGlobal() {
|
|||||||
//配置解析
|
//配置解析
|
||||||
global.Viper = config.Init(&global.Config, global.ConfigPath)
|
global.Viper = config.Init(&global.Config, global.ConfigPath)
|
||||||
|
|
||||||
//从配置文件中加载密钥
|
|
||||||
config.LoadKeyFile(&global.Config.Rustdesk)
|
|
||||||
|
|
||||||
//日志
|
//日志
|
||||||
global.Logger = logger.New(&logger.Config{
|
global.Logger = logger.New(&logger.Config{
|
||||||
Path: global.Config.Logger.Path,
|
Path: global.Config.Logger.Path,
|
||||||
@@ -163,13 +162,13 @@ func InitGlobal() {
|
|||||||
|
|
||||||
//jwt
|
//jwt
|
||||||
//fmt.Println(global.Config.Jwt.PrivateKey)
|
//fmt.Println(global.Config.Jwt.PrivateKey)
|
||||||
//global.Jwt = jwt.NewJwt(global.Config.Jwt.PrivateKey, global.Config.Jwt.ExpireDuration*time.Second)
|
global.Jwt = jwt.NewJwt(global.Config.Jwt.Key, global.Config.Jwt.ExpireDuration*time.Second)
|
||||||
|
|
||||||
//locker
|
//locker
|
||||||
global.Lock = lock.NewLocal()
|
global.Lock = lock.NewLocal()
|
||||||
}
|
}
|
||||||
func DatabaseAutoUpdate() {
|
func DatabaseAutoUpdate() {
|
||||||
version := 246
|
version := 260
|
||||||
|
|
||||||
db := global.DB
|
db := global.DB
|
||||||
|
|
||||||
@@ -253,6 +252,7 @@ func Migrate(version uint) {
|
|||||||
&model.AuditFile{},
|
&model.AuditFile{},
|
||||||
&model.AddressBookCollection{},
|
&model.AddressBookCollection{},
|
||||||
&model.AddressBookCollectionRule{},
|
&model.AddressBookCollectionRule{},
|
||||||
|
&model.ServerCmd{},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("migrate err :=>", err)
|
fmt.Println("migrate err :=>", err)
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
👏👏👏 你好 <strong>{{username}}</strong>, 欢迎使用 <a href='https://github.com/lejianwen/rustdesk-api' target='_blank'>RustDesk Api Admin</a>
|
### 👏👏👏 你好 ***{{username}}***, 欢迎使用 [RustDesk Api](https://github.com/lejianwen/rustdesk-api)
|
||||||
@@ -2,6 +2,7 @@ lang: "zh-CN"
|
|||||||
app:
|
app:
|
||||||
web-client: 1 # 1:启用 0:禁用
|
web-client: 1 # 1:启用 0:禁用
|
||||||
register: false #是否开启注册
|
register: false #是否开启注册
|
||||||
|
show-swagger: 0 # 1:启用 0:禁用
|
||||||
admin:
|
admin:
|
||||||
title: "RustDesk Api Admin"
|
title: "RustDesk Api Admin"
|
||||||
hello-file: "./conf/admin/hello.html" #优先使用file
|
hello-file: "./conf/admin/hello.html" #优先使用file
|
||||||
@@ -25,8 +26,9 @@ rustdesk:
|
|||||||
relay-server: "192.168.1.66:21117"
|
relay-server: "192.168.1.66:21117"
|
||||||
api-server: "http://127.0.0.1:21114"
|
api-server: "http://127.0.0.1:21114"
|
||||||
key: ""
|
key: ""
|
||||||
key-file: "./conf/data/id_ed25519.pub"
|
key-file: "/data/id_ed25519.pub"
|
||||||
personal: 1
|
personal: 1
|
||||||
|
webclient-magic-queryonline: 0
|
||||||
logger:
|
logger:
|
||||||
path: "./runtime/log.txt"
|
path: "./runtime/log.txt"
|
||||||
level: "warn" #trace,debug,info,warn,error,fatal
|
level: "warn" #trace,debug,info,warn,error,fatal
|
||||||
@@ -34,6 +36,9 @@ logger:
|
|||||||
proxy:
|
proxy:
|
||||||
enable: false
|
enable: false
|
||||||
host: "http://127.0.0.1:1080"
|
host: "http://127.0.0.1:1080"
|
||||||
|
jwt:
|
||||||
|
key: ""
|
||||||
|
expire-duration: 360000
|
||||||
redis:
|
redis:
|
||||||
addr: "127.0.0.1:6379"
|
addr: "127.0.0.1:6379"
|
||||||
password: ""
|
password: ""
|
||||||
@@ -51,6 +56,4 @@ oss:
|
|||||||
callback-url: ""
|
callback-url: ""
|
||||||
expire-time: 30
|
expire-time: 30
|
||||||
max-byte: 10240
|
max-byte: 10240
|
||||||
jwt:
|
|
||||||
private-key: "./conf/jwt_pri.pem"
|
|
||||||
expire-duration: 360000
|
|
||||||
|
|||||||
@@ -14,8 +14,9 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type App struct {
|
type App struct {
|
||||||
WebClient int `mapstructure:"web-client"`
|
WebClient int `mapstructure:"web-client"`
|
||||||
Register bool `mapstructure:"register"`
|
Register bool `mapstructure:"register"`
|
||||||
|
ShowSwagger int `mapstructure:"show-swagger"`
|
||||||
}
|
}
|
||||||
type Admin struct {
|
type Admin struct {
|
||||||
Title string `mapstructure:"title"`
|
Title string `mapstructure:"title"`
|
||||||
@@ -39,7 +40,7 @@ type Config struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Init 初始化配置
|
// Init 初始化配置
|
||||||
func Init(rowVal interface{}, path string) *viper.Viper {
|
func Init(rowVal *Config, path string) *viper.Viper {
|
||||||
if path == "" {
|
if path == "" {
|
||||||
path = DefaultConfig
|
path = DefaultConfig
|
||||||
}
|
}
|
||||||
@@ -60,11 +61,14 @@ func Init(rowVal interface{}, path string) *viper.Viper {
|
|||||||
if err2 := v.Unmarshal(rowVal); err2 != nil {
|
if err2 := v.Unmarshal(rowVal); err2 != nil {
|
||||||
fmt.Println(err2)
|
fmt.Println(err2)
|
||||||
}
|
}
|
||||||
|
rowVal.Rustdesk.LoadKeyFile()
|
||||||
|
rowVal.Rustdesk.ParsePort()
|
||||||
})
|
})
|
||||||
if err := v.Unmarshal(rowVal); err != nil {
|
if err := v.Unmarshal(rowVal); err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
}
|
}
|
||||||
|
rowVal.Rustdesk.LoadKeyFile()
|
||||||
|
rowVal.Rustdesk.ParsePort()
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,6 @@ package config
|
|||||||
import "time"
|
import "time"
|
||||||
|
|
||||||
type Jwt struct {
|
type Jwt struct {
|
||||||
PrivateKey string `mapstructure:"private-key"`
|
Key string `mapstructure:"key"`
|
||||||
ExpireDuration time.Duration `mapstructure:"expire-duration"`
|
ExpireDuration time.Duration `mapstructure:"expire-duration"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,29 +2,56 @@ package config
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
DefaultIdServerPort = 21116
|
||||||
|
DefaultRelayServerPort = 21117
|
||||||
)
|
)
|
||||||
|
|
||||||
type Rustdesk struct {
|
type Rustdesk struct {
|
||||||
IdServer string `mapstructure:"id-server"`
|
IdServer string `mapstructure:"id-server"`
|
||||||
RelayServer string `mapstructure:"relay-server"`
|
IdServerPort int `mapstructure:"-"`
|
||||||
ApiServer string `mapstructure:"api-server"`
|
RelayServer string `mapstructure:"relay-server"`
|
||||||
Key string `mapstructure:"key"`
|
RelayServerPort int `mapstructure:"-"`
|
||||||
KeyFile string `mapstructure:"key-file"`
|
ApiServer string `mapstructure:"api-server"`
|
||||||
Personal int `mapstructure:"personal"`
|
Key string `mapstructure:"key"`
|
||||||
|
KeyFile string `mapstructure:"key-file"`
|
||||||
|
Personal int `mapstructure:"personal"`
|
||||||
|
//webclient-magic-queryonline
|
||||||
|
WebclientMagicQueryonline int `mapstructure:"webclient-magic-queryonline"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadKeyFile(rustdesk *Rustdesk) {
|
func (rd *Rustdesk) LoadKeyFile() {
|
||||||
// Load key file
|
// Load key file
|
||||||
if rustdesk.Key != "" {
|
if rd.Key != "" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if rustdesk.KeyFile != "" {
|
if rd.KeyFile != "" {
|
||||||
// Load key from file
|
// Load key from file
|
||||||
b, err := os.ReadFile(rustdesk.KeyFile)
|
b, err := os.ReadFile(rd.KeyFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
rustdesk.Key = string(b)
|
rd.Key = string(b)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
func (rd *Rustdesk) ParsePort() {
|
||||||
|
// Parse port
|
||||||
|
idres := strings.Split(rd.IdServer, ":")
|
||||||
|
if len(idres) == 1 {
|
||||||
|
rd.IdServerPort = DefaultIdServerPort
|
||||||
|
} else if len(idres) == 2 {
|
||||||
|
rd.IdServerPort, _ = strconv.Atoi(idres[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
relayres := strings.Split(rd.RelayServer, ":")
|
||||||
|
if len(relayres) == 1 {
|
||||||
|
rd.RelayServerPort = DefaultRelayServerPort
|
||||||
|
} else if len(relayres) == 2 {
|
||||||
|
rd.RelayServerPort, _ = strconv.Atoi(relayres[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
5
debian/changelog
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
rustdesk-api-server (1.3.6) UNRELEASED; urgency=medium
|
||||||
|
|
||||||
|
* Update the version to 1.3.6 to match the client.
|
||||||
|
|
||||||
|
-- rustdesk-api <ymwlpoolc@qq.com> Tue, 24 Dec 2024 13:48:34 +0800
|
||||||
1
debian/compat
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
10
|
||||||
13
debian/control.tpl
vendored
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
Source: rustdesk-api-server
|
||||||
|
Section: net
|
||||||
|
Priority: optional
|
||||||
|
Maintainer: ymwl <ymwlpoolc@qq.com>
|
||||||
|
Build-Depends: debhelper (>= 10), pkg-config
|
||||||
|
Standards-Version: 4.5.0
|
||||||
|
Homepage: https://github.com/lejianwen/rustdesk-api/
|
||||||
|
|
||||||
|
Package: rustdesk-api-server
|
||||||
|
Architecture: {{ ARCH }}
|
||||||
|
Depends: systemd ${misc:Depends}
|
||||||
|
Description: RustDesk api server
|
||||||
|
RustDesk api server, it is free and open source.
|
||||||
21
debian/copyright
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2024-present Lejianwen and contributors
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
6
debian/rules
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#!/usr/bin/make -f
|
||||||
|
%:
|
||||||
|
dh $@
|
||||||
|
|
||||||
|
override_dh_builddeb:
|
||||||
|
dh_builddeb -- -Zgzip
|
||||||
6
debian/rustdesk-api-server.install
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
bin/rustdesk-api usr/bin
|
||||||
|
systemd/rustdesk-api.service lib/systemd/system
|
||||||
|
conf var/lib/rustdesk-api
|
||||||
|
data var/lib/rustdesk-api
|
||||||
|
resources var/lib/rustdesk-api
|
||||||
|
runtime var/lib/rustdesk-api
|
||||||
28
debian/rustdesk-api-server.postinst
vendored
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
|
|
||||||
|
SERVICE=rustdesk-api.service
|
||||||
|
|
||||||
|
if [ "$1" = "configure" ]; then
|
||||||
|
mkdir -p /var/log/rustdesk-api
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$1" in
|
||||||
|
configure|abort-upgrade|abort-deconfigure|abort-remove)
|
||||||
|
mkdir -p /var/lib/rustdesk-api/
|
||||||
|
deb-systemd-helper unmask "${SERVICE}" >/dev/null || true
|
||||||
|
if deb-systemd-helper --quiet was-enabled "${SERVICE}"; then
|
||||||
|
deb-systemd-invoke enable "${SERVICE}" >/dev/null || true
|
||||||
|
else
|
||||||
|
deb-systemd-invoke update-state "${SERVICE}" >/dev/null || true
|
||||||
|
fi
|
||||||
|
systemctl --system daemon-reload >/dev/null || true
|
||||||
|
if [ -n "$2" ]; then
|
||||||
|
deb-systemd-invoke restart "${SERVICE}" >/dev/null || true
|
||||||
|
else
|
||||||
|
deb-systemd-invoke start "${SERVICE}" >/dev/null || true
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
exit 0
|
||||||
18
debian/rustdesk-api-server.postrm
vendored
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
|
|
||||||
|
SERVICE=rustdesk-api.service
|
||||||
|
|
||||||
|
systemctl --system daemon-reload >/dev/null || true
|
||||||
|
|
||||||
|
if [ "$1" = "purge" ]; then
|
||||||
|
rm -rf /var/log/rustdesk-api/rustdesk-api.*
|
||||||
|
deb-systemd-helper purge "${SERVICE}" >/dev/null || true
|
||||||
|
deb-systemd-helper unmask "${SERVICE}" >/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$1" = "remove" ]; then
|
||||||
|
deb-systemd-helper mask "${SERVICE}" >/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit 0
|
||||||
13
debian/rustdesk-api-server.prerm
vendored
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
|
|
||||||
|
SERVICE=rustdesk-api.service
|
||||||
|
|
||||||
|
case "$1" in
|
||||||
|
remove|deconfigure)
|
||||||
|
deb-systemd-invoke stop "${SERVICE}" >/dev/null || true
|
||||||
|
deb-systemd-invoke disable "${SERVICE}" >/dev/null || true
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
exit 0
|
||||||
1
debian/source/format
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
3.0 (native)
|
||||||
@@ -2,6 +2,8 @@ basePath: /api
|
|||||||
definitions:
|
definitions:
|
||||||
Gwen_http_request_admin.Login:
|
Gwen_http_request_admin.Login:
|
||||||
properties:
|
properties:
|
||||||
|
captcha:
|
||||||
|
type: string
|
||||||
password:
|
password:
|
||||||
type: string
|
type: string
|
||||||
platform:
|
platform:
|
||||||
@@ -773,7 +775,7 @@ info:
|
|||||||
title: 管理系统API
|
title: 管理系统API
|
||||||
version: "1.0"
|
version: "1.0"
|
||||||
paths:
|
paths:
|
||||||
/admin/address_book/create:
|
/admin/address_book/batchCreate:
|
||||||
post:
|
post:
|
||||||
consumes:
|
consumes:
|
||||||
- application/json
|
- application/json
|
||||||
@@ -806,6 +808,39 @@ paths:
|
|||||||
summary: 批量创建地址簿
|
summary: 批量创建地址簿
|
||||||
tags:
|
tags:
|
||||||
- 地址簿
|
- 地址簿
|
||||||
|
/admin/address_book/create:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 创建地址簿
|
||||||
|
parameters:
|
||||||
|
- description: 地址簿信息
|
||||||
|
in: body
|
||||||
|
name: body
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/admin.AddressBookForm'
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/definitions/response.Response'
|
||||||
|
- properties:
|
||||||
|
data:
|
||||||
|
$ref: '#/definitions/model.AddressBook'
|
||||||
|
type: object
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Response'
|
||||||
|
security:
|
||||||
|
- token: []
|
||||||
|
summary: 创建地址簿
|
||||||
|
tags:
|
||||||
|
- 地址簿
|
||||||
/admin/address_book/delete:
|
/admin/address_book/delete:
|
||||||
post:
|
post:
|
||||||
consumes:
|
consumes:
|
||||||
@@ -1975,6 +2010,460 @@ paths:
|
|||||||
summary: 登出
|
summary: 登出
|
||||||
tags:
|
tags:
|
||||||
- 登录
|
- 登录
|
||||||
|
/admin/my/address_book/create:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 创建地址簿
|
||||||
|
parameters:
|
||||||
|
- description: 地址簿信息
|
||||||
|
in: body
|
||||||
|
name: body
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/admin.AddressBookForm'
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/definitions/response.Response'
|
||||||
|
- properties:
|
||||||
|
data:
|
||||||
|
$ref: '#/definitions/model.AddressBook'
|
||||||
|
type: object
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Response'
|
||||||
|
security:
|
||||||
|
- token: []
|
||||||
|
summary: 创建地址簿
|
||||||
|
tags:
|
||||||
|
- 我的地址簿
|
||||||
|
/admin/my/address_book/delete:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 地址簿删除
|
||||||
|
parameters:
|
||||||
|
- description: 地址簿信息
|
||||||
|
in: body
|
||||||
|
name: body
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/admin.AddressBookForm'
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Response'
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Response'
|
||||||
|
security:
|
||||||
|
- token: []
|
||||||
|
summary: 地址簿删除
|
||||||
|
tags:
|
||||||
|
- 我的地址簿
|
||||||
|
/admin/my/address_book/list:
|
||||||
|
get:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 地址簿列表
|
||||||
|
parameters:
|
||||||
|
- description: 页码
|
||||||
|
in: query
|
||||||
|
name: page
|
||||||
|
type: integer
|
||||||
|
- description: 页大小
|
||||||
|
in: query
|
||||||
|
name: page_size
|
||||||
|
type: integer
|
||||||
|
- description: 用户id
|
||||||
|
in: query
|
||||||
|
name: user_id
|
||||||
|
type: integer
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/definitions/response.Response'
|
||||||
|
- properties:
|
||||||
|
data:
|
||||||
|
$ref: '#/definitions/model.AddressBookList'
|
||||||
|
type: object
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Response'
|
||||||
|
security:
|
||||||
|
- token: []
|
||||||
|
summary: 地址簿列表
|
||||||
|
tags:
|
||||||
|
- 我的地址簿
|
||||||
|
/admin/my/address_book/update:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 地址簿编辑
|
||||||
|
parameters:
|
||||||
|
- description: 地址簿信息
|
||||||
|
in: body
|
||||||
|
name: body
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/admin.AddressBookForm'
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/definitions/response.Response'
|
||||||
|
- properties:
|
||||||
|
data:
|
||||||
|
$ref: '#/definitions/model.AddressBook'
|
||||||
|
type: object
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Response'
|
||||||
|
security:
|
||||||
|
- token: []
|
||||||
|
summary: 地址簿编辑
|
||||||
|
tags:
|
||||||
|
- 我的地址簿
|
||||||
|
/admin/my/address_book_collection/create:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 创建地址簿名称
|
||||||
|
parameters:
|
||||||
|
- description: 地址簿名称信息
|
||||||
|
in: body
|
||||||
|
name: body
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/model.AddressBookCollection'
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/definitions/response.Response'
|
||||||
|
- properties:
|
||||||
|
data:
|
||||||
|
$ref: '#/definitions/model.AddressBookCollection'
|
||||||
|
type: object
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Response'
|
||||||
|
security:
|
||||||
|
- token: []
|
||||||
|
summary: 创建地址簿名称
|
||||||
|
tags:
|
||||||
|
- 我的地址簿名称
|
||||||
|
/admin/my/address_book_collection/delete:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 地址簿名称删除
|
||||||
|
parameters:
|
||||||
|
- description: 地址簿名称信息
|
||||||
|
in: body
|
||||||
|
name: body
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/model.AddressBookCollection'
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Response'
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Response'
|
||||||
|
security:
|
||||||
|
- token: []
|
||||||
|
summary: 地址簿名称删除
|
||||||
|
tags:
|
||||||
|
- 我的地址簿名称
|
||||||
|
/admin/my/address_book_collection/list:
|
||||||
|
get:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 地址簿名称列表
|
||||||
|
parameters:
|
||||||
|
- description: 页码
|
||||||
|
in: query
|
||||||
|
name: page
|
||||||
|
type: integer
|
||||||
|
- description: 页大小
|
||||||
|
in: query
|
||||||
|
name: page_size
|
||||||
|
type: integer
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/definitions/response.Response'
|
||||||
|
- properties:
|
||||||
|
data:
|
||||||
|
$ref: '#/definitions/model.AddressBookCollectionList'
|
||||||
|
type: object
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Response'
|
||||||
|
security:
|
||||||
|
- token: []
|
||||||
|
summary: 地址簿名称列表
|
||||||
|
tags:
|
||||||
|
- 我的地址簿名称
|
||||||
|
/admin/my/address_book_collection/update:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 地址簿名称编辑
|
||||||
|
parameters:
|
||||||
|
- description: 地址簿名称信息
|
||||||
|
in: body
|
||||||
|
name: body
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/model.AddressBookCollection'
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/definitions/response.Response'
|
||||||
|
- properties:
|
||||||
|
data:
|
||||||
|
$ref: '#/definitions/model.AddressBookCollection'
|
||||||
|
type: object
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Response'
|
||||||
|
security:
|
||||||
|
- token: []
|
||||||
|
summary: 地址簿名称编辑
|
||||||
|
tags:
|
||||||
|
- 我的地址簿名称
|
||||||
|
/admin/my/address_book_collection_rule/create:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 创建地址簿规则
|
||||||
|
parameters:
|
||||||
|
- description: 地址簿规则信息
|
||||||
|
in: body
|
||||||
|
name: body
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/model.AddressBookCollectionRule'
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/definitions/response.Response'
|
||||||
|
- properties:
|
||||||
|
data:
|
||||||
|
$ref: '#/definitions/model.AddressBookCollection'
|
||||||
|
type: object
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Response'
|
||||||
|
security:
|
||||||
|
- token: []
|
||||||
|
summary: 创建地址簿规则
|
||||||
|
tags:
|
||||||
|
- 我的地址簿规则
|
||||||
|
/admin/my/address_book_collection_rule/delete:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 地址簿规则删除
|
||||||
|
parameters:
|
||||||
|
- description: 地址簿规则信息
|
||||||
|
in: body
|
||||||
|
name: body
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/model.AddressBookCollectionRule'
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Response'
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Response'
|
||||||
|
security:
|
||||||
|
- token: []
|
||||||
|
summary: 地址簿规则删除
|
||||||
|
tags:
|
||||||
|
- 我的地址簿规则
|
||||||
|
/admin/my/address_book_collection_rule/list:
|
||||||
|
get:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 地址簿规则列表
|
||||||
|
parameters:
|
||||||
|
- description: 页码
|
||||||
|
in: query
|
||||||
|
name: page
|
||||||
|
type: integer
|
||||||
|
- description: 页大小
|
||||||
|
in: query
|
||||||
|
name: page_size
|
||||||
|
type: integer
|
||||||
|
- description: 是否是我的
|
||||||
|
in: query
|
||||||
|
name: is_my
|
||||||
|
type: integer
|
||||||
|
- description: 用户id
|
||||||
|
in: query
|
||||||
|
name: user_id
|
||||||
|
type: integer
|
||||||
|
- description: 地址簿集合id
|
||||||
|
in: query
|
||||||
|
name: collection_id
|
||||||
|
type: integer
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/definitions/response.Response'
|
||||||
|
- properties:
|
||||||
|
data:
|
||||||
|
$ref: '#/definitions/model.AddressBookCollectionList'
|
||||||
|
type: object
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Response'
|
||||||
|
security:
|
||||||
|
- token: []
|
||||||
|
summary: 地址簿规则列表
|
||||||
|
tags:
|
||||||
|
- 我的地址簿规则
|
||||||
|
/admin/my/address_book_collection_rule/update:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 地址簿规则编辑
|
||||||
|
parameters:
|
||||||
|
- description: 地址簿规则信息
|
||||||
|
in: body
|
||||||
|
name: body
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/model.AddressBookCollectionRule'
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/definitions/response.Response'
|
||||||
|
- properties:
|
||||||
|
data:
|
||||||
|
$ref: '#/definitions/model.AddressBookCollection'
|
||||||
|
type: object
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Response'
|
||||||
|
security:
|
||||||
|
- token: []
|
||||||
|
summary: 地址簿规则编辑
|
||||||
|
tags:
|
||||||
|
- 我的地址簿规则
|
||||||
|
/admin/my/peer/list:
|
||||||
|
get:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 设备列表
|
||||||
|
parameters:
|
||||||
|
- description: 页码
|
||||||
|
in: query
|
||||||
|
name: page
|
||||||
|
type: integer
|
||||||
|
- description: 页大小
|
||||||
|
in: query
|
||||||
|
name: page_size
|
||||||
|
type: integer
|
||||||
|
- description: 时间
|
||||||
|
in: query
|
||||||
|
name: time_ago
|
||||||
|
type: integer
|
||||||
|
- description: ID
|
||||||
|
in: query
|
||||||
|
name: id
|
||||||
|
type: string
|
||||||
|
- description: 主机名
|
||||||
|
in: query
|
||||||
|
name: hostname
|
||||||
|
type: string
|
||||||
|
- description: uuids 用逗号分隔
|
||||||
|
in: query
|
||||||
|
name: uuids
|
||||||
|
type: string
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/definitions/response.Response'
|
||||||
|
- properties:
|
||||||
|
data:
|
||||||
|
$ref: '#/definitions/model.PeerList'
|
||||||
|
type: object
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Response'
|
||||||
|
security:
|
||||||
|
- token: []
|
||||||
|
summary: 设备列表
|
||||||
|
tags:
|
||||||
|
- 我的设备
|
||||||
/admin/my/share_record/batchDelete:
|
/admin/my/share_record/batchDelete:
|
||||||
post:
|
post:
|
||||||
consumes:
|
consumes:
|
||||||
@@ -2030,7 +2519,7 @@ paths:
|
|||||||
- token: []
|
- token: []
|
||||||
summary: 分享记录删除
|
summary: 分享记录删除
|
||||||
tags:
|
tags:
|
||||||
- 我的
|
- 我的分享记录
|
||||||
/admin/my/share_record/list:
|
/admin/my/share_record/list:
|
||||||
get:
|
get:
|
||||||
consumes:
|
consumes:
|
||||||
@@ -2060,7 +2549,144 @@ paths:
|
|||||||
- token: []
|
- token: []
|
||||||
summary: 分享记录列表
|
summary: 分享记录列表
|
||||||
tags:
|
tags:
|
||||||
- 我的
|
- 我的分享记录
|
||||||
|
/admin/my/tag/create:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 创建标签
|
||||||
|
parameters:
|
||||||
|
- description: 标签信息
|
||||||
|
in: body
|
||||||
|
name: body
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/admin.TagForm'
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/definitions/response.Response'
|
||||||
|
- properties:
|
||||||
|
data:
|
||||||
|
$ref: '#/definitions/model.Tag'
|
||||||
|
type: object
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Response'
|
||||||
|
security:
|
||||||
|
- token: []
|
||||||
|
summary: 创建标签
|
||||||
|
tags:
|
||||||
|
- 我的标签
|
||||||
|
/admin/my/tag/delete:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 标签删除
|
||||||
|
parameters:
|
||||||
|
- description: 标签信息
|
||||||
|
in: body
|
||||||
|
name: body
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/admin.TagForm'
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Response'
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Response'
|
||||||
|
security:
|
||||||
|
- token: []
|
||||||
|
summary: 标签删除
|
||||||
|
tags:
|
||||||
|
- 标签
|
||||||
|
/admin/my/tag/list:
|
||||||
|
get:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 标签列表
|
||||||
|
parameters:
|
||||||
|
- description: 页码
|
||||||
|
in: query
|
||||||
|
name: page
|
||||||
|
type: integer
|
||||||
|
- description: 页大小
|
||||||
|
in: query
|
||||||
|
name: page_size
|
||||||
|
type: integer
|
||||||
|
- description: 是否是我的
|
||||||
|
in: query
|
||||||
|
name: is_my
|
||||||
|
type: integer
|
||||||
|
- description: 用户id
|
||||||
|
in: query
|
||||||
|
name: user_id
|
||||||
|
type: integer
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/definitions/response.Response'
|
||||||
|
- properties:
|
||||||
|
data:
|
||||||
|
$ref: '#/definitions/model.TagList'
|
||||||
|
type: object
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Response'
|
||||||
|
security:
|
||||||
|
- token: []
|
||||||
|
summary: 标签列表
|
||||||
|
tags:
|
||||||
|
- 我的标签
|
||||||
|
/admin/my/tag/update:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 标签编辑
|
||||||
|
parameters:
|
||||||
|
- description: 标签信息
|
||||||
|
in: body
|
||||||
|
name: body
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/admin.TagForm'
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/definitions/response.Response'
|
||||||
|
- properties:
|
||||||
|
data:
|
||||||
|
$ref: '#/definitions/model.Tag'
|
||||||
|
type: object
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Response'
|
||||||
|
security:
|
||||||
|
- token: []
|
||||||
|
summary: 标签编辑
|
||||||
|
tags:
|
||||||
|
- 我的标签
|
||||||
/admin/oauth/create:
|
/admin/oauth/create:
|
||||||
post:
|
post:
|
||||||
consumes:
|
consumes:
|
||||||
@@ -2956,57 +3582,6 @@ paths:
|
|||||||
summary: 我的授权
|
summary: 我的授权
|
||||||
tags:
|
tags:
|
||||||
- 用户
|
- 用户
|
||||||
/admin/user/myPeer:
|
|
||||||
get:
|
|
||||||
consumes:
|
|
||||||
- application/json
|
|
||||||
description: 我的设备列表
|
|
||||||
parameters:
|
|
||||||
- description: 页码
|
|
||||||
in: query
|
|
||||||
name: page
|
|
||||||
type: integer
|
|
||||||
- description: 页大小
|
|
||||||
in: query
|
|
||||||
name: page_size
|
|
||||||
type: integer
|
|
||||||
- description: 时间
|
|
||||||
in: query
|
|
||||||
name: time_ago
|
|
||||||
type: integer
|
|
||||||
- description: ID
|
|
||||||
in: query
|
|
||||||
name: id
|
|
||||||
type: string
|
|
||||||
- description: 主机名
|
|
||||||
in: query
|
|
||||||
name: hostname
|
|
||||||
type: string
|
|
||||||
- description: uuids 用逗号分隔
|
|
||||||
in: query
|
|
||||||
name: uuids
|
|
||||||
type: string
|
|
||||||
produces:
|
|
||||||
- application/json
|
|
||||||
responses:
|
|
||||||
"200":
|
|
||||||
description: OK
|
|
||||||
schema:
|
|
||||||
allOf:
|
|
||||||
- $ref: '#/definitions/response.Response'
|
|
||||||
- properties:
|
|
||||||
data:
|
|
||||||
$ref: '#/definitions/model.PeerList'
|
|
||||||
type: object
|
|
||||||
"500":
|
|
||||||
description: Internal Server Error
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/response.Response'
|
|
||||||
security:
|
|
||||||
- token: []
|
|
||||||
summary: 我的设备列表
|
|
||||||
tags:
|
|
||||||
- 设备
|
|
||||||
/admin/user/update:
|
/admin/user/update:
|
||||||
post:
|
post:
|
||||||
consumes:
|
consumes:
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 97 KiB |
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 97 KiB |
BIN
docs/en_img/rustdesk_command_advance.png
Normal file
|
After Width: | Height: | Size: 99 KiB |
BIN
docs/en_img/rustdesk_command_simple.png
Normal file
|
After Width: | Height: | Size: 67 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 3.8 KiB |
BIN
docs/rustdesk_command_advance.png
Normal file
|
After Width: | Height: | Size: 99 KiB |
BIN
docs/rustdesk_command_simple.png
Normal file
|
After Width: | Height: | Size: 67 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 13 KiB |
@@ -6,6 +6,7 @@ import (
|
|||||||
"github.com/go-playground/locales/es"
|
"github.com/go-playground/locales/es"
|
||||||
"github.com/go-playground/locales/ko"
|
"github.com/go-playground/locales/ko"
|
||||||
"github.com/go-playground/locales/ru"
|
"github.com/go-playground/locales/ru"
|
||||||
|
"github.com/go-playground/locales/fr"
|
||||||
"github.com/go-playground/locales/zh_Hans_CN"
|
"github.com/go-playground/locales/zh_Hans_CN"
|
||||||
|
|
||||||
ut "github.com/go-playground/universal-translator"
|
ut "github.com/go-playground/universal-translator"
|
||||||
@@ -14,6 +15,7 @@ import (
|
|||||||
es_translations "github.com/go-playground/validator/v10/translations/es"
|
es_translations "github.com/go-playground/validator/v10/translations/es"
|
||||||
ru_translations "github.com/go-playground/validator/v10/translations/ru"
|
ru_translations "github.com/go-playground/validator/v10/translations/ru"
|
||||||
zh_translations "github.com/go-playground/validator/v10/translations/zh"
|
zh_translations "github.com/go-playground/validator/v10/translations/zh"
|
||||||
|
fr_translations "github.com/go-playground/validator/v10/translations/fr"
|
||||||
"reflect"
|
"reflect"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -26,14 +28,16 @@ func ApiInitValidator() {
|
|||||||
koT := ko.New()
|
koT := ko.New()
|
||||||
ruT := ru.New()
|
ruT := ru.New()
|
||||||
esT := es.New()
|
esT := es.New()
|
||||||
|
frT := fr.New()
|
||||||
|
|
||||||
uni := ut.New(enT, cn, koT, ruT, esT)
|
uni := ut.New(enT, cn, koT, ruT, esT, frT)
|
||||||
|
|
||||||
enTrans, _ := uni.GetTranslator("en")
|
enTrans, _ := uni.GetTranslator("en")
|
||||||
zhTrans, _ := uni.GetTranslator("zh_Hans_CN")
|
zhTrans, _ := uni.GetTranslator("zh_Hans_CN")
|
||||||
koTrans, _ := uni.GetTranslator("ko")
|
koTrans, _ := uni.GetTranslator("ko")
|
||||||
ruTrans, _ := uni.GetTranslator("ru")
|
ruTrans, _ := uni.GetTranslator("ru")
|
||||||
esTrans, _ := uni.GetTranslator("es")
|
esTrans, _ := uni.GetTranslator("es")
|
||||||
|
frTrans, _ := uni.GetTranslator("fr")
|
||||||
|
|
||||||
err := zh_translations.RegisterDefaultTranslations(validate, zhTrans)
|
err := zh_translations.RegisterDefaultTranslations(validate, zhTrans)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -57,6 +61,10 @@ func ApiInitValidator() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
err = fr_translations.RegisterDefaultTranslations(validate, frTrans)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
validate.RegisterTagNameFunc(func(field reflect.StructField) string {
|
validate.RegisterTagNameFunc(func(field reflect.StructField) string {
|
||||||
label := field.Tag.Get("label")
|
label := field.Tag.Get("label")
|
||||||
@@ -126,6 +134,9 @@ func getTranslatorForLang(lang string) ut.Translator {
|
|||||||
case "es":
|
case "es":
|
||||||
trans, _ := Validator.UT.GetTranslator("es")
|
trans, _ := Validator.UT.GetTranslator("es")
|
||||||
return trans
|
return trans
|
||||||
|
case "fr":
|
||||||
|
trans, _ := Validator.UT.GetTranslator("fr")
|
||||||
|
return trans
|
||||||
case "en":
|
case "en":
|
||||||
fallthrough
|
fallthrough
|
||||||
default:
|
default:
|
||||||
|
|||||||
3
go.mod
@@ -43,6 +43,7 @@ require (
|
|||||||
github.com/go-openapi/swag v0.19.15 // indirect
|
github.com/go-openapi/swag v0.19.15 // indirect
|
||||||
github.com/go-sql-driver/mysql v1.7.0 // indirect
|
github.com/go-sql-driver/mysql v1.7.0 // indirect
|
||||||
github.com/goccy/go-json v0.10.0 // indirect
|
github.com/goccy/go-json v0.10.0 // indirect
|
||||||
|
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
|
||||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
@@ -58,6 +59,7 @@ require (
|
|||||||
github.com/mitchellh/mapstructure v1.4.2 // indirect
|
github.com/mitchellh/mapstructure v1.4.2 // indirect
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
|
github.com/mojocn/base64Captcha v1.3.6 // indirect
|
||||||
github.com/pelletier/go-toml v1.9.4 // indirect
|
github.com/pelletier/go-toml v1.9.4 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
|
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
|
||||||
github.com/spf13/afero v1.6.0 // indirect
|
github.com/spf13/afero v1.6.0 // indirect
|
||||||
@@ -69,6 +71,7 @@ require (
|
|||||||
github.com/ugorji/go/codec v1.2.9 // indirect
|
github.com/ugorji/go/codec v1.2.9 // indirect
|
||||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
|
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
|
||||||
golang.org/x/crypto v0.23.0 // indirect
|
golang.org/x/crypto v0.23.0 // indirect
|
||||||
|
golang.org/x/image v0.13.0 // indirect
|
||||||
golang.org/x/net v0.25.0 // indirect
|
golang.org/x/net v0.25.0 // indirect
|
||||||
golang.org/x/sys v0.25.0 // indirect
|
golang.org/x/sys v0.25.0 // indirect
|
||||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
|
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"Gwen/global"
|
"Gwen/global"
|
||||||
"Gwen/http/request/admin"
|
"Gwen/http/request/admin"
|
||||||
"Gwen/http/response"
|
"Gwen/http/response"
|
||||||
"Gwen/model"
|
|
||||||
"Gwen/service"
|
"Gwen/service"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
_ "encoding/json"
|
_ "encoding/json"
|
||||||
@@ -31,11 +30,6 @@ func (ct *AddressBook) Detail(c *gin.Context) {
|
|||||||
id := c.Param("id")
|
id := c.Param("id")
|
||||||
iid, _ := strconv.Atoi(id)
|
iid, _ := strconv.Atoi(id)
|
||||||
t := service.AllService.AddressBookService.InfoByRowId(uint(iid))
|
t := service.AllService.AddressBookService.InfoByRowId(uint(iid))
|
||||||
u := service.AllService.UserService.CurUser(c)
|
|
||||||
if !service.AllService.UserService.IsAdmin(u) && t.UserId != u.Id {
|
|
||||||
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if t.RowId > 0 {
|
if t.RowId > 0 {
|
||||||
response.Success(c, t)
|
response.Success(c, t)
|
||||||
return
|
return
|
||||||
@@ -67,9 +61,9 @@ func (ct *AddressBook) Create(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
t := f.ToAddressBook()
|
t := f.ToAddressBook()
|
||||||
u := service.AllService.UserService.CurUser(c)
|
if t.UserId == 0 {
|
||||||
if !service.AllService.UserService.IsAdmin(u) || t.UserId == 0 {
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||||
t.UserId = u.Id
|
return
|
||||||
}
|
}
|
||||||
if t.CollectionId > 0 && !service.AllService.AddressBookService.CheckCollectionOwner(t.UserId, t.CollectionId) {
|
if t.CollectionId > 0 && !service.AllService.AddressBookService.CheckCollectionOwner(t.UserId, t.CollectionId) {
|
||||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||||
@@ -99,7 +93,7 @@ func (ct *AddressBook) Create(c *gin.Context) {
|
|||||||
// @Param body body admin.AddressBookForm true "地址簿信息"
|
// @Param body body admin.AddressBookForm true "地址簿信息"
|
||||||
// @Success 200 {object} response.Response{data=model.AddressBook}
|
// @Success 200 {object} response.Response{data=model.AddressBook}
|
||||||
// @Failure 500 {object} response.Response
|
// @Failure 500 {object} response.Response
|
||||||
// @Router /admin/address_book/create [post]
|
// @Router /admin/address_book/batchCreate [post]
|
||||||
// @Security token
|
// @Security token
|
||||||
func (ct *AddressBook) BatchCreate(c *gin.Context) {
|
func (ct *AddressBook) BatchCreate(c *gin.Context) {
|
||||||
f := &admin.AddressBookForm{}
|
f := &admin.AddressBookForm{}
|
||||||
@@ -112,9 +106,21 @@ func (ct *AddressBook) BatchCreate(c *gin.Context) {
|
|||||||
response.Fail(c, 101, errList[0])
|
response.Fail(c, 101, errList[0])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
ul := len(f.UserIds)
|
||||||
|
|
||||||
|
if ul == 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ul > 1 {
|
||||||
|
//多用户置空标签
|
||||||
|
f.Tags = []string{}
|
||||||
|
//多用户只能创建到默认地址簿
|
||||||
|
f.CollectionId = 0
|
||||||
|
}
|
||||||
|
|
||||||
//创建标签
|
//创建标签
|
||||||
for _, fu := range f.UserIds {
|
/*for _, fu := range f.UserIds {
|
||||||
if fu == 0 {
|
if fu == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -127,13 +133,13 @@ func (ct *AddressBook) BatchCreate(c *gin.Context) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
ts := f.ToAddressBooks()
|
ts := f.ToAddressBooks()
|
||||||
for _, t := range ts {
|
for _, t := range ts {
|
||||||
if t.UserId == 0 {
|
if t.UserId == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
ex := service.AllService.AddressBookService.InfoByUserIdAndId(t.UserId, t.Id)
|
ex := service.AllService.AddressBookService.InfoByUserIdAndIdAndCid(t.UserId, t.Id, t.CollectionId)
|
||||||
if ex.RowId == 0 {
|
if ex.RowId == 0 {
|
||||||
service.AllService.AddressBookService.Create(t)
|
service.AllService.AddressBookService.Create(t)
|
||||||
}
|
}
|
||||||
@@ -162,10 +168,6 @@ func (ct *AddressBook) List(c *gin.Context) {
|
|||||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
u := service.AllService.UserService.CurUser(c)
|
|
||||||
if !service.AllService.UserService.IsAdmin(u) || query.IsMy == 1 {
|
|
||||||
query.UserId = int(u.Id)
|
|
||||||
}
|
|
||||||
res := service.AllService.AddressBookService.List(query.Page, query.PageSize, func(tx *gorm.DB) {
|
res := service.AllService.AddressBookService.List(query.Page, query.PageSize, func(tx *gorm.DB) {
|
||||||
tx.Preload("Collection", func(txc *gorm.DB) *gorm.DB {
|
tx.Preload("Collection", func(txc *gorm.DB) *gorm.DB {
|
||||||
return txc.Select("id,name")
|
return txc.Select("id,name")
|
||||||
@@ -191,11 +193,6 @@ func (ct *AddressBook) List(c *gin.Context) {
|
|||||||
for _, ab := range res.AddressBooks {
|
for _, ab := range res.AddressBooks {
|
||||||
abCIds = append(abCIds, ab.CollectionId)
|
abCIds = append(abCIds, ab.CollectionId)
|
||||||
}
|
}
|
||||||
//获取地址簿名称
|
|
||||||
//cRes := service.AllService.AddressBookService.ListCollection(1, 999, func(tx *gorm.DB) {
|
|
||||||
// tx.Where("id in ?", abCIds)
|
|
||||||
//})
|
|
||||||
//
|
|
||||||
response.Success(c, res)
|
response.Success(c, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,15 +219,15 @@ func (ct *AddressBook) Update(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if f.RowId == 0 {
|
if f.RowId == 0 {
|
||||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ex := service.AllService.AddressBookService.InfoByRowId(f.RowId)
|
||||||
|
if ex.RowId == 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
t := f.ToAddressBook()
|
t := f.ToAddressBook()
|
||||||
u := service.AllService.UserService.CurUser(c)
|
|
||||||
if !service.AllService.UserService.IsAdmin(u) && t.UserId != u.Id {
|
|
||||||
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if t.CollectionId > 0 && !service.AllService.AddressBookService.CheckCollectionOwner(t.UserId, t.CollectionId) {
|
if t.CollectionId > 0 && !service.AllService.AddressBookService.CheckCollectionOwner(t.UserId, t.CollectionId) {
|
||||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||||
return
|
return
|
||||||
@@ -271,21 +268,12 @@ func (ct *AddressBook) Delete(c *gin.Context) {
|
|||||||
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
u := service.AllService.UserService.CurUser(c)
|
err := service.AllService.AddressBookService.Delete(t)
|
||||||
if !service.AllService.UserService.IsAdmin(u) && t.UserId != u.Id {
|
if err == nil {
|
||||||
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
|
response.Success(c, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if u.Id > 0 {
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
err := service.AllService.AddressBookService.Delete(t)
|
|
||||||
if err == nil {
|
|
||||||
response.Success(c, nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ShareByWebClient
|
// ShareByWebClient
|
||||||
@@ -372,27 +360,3 @@ func (ct *AddressBook) BatchCreateFromPeers(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
response.Success(c, nil)
|
response.Success(c, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ct *AddressBook) BatchUpdateTags(c *gin.Context) {
|
|
||||||
f := &admin.BatchUpdateTagsForm{}
|
|
||||||
if err := c.ShouldBindJSON(f); err != nil {
|
|
||||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
u := service.AllService.UserService.CurUser(c)
|
|
||||||
|
|
||||||
abs := service.AllService.AddressBookService.List(1, 999, func(tx *gorm.DB) {
|
|
||||||
tx.Where("row_id in ?", f.RowIds)
|
|
||||||
tx.Where("user_id = ?", u.Id)
|
|
||||||
})
|
|
||||||
if abs.Total == 0 {
|
|
||||||
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err := service.AllService.AddressBookService.BatchUpdateTags(abs.AddressBooks, f.Tags)
|
|
||||||
if err != nil {
|
|
||||||
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
response.Success(c, nil)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -29,11 +29,6 @@ func (abc *AddressBookCollection) Detail(c *gin.Context) {
|
|||||||
id := c.Param("id")
|
id := c.Param("id")
|
||||||
iid, _ := strconv.Atoi(id)
|
iid, _ := strconv.Atoi(id)
|
||||||
t := service.AllService.AddressBookService.CollectionInfoById(uint(iid))
|
t := service.AllService.AddressBookService.CollectionInfoById(uint(iid))
|
||||||
u := service.AllService.UserService.CurUser(c)
|
|
||||||
if !service.AllService.UserService.IsAdmin(u) && t.UserId != u.Id {
|
|
||||||
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if t.Id > 0 {
|
if t.Id > 0 {
|
||||||
response.Success(c, t)
|
response.Success(c, t)
|
||||||
return
|
return
|
||||||
@@ -64,12 +59,11 @@ func (abc *AddressBookCollection) Create(c *gin.Context) {
|
|||||||
response.Fail(c, 101, errList[0])
|
response.Fail(c, 101, errList[0])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
//t := f.ToAddressBookCollection()
|
if f.UserId == 0 {
|
||||||
t := f
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||||
u := service.AllService.UserService.CurUser(c)
|
return
|
||||||
if !service.AllService.UserService.IsAdmin(u) || t.UserId == 0 {
|
|
||||||
t.UserId = u.Id
|
|
||||||
}
|
}
|
||||||
|
t := f
|
||||||
err := service.AllService.AddressBookService.CreateCollection(t)
|
err := service.AllService.AddressBookService.CreateCollection(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
@@ -98,10 +92,6 @@ func (abc *AddressBookCollection) List(c *gin.Context) {
|
|||||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
u := service.AllService.UserService.CurUser(c)
|
|
||||||
if !service.AllService.UserService.IsAdmin(u) || query.IsMy == 1 {
|
|
||||||
query.UserId = int(u.Id)
|
|
||||||
}
|
|
||||||
res := service.AllService.AddressBookService.ListCollection(query.Page, query.PageSize, func(tx *gorm.DB) {
|
res := service.AllService.AddressBookService.ListCollection(query.Page, query.PageSize, func(tx *gorm.DB) {
|
||||||
if query.UserId > 0 {
|
if query.UserId > 0 {
|
||||||
tx.Where("user_id = ?", query.UserId)
|
tx.Where("user_id = ?", query.UserId)
|
||||||
@@ -137,11 +127,6 @@ func (abc *AddressBookCollection) Update(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
t := f //f.ToAddressBookCollection()
|
t := f //f.ToAddressBookCollection()
|
||||||
u := service.AllService.UserService.CurUser(c)
|
|
||||||
if !service.AllService.UserService.IsAdmin(u) && t.UserId != u.Id {
|
|
||||||
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err := service.AllService.AddressBookService.UpdateCollection(t)
|
err := service.AllService.AddressBookService.UpdateCollection(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
@@ -173,20 +158,15 @@ func (abc *AddressBookCollection) Delete(c *gin.Context) {
|
|||||||
response.Fail(c, 101, errList[0])
|
response.Fail(c, 101, errList[0])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
t := service.AllService.AddressBookService.CollectionInfoById(f.Id)
|
ex := service.AllService.AddressBookService.CollectionInfoById(f.Id)
|
||||||
u := service.AllService.UserService.CurUser(c)
|
if ex.Id == 0 {
|
||||||
if !service.AllService.UserService.IsAdmin(u) && t.UserId != u.Id {
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if u.Id > 0 {
|
err := service.AllService.AddressBookService.DeleteCollection(ex)
|
||||||
err := service.AllService.AddressBookService.DeleteCollection(t)
|
if err == nil {
|
||||||
if err == nil {
|
response.Success(c, nil)
|
||||||
response.Success(c, nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
response.Fail(c, 101, err.Error())
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,10 +35,6 @@ func (abcr *AddressBookCollectionRule) List(c *gin.Context) {
|
|||||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
u := service.AllService.UserService.CurUser(c)
|
|
||||||
if !service.AllService.UserService.IsAdmin(u) || query.IsMy == 1 {
|
|
||||||
query.UserId = int(u.Id)
|
|
||||||
}
|
|
||||||
|
|
||||||
res := service.AllService.AddressBookService.ListRules(query.Page, query.PageSize, func(tx *gorm.DB) {
|
res := service.AllService.AddressBookService.ListRules(query.Page, query.PageSize, func(tx *gorm.DB) {
|
||||||
if query.UserId > 0 {
|
if query.UserId > 0 {
|
||||||
@@ -66,17 +62,11 @@ func (abcr *AddressBookCollectionRule) Detail(c *gin.Context) {
|
|||||||
id := c.Param("id")
|
id := c.Param("id")
|
||||||
iid, _ := strconv.Atoi(id)
|
iid, _ := strconv.Atoi(id)
|
||||||
t := service.AllService.AddressBookService.RuleInfoById(uint(iid))
|
t := service.AllService.AddressBookService.RuleInfoById(uint(iid))
|
||||||
u := service.AllService.UserService.CurUser(c)
|
|
||||||
if !service.AllService.UserService.IsAdmin(u) && t.UserId != u.Id {
|
|
||||||
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if t.Id > 0 {
|
if t.Id > 0 {
|
||||||
response.Success(c, t)
|
response.Success(c, t)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create 创建地址簿规则
|
// Create 创建地址簿规则
|
||||||
@@ -105,13 +95,8 @@ func (abcr *AddressBookCollectionRule) Create(c *gin.Context) {
|
|||||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
//t := f.ToAddressBookCollection()
|
|
||||||
t := f
|
t := f
|
||||||
u := service.AllService.UserService.CurUser(c)
|
msg, res := abcr.CheckForm(t)
|
||||||
if t.UserId == 0 {
|
|
||||||
t.UserId = u.Id
|
|
||||||
}
|
|
||||||
msg, res := abcr.CheckForm(u, t)
|
|
||||||
if !res {
|
if !res {
|
||||||
response.Fail(c, 101, response.TranslateMsg(c, msg))
|
response.Fail(c, 101, response.TranslateMsg(c, msg))
|
||||||
return
|
return
|
||||||
@@ -124,9 +109,9 @@ func (abcr *AddressBookCollectionRule) Create(c *gin.Context) {
|
|||||||
response.Success(c, nil)
|
response.Success(c, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (abcr *AddressBookCollectionRule) CheckForm(u *model.User, t *model.AddressBookCollectionRule) (string, bool) {
|
func (abcr *AddressBookCollectionRule) CheckForm(t *model.AddressBookCollectionRule) (string, bool) {
|
||||||
if !service.AllService.UserService.IsAdmin(u) && t.UserId != u.Id {
|
if t.UserId == 0 {
|
||||||
return "NoAccess", false
|
return "ParamsError", false
|
||||||
}
|
}
|
||||||
if t.CollectionId > 0 && !service.AllService.AddressBookService.CheckCollectionOwner(t.UserId, t.CollectionId) {
|
if t.CollectionId > 0 && !service.AllService.AddressBookService.CheckCollectionOwner(t.UserId, t.CollectionId) {
|
||||||
return "ParamsError", false
|
return "ParamsError", false
|
||||||
@@ -141,15 +126,7 @@ func (abcr *AddressBookCollectionRule) CheckForm(u *model.User, t *model.Address
|
|||||||
if tou.Id == 0 {
|
if tou.Id == 0 {
|
||||||
return "ItemNotFound", false
|
return "ItemNotFound", false
|
||||||
}
|
}
|
||||||
//非管理员不能分享给非本组织用户
|
|
||||||
if tou.GroupId != u.GroupId && !service.AllService.UserService.IsAdmin(u) {
|
|
||||||
return "NoAccess", false
|
|
||||||
}
|
|
||||||
} else if t.Type == model.ShareAddressBookRuleTypeGroup {
|
} else if t.Type == model.ShareAddressBookRuleTypeGroup {
|
||||||
if t.ToId != u.GroupId && !service.AllService.UserService.IsAdmin(u) {
|
|
||||||
return "NoAccess", false
|
|
||||||
}
|
|
||||||
|
|
||||||
tog := service.AllService.GroupService.InfoById(t.ToId)
|
tog := service.AllService.GroupService.InfoById(t.ToId)
|
||||||
if tog.Id == 0 {
|
if tog.Id == 0 {
|
||||||
return "ItemNotFound", false
|
return "ItemNotFound", false
|
||||||
@@ -194,9 +171,8 @@ func (abcr *AddressBookCollectionRule) Update(c *gin.Context) {
|
|||||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
t := f //f.ToAddressBookCollection()
|
t := f
|
||||||
u := service.AllService.UserService.CurUser(c)
|
msg, res := abcr.CheckForm(t)
|
||||||
msg, res := abcr.CheckForm(u, t)
|
|
||||||
if !res {
|
if !res {
|
||||||
response.Fail(c, 101, response.TranslateMsg(c, msg))
|
response.Fail(c, 101, response.TranslateMsg(c, msg))
|
||||||
return
|
return
|
||||||
@@ -232,20 +208,15 @@ func (abcr *AddressBookCollectionRule) Delete(c *gin.Context) {
|
|||||||
response.Fail(c, 101, errList[0])
|
response.Fail(c, 101, errList[0])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
t := service.AllService.AddressBookService.RuleInfoById(f.Id)
|
ex := service.AllService.AddressBookService.RuleInfoById(f.Id)
|
||||||
u := service.AllService.UserService.CurUser(c)
|
if ex.Id == 0 {
|
||||||
if !service.AllService.UserService.IsAdmin(u) && t.UserId != u.Id {
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if t.Id > 0 {
|
err := service.AllService.AddressBookService.DeleteRule(ex)
|
||||||
err := service.AllService.AddressBookService.DeleteRule(t)
|
if err == nil {
|
||||||
if err == nil {
|
response.Success(c, nil)
|
||||||
response.Success(c, nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
response.Fail(c, 101, err.Error())
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,11 +11,135 @@ import (
|
|||||||
"Gwen/service"
|
"Gwen/service"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/mojocn/base64Captcha"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Login struct {
|
type Login struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Captcha 验证码结构
|
||||||
|
type Captcha struct {
|
||||||
|
Id string `json:"id"` // 验证码 ID
|
||||||
|
B64 string `json:"b64"` // base64 验证码
|
||||||
|
Code string `json:"-"` // 验证码内容
|
||||||
|
ExpiresAt time.Time `json:"-"` // 过期时间
|
||||||
|
}
|
||||||
|
type LoginLimiter struct {
|
||||||
|
mu sync.RWMutex
|
||||||
|
failCount map[string]int // 记录每个 IP 的失败次数
|
||||||
|
timestamp map[string]time.Time // 记录每个 IP 的最后失败时间
|
||||||
|
captchas map[string]Captcha // 每个 IP 的验证码
|
||||||
|
threshold int // 失败阈值
|
||||||
|
expiry time.Duration // 失败记录过期时间
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLoginLimiter(threshold int, expiry time.Duration) *LoginLimiter {
|
||||||
|
return &LoginLimiter{
|
||||||
|
failCount: make(map[string]int),
|
||||||
|
timestamp: make(map[string]time.Time),
|
||||||
|
captchas: make(map[string]Captcha),
|
||||||
|
threshold: threshold,
|
||||||
|
expiry: expiry,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RecordFailure 记录登录失败
|
||||||
|
func (l *LoginLimiter) RecordFailure(ip string) {
|
||||||
|
l.mu.Lock()
|
||||||
|
defer l.mu.Unlock()
|
||||||
|
|
||||||
|
// 如果该 IP 的记录已经过期,重置计数
|
||||||
|
if lastTime, exists := l.timestamp[ip]; exists && time.Since(lastTime) > l.expiry {
|
||||||
|
l.failCount[ip] = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新失败次数和时间戳
|
||||||
|
l.failCount[ip]++
|
||||||
|
l.timestamp[ip] = time.Now()
|
||||||
|
}
|
||||||
|
|
||||||
|
// NeedsCaptcha 检查是否需要验证码
|
||||||
|
func (l *LoginLimiter) NeedsCaptcha(ip string) bool {
|
||||||
|
l.mu.RLock()
|
||||||
|
defer l.mu.RUnlock()
|
||||||
|
|
||||||
|
// 检查记录是否存在且未过期
|
||||||
|
if lastTime, exists := l.timestamp[ip]; exists && time.Since(lastTime) <= l.expiry {
|
||||||
|
return l.failCount[ip] >= l.threshold
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateCaptcha 为指定 IP 生成验证码
|
||||||
|
func (l *LoginLimiter) GenerateCaptcha(ip string) Captcha {
|
||||||
|
l.mu.Lock()
|
||||||
|
defer l.mu.Unlock()
|
||||||
|
|
||||||
|
capd := base64Captcha.NewDriverString(50, 150, 5, 10, 4, "1234567890abcdefghijklmnopqrstuvwxyz", nil, nil, nil)
|
||||||
|
b64cap := base64Captcha.NewCaptcha(capd, base64Captcha.DefaultMemStore)
|
||||||
|
id, b64s, answer, err := b64cap.Generate()
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Error("Generate captcha failed: " + err.Error())
|
||||||
|
return Captcha{}
|
||||||
|
}
|
||||||
|
// 保存验证码到对应 IP
|
||||||
|
l.captchas[ip] = Captcha{
|
||||||
|
Id: id,
|
||||||
|
B64: b64s,
|
||||||
|
Code: answer,
|
||||||
|
ExpiresAt: time.Now().Add(5 * time.Minute),
|
||||||
|
}
|
||||||
|
return l.captchas[ip]
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyCaptcha 验证指定 IP 的验证码
|
||||||
|
func (l *LoginLimiter) VerifyCaptcha(ip, code string) bool {
|
||||||
|
l.mu.RLock()
|
||||||
|
defer l.mu.RUnlock()
|
||||||
|
|
||||||
|
// 检查验证码是否存在且未过期
|
||||||
|
if captcha, exists := l.captchas[ip]; exists && time.Now().Before(captcha.ExpiresAt) {
|
||||||
|
return captcha.Code == code
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveCaptcha 移除指定 IP 的验证码
|
||||||
|
func (l *LoginLimiter) RemoveCaptcha(ip string) {
|
||||||
|
l.mu.Lock()
|
||||||
|
defer l.mu.Unlock()
|
||||||
|
|
||||||
|
delete(l.captchas, ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CleanupExpired 清理过期的记录
|
||||||
|
func (l *LoginLimiter) CleanupExpired() {
|
||||||
|
l.mu.Lock()
|
||||||
|
defer l.mu.Unlock()
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
|
for ip, lastTime := range l.timestamp {
|
||||||
|
if now.Sub(lastTime) > l.expiry {
|
||||||
|
delete(l.failCount, ip)
|
||||||
|
delete(l.timestamp, ip)
|
||||||
|
delete(l.captchas, ip)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LoginLimiter) RemoveRecord(ip string) {
|
||||||
|
l.mu.Lock()
|
||||||
|
defer l.mu.Unlock()
|
||||||
|
|
||||||
|
delete(l.failCount, ip)
|
||||||
|
delete(l.timestamp, ip)
|
||||||
|
delete(l.captchas, ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
var loginLimiter = NewLoginLimiter(3, 5*time.Minute)
|
||||||
|
|
||||||
// Login 登录
|
// Login 登录
|
||||||
// @Tags 登录
|
// @Tags 登录
|
||||||
// @Summary 登录
|
// @Summary 登录
|
||||||
@@ -30,22 +154,39 @@ type Login struct {
|
|||||||
func (ct *Login) Login(c *gin.Context) {
|
func (ct *Login) Login(c *gin.Context) {
|
||||||
f := &admin.Login{}
|
f := &admin.Login{}
|
||||||
err := c.ShouldBindJSON(f)
|
err := c.ShouldBindJSON(f)
|
||||||
|
clientIp := c.ClientIP()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
global.Logger.Warn(fmt.Sprintf("Login Fail: %s %s %s", "ParamsError", c.RemoteIP(), c.ClientIP()))
|
global.Logger.Warn(fmt.Sprintf("Login Fail: %s %s %s", "ParamsError", c.RemoteIP(), clientIp))
|
||||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
errList := global.Validator.ValidStruct(c, f)
|
errList := global.Validator.ValidStruct(c, f)
|
||||||
if len(errList) > 0 {
|
if len(errList) > 0 {
|
||||||
global.Logger.Warn(fmt.Sprintf("Login Fail: %s %s %s", "ParamsError", c.RemoteIP(), c.ClientIP()))
|
global.Logger.Warn(fmt.Sprintf("Login Fail: %s %s %s", "ParamsError", c.RemoteIP(), clientIp))
|
||||||
response.Fail(c, 101, errList[0])
|
response.Fail(c, 101, errList[0])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 检查是否需要验证码
|
||||||
|
if loginLimiter.NeedsCaptcha(clientIp) {
|
||||||
|
if f.Captcha == "" || !loginLimiter.VerifyCaptcha(clientIp, f.Captcha) {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "CaptchaError"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
u := service.AllService.UserService.InfoByUsernamePassword(f.Username, f.Password)
|
u := service.AllService.UserService.InfoByUsernamePassword(f.Username, f.Password)
|
||||||
|
|
||||||
if u.Id == 0 {
|
if u.Id == 0 {
|
||||||
global.Logger.Warn(fmt.Sprintf("Login Fail: %s %s %s", "UsernameOrPasswordError", c.RemoteIP(), c.ClientIP()))
|
global.Logger.Warn(fmt.Sprintf("Login Fail: %s %s %s", "UsernameOrPasswordError", c.RemoteIP(), clientIp))
|
||||||
|
loginLimiter.RecordFailure(clientIp)
|
||||||
|
if loginLimiter.NeedsCaptcha(clientIp) {
|
||||||
|
// 移除原验证码,重新生成
|
||||||
|
loginLimiter.RemoveCaptcha(clientIp)
|
||||||
|
response.Fail(c, 110, response.TranslateMsg(c, "UsernameOrPasswordError"))
|
||||||
|
return
|
||||||
|
}
|
||||||
response.Fail(c, 101, response.TranslateMsg(c, "UsernameOrPasswordError"))
|
response.Fail(c, 101, response.TranslateMsg(c, "UsernameOrPasswordError"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -54,13 +195,30 @@ func (ct *Login) Login(c *gin.Context) {
|
|||||||
UserId: u.Id,
|
UserId: u.Id,
|
||||||
Client: model.LoginLogClientWebAdmin,
|
Client: model.LoginLogClientWebAdmin,
|
||||||
Uuid: "", //must be empty
|
Uuid: "", //must be empty
|
||||||
Ip: c.ClientIP(),
|
Ip: clientIp,
|
||||||
Type: model.LoginLogTypeAccount,
|
Type: model.LoginLogTypeAccount,
|
||||||
Platform: f.Platform,
|
Platform: f.Platform,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 成功后清除记录
|
||||||
|
loginLimiter.RemoveRecord(clientIp)
|
||||||
|
|
||||||
|
// 清理过期记录
|
||||||
|
go loginLimiter.CleanupExpired()
|
||||||
|
|
||||||
responseLoginSuccess(c, u, ut.Token)
|
responseLoginSuccess(c, u, ut.Token)
|
||||||
}
|
}
|
||||||
|
func (ct *Login) Captcha(c *gin.Context) {
|
||||||
|
clientIp := c.ClientIP()
|
||||||
|
if !loginLimiter.NeedsCaptcha(clientIp) {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "NoCaptchaRequired"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
captcha := loginLimiter.GenerateCaptcha(clientIp)
|
||||||
|
response.Success(c, gin.H{
|
||||||
|
"captcha": captcha,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Logout 登出
|
// Logout 登出
|
||||||
// @Tags 登录
|
// @Tags 登录
|
||||||
@@ -90,10 +248,12 @@ func (ct *Login) Logout(c *gin.Context) {
|
|||||||
// @Failure 500 {object} response.ErrorResponse
|
// @Failure 500 {object} response.ErrorResponse
|
||||||
// @Router /admin/login-options [post]
|
// @Router /admin/login-options [post]
|
||||||
func (ct *Login) LoginOptions(c *gin.Context) {
|
func (ct *Login) LoginOptions(c *gin.Context) {
|
||||||
|
ip := c.ClientIP()
|
||||||
ops := service.AllService.OauthService.GetOauthProviders()
|
ops := service.AllService.OauthService.GetOauthProviders()
|
||||||
response.Success(c, gin.H{
|
response.Success(c, gin.H{
|
||||||
"ops": ops,
|
"ops": ops,
|
||||||
"register": global.Config.App.Register,
|
"register": global.Config.App.Register,
|
||||||
|
"need_captcha": loginLimiter.NeedsCaptcha(ip),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,11 +314,10 @@ func (ct *Login) OidcAuthQuery(c *gin.Context) {
|
|||||||
responseLoginSuccess(c, u, ut.Token)
|
responseLoginSuccess(c, u, ut.Token)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func responseLoginSuccess(c *gin.Context, u *model.User, token string) {
|
func responseLoginSuccess(c *gin.Context, u *model.User, token string) {
|
||||||
lp := &adResp.LoginPayload{}
|
lp := &adResp.LoginPayload{}
|
||||||
lp.FromUser(u)
|
lp.FromUser(u)
|
||||||
lp.Token = token
|
lp.Token = token
|
||||||
lp.RouteNames = service.AllService.UserService.RouteNames(u)
|
lp.RouteNames = service.AllService.UserService.RouteNames(u)
|
||||||
response.Success(c, lp)
|
response.Success(c, lp)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,10 +56,6 @@ func (ct *LoginLog) List(c *gin.Context) {
|
|||||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
u := service.AllService.UserService.CurUser(c)
|
|
||||||
if !service.AllService.UserService.IsAdmin(u) || query.IsMy == 1 {
|
|
||||||
query.UserId = int(u.Id)
|
|
||||||
}
|
|
||||||
res := service.AllService.LoginLogService.List(query.Page, query.PageSize, func(tx *gorm.DB) {
|
res := service.AllService.LoginLogService.List(query.Page, query.PageSize, func(tx *gorm.DB) {
|
||||||
if query.UserId > 0 {
|
if query.UserId > 0 {
|
||||||
tx.Where("user_id = ?", query.UserId)
|
tx.Where("user_id = ?", query.UserId)
|
||||||
@@ -93,21 +89,16 @@ func (ct *LoginLog) Delete(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
l := service.AllService.LoginLogService.InfoById(f.Id)
|
l := service.AllService.LoginLogService.InfoById(f.Id)
|
||||||
u := service.AllService.UserService.CurUser(c)
|
if l.Id == 0 {
|
||||||
if !service.AllService.UserService.IsAdmin(u) && l.UserId != u.Id {
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if l.Id > 0 {
|
err := service.AllService.LoginLogService.Delete(l)
|
||||||
err := service.AllService.LoginLogService.Delete(l)
|
if err == nil {
|
||||||
if err == nil {
|
response.Success(c, nil)
|
||||||
response.Success(c, nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
response.Fail(c, 101, err.Error())
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
response.Fail(c, 101, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
// BatchDelete 删除
|
// BatchDelete 删除
|
||||||
@@ -119,7 +110,7 @@ func (ct *LoginLog) Delete(c *gin.Context) {
|
|||||||
// @Param body body admin.LoginLogIds true "登录日志"
|
// @Param body body admin.LoginLogIds true "登录日志"
|
||||||
// @Success 200 {object} response.Response
|
// @Success 200 {object} response.Response
|
||||||
// @Failure 500 {object} response.Response
|
// @Failure 500 {object} response.Response
|
||||||
// @Router /admin/login_log/delete [post]
|
// @Router /admin/login_log/batchDelete [post]
|
||||||
// @Security token
|
// @Security token
|
||||||
func (ct *LoginLog) BatchDelete(c *gin.Context) {
|
func (ct *LoginLog) BatchDelete(c *gin.Context) {
|
||||||
f := &admin.LoginLogIds{}
|
f := &admin.LoginLogIds{}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package my
|
package my
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"Gwen/global"
|
||||||
"Gwen/http/request/admin"
|
"Gwen/http/request/admin"
|
||||||
"Gwen/http/response"
|
"Gwen/http/response"
|
||||||
"Gwen/service"
|
"Gwen/service"
|
||||||
@@ -11,6 +12,193 @@ import (
|
|||||||
|
|
||||||
type AddressBook struct{}
|
type AddressBook struct{}
|
||||||
|
|
||||||
|
// List 列表
|
||||||
|
// @Tags 我的地址簿
|
||||||
|
// @Summary 地址簿列表
|
||||||
|
// @Description 地址簿列表
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param page query int false "页码"
|
||||||
|
// @Param page_size query int false "页大小"
|
||||||
|
// @Param user_id query int false "用户id"
|
||||||
|
// @Success 200 {object} response.Response{data=model.AddressBookList}
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/my/address_book/list [get]
|
||||||
|
// @Security token
|
||||||
|
func (ct *AddressBook) List(c *gin.Context) {
|
||||||
|
query := &admin.AddressBookQuery{}
|
||||||
|
if err := c.ShouldBindQuery(query); err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
u := service.AllService.UserService.CurUser(c)
|
||||||
|
query.UserId = int(u.Id)
|
||||||
|
res := service.AllService.AddressBookService.List(query.Page, query.PageSize, func(tx *gorm.DB) {
|
||||||
|
//预加载地址簿名称
|
||||||
|
tx.Preload("Collection", func(txc *gorm.DB) *gorm.DB {
|
||||||
|
return txc.Select("id,name")
|
||||||
|
})
|
||||||
|
if query.Id != "" {
|
||||||
|
tx.Where("id like ?", "%"+query.Id+"%")
|
||||||
|
}
|
||||||
|
tx.Where("user_id = ?", query.UserId)
|
||||||
|
if query.Username != "" {
|
||||||
|
tx.Where("username like ?", "%"+query.Username+"%")
|
||||||
|
}
|
||||||
|
if query.Hostname != "" {
|
||||||
|
tx.Where("hostname like ?", "%"+query.Hostname+"%")
|
||||||
|
}
|
||||||
|
if query.CollectionId != nil && *query.CollectionId >= 0 {
|
||||||
|
tx.Where("collection_id = ?", query.CollectionId)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
abCIds := make([]uint, 0)
|
||||||
|
for _, ab := range res.AddressBooks {
|
||||||
|
abCIds = append(abCIds, ab.CollectionId)
|
||||||
|
}
|
||||||
|
response.Success(c, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create 创建地址簿
|
||||||
|
// @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/my/address_book/create [post]
|
||||||
|
// @Security token
|
||||||
|
func (ct *AddressBook) Create(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
|
||||||
|
}
|
||||||
|
t := f.ToAddressBook()
|
||||||
|
u := service.AllService.UserService.CurUser(c)
|
||||||
|
t.UserId = u.Id
|
||||||
|
if t.CollectionId > 0 && !service.AllService.AddressBookService.CheckCollectionOwner(t.UserId, t.CollectionId) {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ex := service.AllService.AddressBookService.InfoByUserIdAndIdAndCid(t.UserId, t.Id, t.CollectionId)
|
||||||
|
if ex.RowId > 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemExists"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err := service.AllService.AddressBookService.Create(t)
|
||||||
|
if err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Success(c, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update 编辑
|
||||||
|
// @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/my/address_book/update [post]
|
||||||
|
// @Security token
|
||||||
|
func (ct *AddressBook) Update(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
|
||||||
|
}
|
||||||
|
if f.RowId == 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
u := service.AllService.UserService.CurUser(c)
|
||||||
|
if f.UserId != u.Id {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ex := service.AllService.AddressBookService.InfoByRowId(f.RowId)
|
||||||
|
if ex.RowId == 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ex.UserId != u.Id {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t := f.ToAddressBook()
|
||||||
|
if t.CollectionId > 0 && !service.AllService.AddressBookService.CheckCollectionOwner(t.UserId, t.CollectionId) {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err := service.AllService.AddressBookService.UpdateAll(t)
|
||||||
|
if err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Success(c, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete 删除
|
||||||
|
// @Tags 我的地址簿
|
||||||
|
// @Summary 地址簿删除
|
||||||
|
// @Description 地址簿删除
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param body body admin.AddressBookForm true "地址簿信息"
|
||||||
|
// @Success 200 {object} response.Response
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/my/address_book/delete [post]
|
||||||
|
// @Security token
|
||||||
|
func (ct *AddressBook) Delete(c *gin.Context) {
|
||||||
|
f := &admin.AddressBookForm{}
|
||||||
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
id := f.RowId
|
||||||
|
errList := global.Validator.ValidVar(c, id, "required,gt=0")
|
||||||
|
if len(errList) > 0 {
|
||||||
|
response.Fail(c, 101, errList[0])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ex := service.AllService.AddressBookService.InfoByRowId(f.RowId)
|
||||||
|
if ex.RowId == 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
u := service.AllService.UserService.CurUser(c)
|
||||||
|
if ex.UserId != u.Id {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err := service.AllService.AddressBookService.Delete(ex)
|
||||||
|
if err == nil {
|
||||||
|
response.Success(c, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
func (ct *AddressBook) BatchCreateFromPeers(c *gin.Context) {
|
func (ct *AddressBook) BatchCreateFromPeers(c *gin.Context) {
|
||||||
f := &admin.BatchCreateFromPeersForm{}
|
f := &admin.BatchCreateFromPeersForm{}
|
||||||
if err := c.ShouldBindJSON(f); err != nil {
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
@@ -57,3 +245,27 @@ func (ct *AddressBook) BatchCreateFromPeers(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
response.Success(c, nil)
|
response.Success(c, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ct *AddressBook) BatchUpdateTags(c *gin.Context) {
|
||||||
|
f := &admin.BatchUpdateTagsForm{}
|
||||||
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
u := service.AllService.UserService.CurUser(c)
|
||||||
|
|
||||||
|
abs := service.AllService.AddressBookService.List(1, 999, func(tx *gorm.DB) {
|
||||||
|
tx.Where("row_id in ?", f.RowIds)
|
||||||
|
tx.Where("user_id = ?", u.Id)
|
||||||
|
})
|
||||||
|
if abs.Total == 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err := service.AllService.AddressBookService.BatchUpdateTags(abs.AddressBooks, f.Tags)
|
||||||
|
if err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Success(c, nil)
|
||||||
|
}
|
||||||
|
|||||||
162
http/controller/admin/my/addressBookCollection.go
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
package my
|
||||||
|
|
||||||
|
import (
|
||||||
|
"Gwen/global"
|
||||||
|
"Gwen/http/request/admin"
|
||||||
|
"Gwen/http/response"
|
||||||
|
"Gwen/model"
|
||||||
|
"Gwen/service"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AddressBookCollection struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create 创建地址簿名称
|
||||||
|
// @Tags 我的地址簿名称
|
||||||
|
// @Summary 创建地址簿名称
|
||||||
|
// @Description 创建地址簿名称
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param body body model.AddressBookCollection true "地址簿名称信息"
|
||||||
|
// @Success 200 {object} response.Response{data=model.AddressBookCollection}
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/my/address_book_collection/create [post]
|
||||||
|
// @Security token
|
||||||
|
func (abc *AddressBookCollection) Create(c *gin.Context) {
|
||||||
|
f := &model.AddressBookCollection{}
|
||||||
|
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)
|
||||||
|
f.UserId = u.Id
|
||||||
|
err := service.AllService.AddressBookService.CreateCollection(f)
|
||||||
|
if err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Success(c, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// List 列表
|
||||||
|
// @Tags 我的地址簿名称
|
||||||
|
// @Summary 地址簿名称列表
|
||||||
|
// @Description 地址簿名称列表
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param page query int false "页码"
|
||||||
|
// @Param page_size query int false "页大小"
|
||||||
|
// @Success 200 {object} response.Response{data=model.AddressBookCollectionList}
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/my/address_book_collection/list [get]
|
||||||
|
// @Security token
|
||||||
|
func (abc *AddressBookCollection) List(c *gin.Context) {
|
||||||
|
query := &admin.AddressBookCollectionQuery{}
|
||||||
|
if err := c.ShouldBindQuery(query); err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
u := service.AllService.UserService.CurUser(c)
|
||||||
|
query.UserId = int(u.Id)
|
||||||
|
res := service.AllService.AddressBookService.ListCollection(query.Page, query.PageSize, func(tx *gorm.DB) {
|
||||||
|
tx.Where("user_id = ?", query.UserId)
|
||||||
|
})
|
||||||
|
response.Success(c, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update 编辑
|
||||||
|
// @Tags 我的地址簿名称
|
||||||
|
// @Summary 地址簿名称编辑
|
||||||
|
// @Description 地址簿名称编辑
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param body body model.AddressBookCollection true "地址簿名称信息"
|
||||||
|
// @Success 200 {object} response.Response{data=model.AddressBookCollection}
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/my/address_book_collection/update [post]
|
||||||
|
// @Security token
|
||||||
|
func (abc *AddressBookCollection) Update(c *gin.Context) {
|
||||||
|
f := &model.AddressBookCollection{}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
if f.Id == 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
u := service.AllService.UserService.CurUser(c)
|
||||||
|
if f.UserId != u.Id {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ex := service.AllService.AddressBookService.CollectionInfoById(f.Id)
|
||||||
|
if ex.Id == 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ex.UserId != u.Id {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err := service.AllService.AddressBookService.UpdateCollection(f)
|
||||||
|
if err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Success(c, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete 删除
|
||||||
|
// @Tags 我的地址簿名称
|
||||||
|
// @Summary 地址簿名称删除
|
||||||
|
// @Description 地址簿名称删除
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param body body model.AddressBookCollection true "地址簿名称信息"
|
||||||
|
// @Success 200 {object} response.Response
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/my/address_book_collection/delete [post]
|
||||||
|
// @Security token
|
||||||
|
func (abc *AddressBookCollection) Delete(c *gin.Context) {
|
||||||
|
f := &model.AddressBookCollection{}
|
||||||
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
id := f.Id
|
||||||
|
errList := global.Validator.ValidVar(c, id, "required,gt=0")
|
||||||
|
if len(errList) > 0 {
|
||||||
|
response.Fail(c, 101, errList[0])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ex := service.AllService.AddressBookService.CollectionInfoById(f.Id)
|
||||||
|
if ex.Id == 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
u := service.AllService.UserService.CurUser(c)
|
||||||
|
if ex.UserId != u.Id {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err := service.AllService.AddressBookService.DeleteCollection(ex)
|
||||||
|
if err == nil {
|
||||||
|
response.Success(c, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
|
}
|
||||||
228
http/controller/admin/my/addressBookCollectionRule.go
Normal file
@@ -0,0 +1,228 @@
|
|||||||
|
package my
|
||||||
|
|
||||||
|
import (
|
||||||
|
"Gwen/global"
|
||||||
|
"Gwen/http/request/admin"
|
||||||
|
"Gwen/http/response"
|
||||||
|
"Gwen/model"
|
||||||
|
"Gwen/service"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AddressBookCollectionRule struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// List 列表
|
||||||
|
// @Tags 我的地址簿规则
|
||||||
|
// @Summary 地址簿规则列表
|
||||||
|
// @Description 地址簿规则列表
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param page query int false "页码"
|
||||||
|
// @Param page_size query int false "页大小"
|
||||||
|
// @Param is_my query int false "是否是我的"
|
||||||
|
// @Param user_id query int false "用户id"
|
||||||
|
// @Param collection_id query int false "地址簿集合id"
|
||||||
|
// @Success 200 {object} response.Response{data=model.AddressBookCollectionList}
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/my/address_book_collection_rule/list [get]
|
||||||
|
// @Security token
|
||||||
|
func (abcr *AddressBookCollectionRule) List(c *gin.Context) {
|
||||||
|
query := &admin.AddressBookCollectionRuleQuery{}
|
||||||
|
if err := c.ShouldBindQuery(query); err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
u := service.AllService.UserService.CurUser(c)
|
||||||
|
query.UserId = int(u.Id)
|
||||||
|
|
||||||
|
res := service.AllService.AddressBookService.ListRules(query.Page, query.PageSize, func(tx *gorm.DB) {
|
||||||
|
tx.Where("user_id = ?", query.UserId)
|
||||||
|
if query.CollectionId > 0 {
|
||||||
|
tx.Where("collection_id = ?", query.CollectionId)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
response.Success(c, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create 创建地址簿规则
|
||||||
|
// @Tags 我的地址簿规则
|
||||||
|
// @Summary 创建地址簿规则
|
||||||
|
// @Description 创建地址簿规则
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param body body model.AddressBookCollectionRule true "地址簿规则信息"
|
||||||
|
// @Success 200 {object} response.Response{data=model.AddressBookCollection}
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/my/address_book_collection_rule/create [post]
|
||||||
|
// @Security token
|
||||||
|
func (abcr *AddressBookCollectionRule) Create(c *gin.Context) {
|
||||||
|
f := &model.AddressBookCollectionRule{}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
if f.Type != model.ShareAddressBookRuleTypePersonal && f.Type != model.ShareAddressBookRuleTypeGroup {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//t := f.ToAddressBookCollection()
|
||||||
|
t := f
|
||||||
|
u := service.AllService.UserService.CurUser(c)
|
||||||
|
t.UserId = u.Id
|
||||||
|
msg, res := abcr.CheckForm(u, t)
|
||||||
|
if !res {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, msg))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err := service.AllService.AddressBookService.CreateRule(t)
|
||||||
|
if err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Success(c, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (abcr *AddressBookCollectionRule) CheckForm(u *model.User, t *model.AddressBookCollectionRule) (string, bool) {
|
||||||
|
if t.UserId != u.Id {
|
||||||
|
return "NoAccess", false
|
||||||
|
}
|
||||||
|
if t.CollectionId > 0 && !service.AllService.AddressBookService.CheckCollectionOwner(t.UserId, t.CollectionId) {
|
||||||
|
return "ParamsError", false
|
||||||
|
}
|
||||||
|
|
||||||
|
//check to_id
|
||||||
|
if t.Type == model.ShareAddressBookRuleTypePersonal {
|
||||||
|
if t.ToId == t.UserId {
|
||||||
|
return "ParamsError", false
|
||||||
|
}
|
||||||
|
tou := service.AllService.UserService.InfoById(t.ToId)
|
||||||
|
if tou.Id == 0 {
|
||||||
|
return "ItemNotFound", false
|
||||||
|
}
|
||||||
|
//非管理员不能分享给非本组织用户
|
||||||
|
if tou.GroupId != u.GroupId {
|
||||||
|
return "NoAccess", false
|
||||||
|
}
|
||||||
|
} else if t.Type == model.ShareAddressBookRuleTypeGroup {
|
||||||
|
//非管理员不能分享给其他组
|
||||||
|
if t.ToId != u.GroupId {
|
||||||
|
return "NoAccess", false
|
||||||
|
}
|
||||||
|
|
||||||
|
tog := service.AllService.GroupService.InfoById(t.ToId)
|
||||||
|
if tog.Id == 0 {
|
||||||
|
return "ItemNotFound", false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return "ParamsError", false
|
||||||
|
}
|
||||||
|
// 重复检查
|
||||||
|
ex := service.AllService.AddressBookService.RulePersonalInfoByToIdAndCid(t.ToId, t.CollectionId)
|
||||||
|
if t.Id == 0 && ex.Id > 0 {
|
||||||
|
return "ItemExists", false
|
||||||
|
}
|
||||||
|
if t.Id > 0 && ex.Id > 0 && t.Id != ex.Id {
|
||||||
|
return "ItemExists", false
|
||||||
|
}
|
||||||
|
return "", true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update 编辑
|
||||||
|
// @Tags 我的地址簿规则
|
||||||
|
// @Summary 地址簿规则编辑
|
||||||
|
// @Description 地址簿规则编辑
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param body body model.AddressBookCollectionRule true "地址簿规则信息"
|
||||||
|
// @Success 200 {object} response.Response{data=model.AddressBookCollection}
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/my/address_book_collection_rule/update [post]
|
||||||
|
// @Security token
|
||||||
|
func (abcr *AddressBookCollectionRule) Update(c *gin.Context) {
|
||||||
|
f := &model.AddressBookCollectionRule{}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
if f.Id == 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
u := service.AllService.UserService.CurUser(c)
|
||||||
|
|
||||||
|
ex := service.AllService.AddressBookService.RuleInfoById(f.Id)
|
||||||
|
if ex.Id == 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ex.UserId != u.Id {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t := f
|
||||||
|
msg, res := abcr.CheckForm(u, t)
|
||||||
|
if !res {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, msg))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err := service.AllService.AddressBookService.UpdateRule(t)
|
||||||
|
if err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Success(c, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete 删除
|
||||||
|
// @Tags 我的地址簿规则
|
||||||
|
// @Summary 地址簿规则删除
|
||||||
|
// @Description 地址簿规则删除
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param body body model.AddressBookCollectionRule true "地址簿规则信息"
|
||||||
|
// @Success 200 {object} response.Response
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/my/address_book_collection_rule/delete [post]
|
||||||
|
// @Security token
|
||||||
|
func (abcr *AddressBookCollectionRule) Delete(c *gin.Context) {
|
||||||
|
f := &model.AddressBookCollectionRule{}
|
||||||
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
id := f.Id
|
||||||
|
errList := global.Validator.ValidVar(c, id, "required,gt=0")
|
||||||
|
if len(errList) > 0 {
|
||||||
|
response.Fail(c, 101, errList[0])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ex := service.AllService.AddressBookService.RuleInfoById(f.Id)
|
||||||
|
if ex.Id == 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
u := service.AllService.UserService.CurUser(c)
|
||||||
|
if ex.UserId != u.Id {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err := service.AllService.AddressBookService.DeleteRule(ex)
|
||||||
|
if err == nil {
|
||||||
|
response.Success(c, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
|
}
|
||||||
113
http/controller/admin/my/loginLog.go
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
package my
|
||||||
|
|
||||||
|
import (
|
||||||
|
"Gwen/global"
|
||||||
|
"Gwen/http/request/admin"
|
||||||
|
"Gwen/http/response"
|
||||||
|
"Gwen/model"
|
||||||
|
"Gwen/service"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LoginLog struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// List 列表
|
||||||
|
// @Tags 我的登录日志
|
||||||
|
// @Summary 登录日志列表
|
||||||
|
// @Description 登录日志列表
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param page query int false "页码"
|
||||||
|
// @Param page_size query int false "页大小"
|
||||||
|
// @Param user_id query int false "用户ID"
|
||||||
|
// @Success 200 {object} response.Response{data=model.LoginLogList}
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/my/login_log/list [get]
|
||||||
|
// @Security token
|
||||||
|
func (ct *LoginLog) List(c *gin.Context) {
|
||||||
|
query := &admin.LoginLogQuery{}
|
||||||
|
if err := c.ShouldBindQuery(query); err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
u := service.AllService.UserService.CurUser(c)
|
||||||
|
res := service.AllService.LoginLogService.List(query.Page, query.PageSize, func(tx *gorm.DB) {
|
||||||
|
tx.Where("user_id = ? and is_deleted = ?", u.Id, model.IsDeletedNo)
|
||||||
|
tx.Order("id desc")
|
||||||
|
})
|
||||||
|
response.Success(c, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete 删除
|
||||||
|
// @Tags 我的登录日志
|
||||||
|
// @Summary 登录日志删除
|
||||||
|
// @Description 登录日志删除
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param body body model.LoginLog true "登录日志信息"
|
||||||
|
// @Success 200 {object} response.Response
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/my/login_log/delete [post]
|
||||||
|
// @Security token
|
||||||
|
func (ct *LoginLog) Delete(c *gin.Context) {
|
||||||
|
f := &model.LoginLog{}
|
||||||
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
id := f.Id
|
||||||
|
errList := global.Validator.ValidVar(c, id, "required,gt=0")
|
||||||
|
if len(errList) > 0 {
|
||||||
|
response.Fail(c, 101, errList[0])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
l := service.AllService.LoginLogService.InfoById(f.Id)
|
||||||
|
if l.Id == 0 || l.IsDeleted == model.IsDeletedYes {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
u := service.AllService.UserService.CurUser(c)
|
||||||
|
if l.UserId != u.Id {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err := service.AllService.LoginLogService.SoftDelete(l)
|
||||||
|
if err == nil {
|
||||||
|
response.Success(c, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Fail(c, 101, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// BatchDelete 删除
|
||||||
|
// @Tags 我的登录日志
|
||||||
|
// @Summary 登录日志批量删除
|
||||||
|
// @Description 登录日志批量删除
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param body body admin.LoginLogIds true "登录日志"
|
||||||
|
// @Success 200 {object} response.Response
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/my/login_log/batchDelete [post]
|
||||||
|
// @Security token
|
||||||
|
func (ct *LoginLog) BatchDelete(c *gin.Context) {
|
||||||
|
f := &admin.LoginLogIds{}
|
||||||
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(f.Ids) == 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
u := service.AllService.UserService.CurUser(c)
|
||||||
|
err := service.AllService.LoginLogService.BatchSoftDelete(u.Id, f.Ids)
|
||||||
|
if err == nil {
|
||||||
|
response.Success(c, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Fail(c, 101, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
59
http/controller/admin/my/peer.go
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
package my
|
||||||
|
|
||||||
|
import (
|
||||||
|
"Gwen/http/request/admin"
|
||||||
|
"Gwen/http/response"
|
||||||
|
"Gwen/service"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Peer struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// List 列表
|
||||||
|
// @Tags 我的设备
|
||||||
|
// @Summary 设备列表
|
||||||
|
// @Description 设备列表
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param page query int false "页码"
|
||||||
|
// @Param page_size query int false "页大小"
|
||||||
|
// @Param time_ago query int false "时间"
|
||||||
|
// @Param id query string false "ID"
|
||||||
|
// @Param hostname query string false "主机名"
|
||||||
|
// @Param uuids query string false "uuids 用逗号分隔"
|
||||||
|
// @Success 200 {object} response.Response{data=model.PeerList}
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/my/peer/list [get]
|
||||||
|
// @Security token
|
||||||
|
func (ct *Peer) List(c *gin.Context) {
|
||||||
|
query := &admin.PeerQuery{}
|
||||||
|
if err := c.ShouldBindQuery(query); err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
u := service.AllService.UserService.CurUser(c)
|
||||||
|
res := service.AllService.PeerService.List(query.Page, query.PageSize, func(tx *gorm.DB) {
|
||||||
|
tx.Where("user_id = ?", u.Id)
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
if query.Id != "" {
|
||||||
|
tx.Where("id like ?", "%"+query.Id+"%")
|
||||||
|
}
|
||||||
|
if query.Hostname != "" {
|
||||||
|
tx.Where("hostname like ?", "%"+query.Hostname+"%")
|
||||||
|
}
|
||||||
|
if query.Uuids != "" {
|
||||||
|
tx.Where("uuid in (?)", query.Uuids)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
response.Success(c, res)
|
||||||
|
}
|
||||||
@@ -13,7 +13,7 @@ type ShareRecord struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// List 分享记录列表
|
// List 分享记录列表
|
||||||
// @Tags 我的
|
// @Tags 我的分享记录
|
||||||
// @Summary 分享记录列表
|
// @Summary 分享记录列表
|
||||||
// @Description 分享记录列表
|
// @Description 分享记录列表
|
||||||
// @Accept json
|
// @Accept json
|
||||||
@@ -38,7 +38,7 @@ func (sr *ShareRecord) List(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Delete 分享记录删除
|
// Delete 分享记录删除
|
||||||
// @Tags 我的
|
// @Tags 我的分享记录
|
||||||
// @Summary 分享记录删除
|
// @Summary 分享记录删除
|
||||||
// @Description 分享记录删除
|
// @Description 分享记录删除
|
||||||
// @Accept json
|
// @Accept json
|
||||||
|
|||||||
176
http/controller/admin/my/tag.go
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
package my
|
||||||
|
|
||||||
|
import (
|
||||||
|
"Gwen/global"
|
||||||
|
"Gwen/http/request/admin"
|
||||||
|
"Gwen/http/response"
|
||||||
|
"Gwen/service"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Tag struct{}
|
||||||
|
|
||||||
|
// List 列表
|
||||||
|
// @Tags 我的标签
|
||||||
|
// @Summary 标签列表
|
||||||
|
// @Description 标签列表
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param page query int false "页码"
|
||||||
|
// @Param page_size query int false "页大小"
|
||||||
|
// @Param is_my query int false "是否是我的"
|
||||||
|
// @Param user_id query int false "用户id"
|
||||||
|
// @Success 200 {object} response.Response{data=model.TagList}
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/my/tag/list [get]
|
||||||
|
// @Security token
|
||||||
|
func (ct *Tag) List(c *gin.Context) {
|
||||||
|
query := &admin.TagQuery{}
|
||||||
|
if err := c.ShouldBindQuery(query); err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
u := service.AllService.UserService.CurUser(c)
|
||||||
|
query.UserId = int(u.Id)
|
||||||
|
res := service.AllService.TagService.List(query.Page, query.PageSize, func(tx *gorm.DB) {
|
||||||
|
tx.Preload("Collection", func(txc *gorm.DB) *gorm.DB {
|
||||||
|
return txc.Select("id,name")
|
||||||
|
})
|
||||||
|
tx.Where("user_id = ?", query.UserId)
|
||||||
|
if query.CollectionId != nil && *query.CollectionId >= 0 {
|
||||||
|
tx.Where("collection_id = ?", query.CollectionId)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
response.Success(c, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create 创建标签
|
||||||
|
// @Tags 我的标签
|
||||||
|
// @Summary 创建标签
|
||||||
|
// @Description 创建标签
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param body body admin.TagForm true "标签信息"
|
||||||
|
// @Success 200 {object} response.Response{data=model.Tag}
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/my/tag/create [post]
|
||||||
|
// @Security token
|
||||||
|
func (ct *Tag) Create(c *gin.Context) {
|
||||||
|
f := &admin.TagForm{}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
t := f.ToTag()
|
||||||
|
u := service.AllService.UserService.CurUser(c)
|
||||||
|
t.UserId = u.Id
|
||||||
|
err := service.AllService.TagService.Create(t)
|
||||||
|
if err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Success(c, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update 编辑
|
||||||
|
// @Tags 我的标签
|
||||||
|
// @Summary 标签编辑
|
||||||
|
// @Description 标签编辑
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param body body admin.TagForm true "标签信息"
|
||||||
|
// @Success 200 {object} response.Response{data=model.Tag}
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/my/tag/update [post]
|
||||||
|
// @Security token
|
||||||
|
func (ct *Tag) Update(c *gin.Context) {
|
||||||
|
f := &admin.TagForm{}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
if f.Id == 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
u := service.AllService.UserService.CurUser(c)
|
||||||
|
if f.UserId != u.Id {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ex := service.AllService.TagService.InfoById(f.Id)
|
||||||
|
if ex.Id == 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ex.UserId != u.Id {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
t := f.ToTag()
|
||||||
|
if t.CollectionId > 0 && !service.AllService.AddressBookService.CheckCollectionOwner(t.UserId, t.CollectionId) {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err := service.AllService.TagService.Update(t)
|
||||||
|
if err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Success(c, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete 删除
|
||||||
|
// @Tags 标签
|
||||||
|
// @Summary 标签删除
|
||||||
|
// @Description 标签删除
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param body body admin.TagForm true "标签信息"
|
||||||
|
// @Success 200 {object} response.Response
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/my/tag/delete [post]
|
||||||
|
// @Security token
|
||||||
|
func (ct *Tag) Delete(c *gin.Context) {
|
||||||
|
f := &admin.TagForm{}
|
||||||
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
id := f.Id
|
||||||
|
errList := global.Validator.ValidVar(c, id, "required,gt=0")
|
||||||
|
if len(errList) > 0 {
|
||||||
|
response.Fail(c, 101, errList[0])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ex := service.AllService.TagService.InfoById(f.Id)
|
||||||
|
if ex.Id == 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
u := service.AllService.UserService.CurUser(c)
|
||||||
|
if ex.UserId != u.Id {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err := service.AllService.TagService.Delete(ex)
|
||||||
|
if err == nil {
|
||||||
|
response.Success(c, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Fail(c, 101, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -2,45 +2,127 @@ package admin
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"Gwen/global"
|
"Gwen/global"
|
||||||
|
"Gwen/http/request/admin"
|
||||||
"Gwen/http/response"
|
"Gwen/http/response"
|
||||||
|
"Gwen/model"
|
||||||
|
"Gwen/service"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Rustdesk struct {
|
type Rustdesk struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServerConfig RUSTDESK服务配置
|
type RustdeskCmd struct {
|
||||||
// @Tags ADMIN
|
Cmd string `json:"cmd"`
|
||||||
// @Summary RUSTDESK服务配置
|
Option string `json:"option"`
|
||||||
// @Description 服务配置,给webclient提供api-server
|
Target string `json:"target"`
|
||||||
// @Accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Success 200 {object} response.Response
|
|
||||||
// @Failure 500 {object} response.Response
|
|
||||||
// @Router /admin/server-config [get]
|
|
||||||
// @Security token
|
|
||||||
func (r *Rustdesk) ServerConfig(c *gin.Context) {
|
|
||||||
cf := &response.ServerConfigResponse{
|
|
||||||
IdServer: global.Config.Rustdesk.IdServer,
|
|
||||||
Key: global.Config.Rustdesk.Key,
|
|
||||||
RelayServer: global.Config.Rustdesk.RelayServer,
|
|
||||||
ApiServer: global.Config.Rustdesk.ApiServer,
|
|
||||||
}
|
|
||||||
response.Success(c, cf)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AppConfig APP服务配置
|
func (r *Rustdesk) CmdList(c *gin.Context) {
|
||||||
// @Tags ADMIN
|
q := &admin.PageQuery{}
|
||||||
// @Summary APP服务配置
|
if err := c.ShouldBindQuery(q); err != nil {
|
||||||
// @Description APP服务配置
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
// @Accept json
|
return
|
||||||
// @Produce json
|
}
|
||||||
// @Success 200 {object} response.Response
|
res := service.AllService.ServerCmdService.List(q.Page, 9999)
|
||||||
// @Failure 500 {object} response.Response
|
//在列表前添加系统命令
|
||||||
// @Router /admin/app-config [get]
|
list := make([]*model.ServerCmd, 0)
|
||||||
// @Security token
|
list = append(list, model.SysIdServerCmds...)
|
||||||
func (r *Rustdesk) AppConfig(c *gin.Context) {
|
list = append(list, model.SysRelayServerCmds...)
|
||||||
response.Success(c, &gin.H{
|
list = append(list, res.ServerCmds...)
|
||||||
"web_client": global.Config.App.WebClient,
|
res.ServerCmds = list
|
||||||
})
|
response.Success(c, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Rustdesk) CmdDelete(c *gin.Context) {
|
||||||
|
f := &model.ServerCmd{}
|
||||||
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if f.Id == 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ex := service.AllService.ServerCmdService.Info(f.Id)
|
||||||
|
if ex.Id == 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err := service.AllService.ServerCmdService.Delete(ex)
|
||||||
|
if err != nil {
|
||||||
|
response.Fail(c, 101, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Success(c, nil)
|
||||||
|
}
|
||||||
|
func (r *Rustdesk) CmdCreate(c *gin.Context) {
|
||||||
|
f := &model.ServerCmd{}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
err := service.AllService.ServerCmdService.Create(f)
|
||||||
|
if err != nil {
|
||||||
|
response.Fail(c, 101, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Success(c, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Rustdesk) CmdUpdate(c *gin.Context) {
|
||||||
|
f := &model.ServerCmd{}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
ex := service.AllService.ServerCmdService.Info(f.Id)
|
||||||
|
if ex.Id == 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err := service.AllService.ServerCmdService.Update(f)
|
||||||
|
if err != nil {
|
||||||
|
response.Fail(c, 101, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Success(c, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Rustdesk) SendCmd(c *gin.Context) {
|
||||||
|
rc := &RustdeskCmd{}
|
||||||
|
if err := c.ShouldBindJSON(rc); err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if rc.Cmd == "" {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if rc.Target == "" {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if rc.Target != model.ServerCmdTargetIdServer && rc.Target != model.ServerCmdTargetRelayServer {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
res, err := service.AllService.ServerCmdService.SendCmd(rc.Target, rc.Cmd, rc.Option)
|
||||||
|
if err != nil {
|
||||||
|
response.Fail(c, 101, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Success(c, res)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,9 +64,9 @@ func (ct *Tag) Create(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
t := f.ToTag()
|
t := f.ToTag()
|
||||||
u := service.AllService.UserService.CurUser(c)
|
if t.UserId == 0 {
|
||||||
if !service.AllService.UserService.IsAdmin(u) || t.UserId == 0 {
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||||
t.UserId = u.Id
|
return
|
||||||
}
|
}
|
||||||
err := service.AllService.TagService.Create(t)
|
err := service.AllService.TagService.Create(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -96,10 +96,6 @@ func (ct *Tag) List(c *gin.Context) {
|
|||||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
u := service.AllService.UserService.CurUser(c)
|
|
||||||
if !service.AllService.UserService.IsAdmin(u) || query.IsMy == 1 {
|
|
||||||
query.UserId = int(u.Id)
|
|
||||||
}
|
|
||||||
res := service.AllService.TagService.List(query.Page, query.PageSize, func(tx *gorm.DB) {
|
res := service.AllService.TagService.List(query.Page, query.PageSize, func(tx *gorm.DB) {
|
||||||
tx.Preload("Collection", func(txc *gorm.DB) *gorm.DB {
|
tx.Preload("Collection", func(txc *gorm.DB) *gorm.DB {
|
||||||
return txc.Select("id,name")
|
return txc.Select("id,name")
|
||||||
@@ -140,12 +136,12 @@ func (ct *Tag) Update(c *gin.Context) {
|
|||||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
t := f.ToTag()
|
ex := service.AllService.TagService.InfoById(f.Id)
|
||||||
u := service.AllService.UserService.CurUser(c)
|
if ex.Id == 0 {
|
||||||
if !service.AllService.UserService.IsAdmin(u) && t.UserId != u.Id {
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
t := f.ToTag()
|
||||||
err := service.AllService.TagService.Update(t)
|
err := service.AllService.TagService.Update(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
@@ -177,20 +173,15 @@ func (ct *Tag) Delete(c *gin.Context) {
|
|||||||
response.Fail(c, 101, errList[0])
|
response.Fail(c, 101, errList[0])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
t := service.AllService.TagService.InfoById(f.Id)
|
ex := service.AllService.TagService.InfoById(f.Id)
|
||||||
u := service.AllService.UserService.CurUser(c)
|
if ex.Id == 0 {
|
||||||
if !service.AllService.UserService.IsAdmin(u) && t.UserId != u.Id {
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if u.Id > 0 {
|
err := service.AllService.TagService.Delete(ex)
|
||||||
err := service.AllService.TagService.Delete(t)
|
if err == nil {
|
||||||
if err == nil {
|
response.Success(c, nil)
|
||||||
response.Success(c, nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
response.Fail(c, 101, err.Error())
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
response.Fail(c, 101, err.Error())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
@@ -295,51 +294,6 @@ func (ct *User) MyOauth(c *gin.Context) {
|
|||||||
response.Success(c, res)
|
response.Success(c, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MyPeer 列表
|
|
||||||
// @Tags 设备
|
|
||||||
// @Summary 我的设备列表
|
|
||||||
// @Description 我的设备列表
|
|
||||||
// @Accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param page query int false "页码"
|
|
||||||
// @Param page_size query int false "页大小"
|
|
||||||
// @Param time_ago query int false "时间"
|
|
||||||
// @Param id query string false "ID"
|
|
||||||
// @Param hostname query string false "主机名"
|
|
||||||
// @Param uuids query string false "uuids 用逗号分隔"
|
|
||||||
// @Success 200 {object} response.Response{data=model.PeerList}
|
|
||||||
// @Failure 500 {object} response.Response
|
|
||||||
// @Router /admin/user/myPeer [get]
|
|
||||||
// @Security token
|
|
||||||
func (ct *User) MyPeer(c *gin.Context) {
|
|
||||||
query := &admin.PeerQuery{}
|
|
||||||
if err := c.ShouldBindQuery(query); err != nil {
|
|
||||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
u := service.AllService.UserService.CurUser(c)
|
|
||||||
res := service.AllService.PeerService.ListFilterByUserId(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)
|
|
||||||
}
|
|
||||||
if query.Id != "" {
|
|
||||||
tx.Where("id like ?", "%"+query.Id+"%")
|
|
||||||
}
|
|
||||||
if query.Hostname != "" {
|
|
||||||
tx.Where("hostname like ?", "%"+query.Hostname+"%")
|
|
||||||
}
|
|
||||||
if query.Uuids != "" {
|
|
||||||
tx.Where("uuid in (?)", query.Uuids)
|
|
||||||
}
|
|
||||||
}, u.Id)
|
|
||||||
response.Success(c, res)
|
|
||||||
}
|
|
||||||
|
|
||||||
// groupUsers
|
// groupUsers
|
||||||
func (ct *User) GroupUsers(c *gin.Context) {
|
func (ct *User) GroupUsers(c *gin.Context) {
|
||||||
q := &admin.GroupUsersQuery{}
|
q := &admin.GroupUsersQuery{}
|
||||||
|
|||||||
@@ -32,8 +32,8 @@ type Ab struct {
|
|||||||
func (a *Ab) Ab(c *gin.Context) {
|
func (a *Ab) Ab(c *gin.Context) {
|
||||||
user := service.AllService.UserService.CurUser(c)
|
user := service.AllService.UserService.CurUser(c)
|
||||||
|
|
||||||
al := service.AllService.AddressBookService.ListByUserId(user.Id, 1, 1000)
|
al := service.AllService.AddressBookService.ListByUserIdAndCollectionId(user.Id, 0, 1, 1000)
|
||||||
tags := service.AllService.TagService.ListByUserId(user.Id)
|
tags := service.AllService.TagService.ListByUserIdAndCollectionId(user.Id, 0)
|
||||||
|
|
||||||
tagColors := map[string]uint{}
|
tagColors := map[string]uint{}
|
||||||
//将tags中的name转成一个以逗号分割的字符串
|
//将tags中的name转成一个以逗号分割的字符串
|
||||||
@@ -98,23 +98,6 @@ func (a *Ab) UpAb(c *gin.Context) {
|
|||||||
c.JSON(http.StatusOK, nil)
|
c.JSON(http.StatusOK, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tags
|
|
||||||
// @Tags 地址
|
|
||||||
// @Summary 标签
|
|
||||||
// @Description 标签
|
|
||||||
// @Accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Success 200 {object} []model.Tag
|
|
||||||
// @Failure 500 {object} response.ErrorResponse
|
|
||||||
// @Router /tags [post]
|
|
||||||
// @Security BearerAuth
|
|
||||||
func (a *Ab) Tags(c *gin.Context) {
|
|
||||||
user := service.AllService.UserService.CurUser(c)
|
|
||||||
|
|
||||||
tags := service.AllService.TagService.ListByUserId(user.Id)
|
|
||||||
c.JSON(http.StatusOK, tags.Tags)
|
|
||||||
}
|
|
||||||
|
|
||||||
// PTags
|
// PTags
|
||||||
// @Tags 地址[Personal]
|
// @Tags 地址[Personal]
|
||||||
// @Summary 标签
|
// @Summary 标签
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ func (i *WebClient) ServerConfig(c *gin.Context) {
|
|||||||
u := service.AllService.UserService.CurUser(c)
|
u := service.AllService.UserService.CurUser(c)
|
||||||
|
|
||||||
peers := map[string]*api.WebClientPeerPayload{}
|
peers := map[string]*api.WebClientPeerPayload{}
|
||||||
abs := service.AllService.AddressBookService.ListByUserId(u.Id, 1, 100)
|
abs := service.AllService.AddressBookService.ListByUserIdAndCollectionId(u.Id, 0, 1, 100)
|
||||||
for _, ab := range abs.AddressBooks {
|
for _, ab := range abs.AddressBooks {
|
||||||
pp := &api.WebClientPeerPayload{}
|
pp := &api.WebClientPeerPayload{}
|
||||||
pp.FromAddressBook(ab)
|
pp.FromAddressBook(ab)
|
||||||
@@ -64,12 +64,15 @@ func (i *WebClient) SharedPeer(c *gin.Context) {
|
|||||||
response.Fail(c, 101, "share not found")
|
response.Fail(c, 101, "share not found")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
//判断是否过期,created_at + expire > now
|
if sr.Expire != 0 {
|
||||||
ca := time.Time(sr.CreatedAt)
|
//判断是否过期,created_at + expire > now
|
||||||
if ca.Add(time.Second * time.Duration(sr.Expire)).Before(time.Now()) {
|
ca := time.Time(sr.CreatedAt)
|
||||||
response.Fail(c, 101, "share expired")
|
if ca.Add(time.Second * time.Duration(sr.Expire)).Before(time.Now()) {
|
||||||
return
|
response.Fail(c, 101, "share expired")
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ab := service.AllService.AddressBookService.InfoByUserIdAndId(sr.UserId, sr.PeerId)
|
ab := service.AllService.AddressBookService.InfoByUserIdAndId(sr.UserId, sr.PeerId)
|
||||||
if ab.RowId == 0 {
|
if ab.RowId == 0 {
|
||||||
response.Fail(c, 101, "peer not found")
|
response.Fail(c, 101, "peer not found")
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package web
|
|||||||
import (
|
import (
|
||||||
"Gwen/global"
|
"Gwen/global"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Index struct {
|
type Index struct {
|
||||||
@@ -14,11 +15,13 @@ func (i *Index) Index(c *gin.Context) {
|
|||||||
|
|
||||||
func (i *Index) ConfigJs(c *gin.Context) {
|
func (i *Index) ConfigJs(c *gin.Context) {
|
||||||
apiServer := global.Config.Rustdesk.ApiServer
|
apiServer := global.Config.Rustdesk.ApiServer
|
||||||
|
magicQueryonline := strconv.Itoa(global.Config.Rustdesk.WebclientMagicQueryonline)
|
||||||
tmp := `
|
tmp := `
|
||||||
localStorage.setItem('api-server', "` + apiServer + `")
|
localStorage.setItem('api-server', "` + apiServer + `")
|
||||||
const ws2_prefix = 'wc-'
|
const ws2_prefix = 'wc-'
|
||||||
localStorage.setItem(ws2_prefix+'api-server', "` + apiServer + `")
|
localStorage.setItem(ws2_prefix+'api-server', "` + apiServer + `")
|
||||||
`
|
|
||||||
|
window.webclient_magic_queryonline = ` + magicQueryonline + ``
|
||||||
|
|
||||||
c.String(200, tmp)
|
c.String(200, tmp)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"Gwen/global"
|
||||||
"Gwen/service"
|
"Gwen/service"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
@@ -27,7 +28,21 @@ func RustAuth() gin.HandlerFunc {
|
|||||||
//提取token,格式是Bearer {token}
|
//提取token,格式是Bearer {token}
|
||||||
//这里只是简单的提取
|
//这里只是简单的提取
|
||||||
token = token[7:]
|
token = token[7:]
|
||||||
|
|
||||||
//验证token
|
//验证token
|
||||||
|
|
||||||
|
//检查是否设置了jwt key
|
||||||
|
if len(global.Jwt.Key) > 0 {
|
||||||
|
uid, _ := service.AllService.UserService.VerifyJWT(token)
|
||||||
|
if uid == 0 {
|
||||||
|
c.JSON(401, gin.H{
|
||||||
|
"error": "Unauthorized",
|
||||||
|
})
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
user, ut := service.AllService.UserService.InfoByAccessToken(token)
|
user, ut := service.AllService.UserService.InfoByAccessToken(token)
|
||||||
if user.Id == 0 {
|
if user.Id == 0 {
|
||||||
c.JSON(401, gin.H{
|
c.JSON(401, gin.H{
|
||||||
@@ -38,7 +53,7 @@ func RustAuth() gin.HandlerFunc {
|
|||||||
}
|
}
|
||||||
if !service.AllService.UserService.CheckUserEnable(user) {
|
if !service.AllService.UserService.CheckUserEnable(user) {
|
||||||
c.JSON(401, gin.H{
|
c.JSON(401, gin.H{
|
||||||
"error": "账号已被禁用",
|
"error": "Unauthorized",
|
||||||
})
|
})
|
||||||
c.Abort()
|
c.Abort()
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ type Login struct {
|
|||||||
Username string `json:"username" validate:"required" label:"用户名"`
|
Username string `json:"username" validate:"required" label:"用户名"`
|
||||||
Password string `json:"password,omitempty" validate:"required" label:"密码"`
|
Password string `json:"password,omitempty" validate:"required" label:"密码"`
|
||||||
Platform string `json:"platform" label:"平台"`
|
Platform string `json:"platform" label:"平台"`
|
||||||
|
Captcha string `json:"captcha,omitempty" label:"验证码"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type LoginLogQuery struct {
|
type LoginLogQuery struct {
|
||||||
|
|||||||
@@ -18,11 +18,6 @@ func (lp *LoginPayload) FromUser(user *model.User) {
|
|||||||
lp.Nickname = user.Nickname
|
lp.Nickname = user.Nickname
|
||||||
}
|
}
|
||||||
|
|
||||||
var UserRouteNames = []string{
|
|
||||||
"MyTagList", "MyAddressBookList", "MyInfo", "MyAddressBookCollection", "MyPeer", "MyShareRecordList",
|
|
||||||
}
|
|
||||||
var AdminRouteNames = []string{"*"}
|
|
||||||
|
|
||||||
type UserOauthItem struct {
|
type UserOauthItem struct {
|
||||||
Op string `json:"op"`
|
Op string `json:"op"`
|
||||||
Status int `json:"status"`
|
Status int `json:"status"`
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package router
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
_ "Gwen/docs/admin"
|
_ "Gwen/docs/admin"
|
||||||
|
"Gwen/global"
|
||||||
"Gwen/http/controller/admin"
|
"Gwen/http/controller/admin"
|
||||||
"Gwen/http/controller/admin/my"
|
"Gwen/http/controller/admin/my"
|
||||||
"Gwen/http/middleware"
|
"Gwen/http/middleware"
|
||||||
@@ -14,7 +15,9 @@ func Init(g *gin.Engine) {
|
|||||||
|
|
||||||
//swagger
|
//swagger
|
||||||
//g.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
|
//g.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
|
||||||
g.GET("/admin/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler, ginSwagger.InstanceName("admin")))
|
if global.Config.App.ShowSwagger == 1 {
|
||||||
|
g.GET("/admin/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler, ginSwagger.InstanceName("admin")))
|
||||||
|
}
|
||||||
|
|
||||||
adg := g.Group("/api/admin")
|
adg := g.Group("/api/admin")
|
||||||
LoginBind(adg)
|
LoginBind(adg)
|
||||||
@@ -43,12 +46,24 @@ func Init(g *gin.Engine) {
|
|||||||
ShareRecordBind(adg)
|
ShareRecordBind(adg)
|
||||||
MyBind(adg)
|
MyBind(adg)
|
||||||
|
|
||||||
|
RustdeskCmdBind(adg)
|
||||||
|
|
||||||
//访问静态文件
|
//访问静态文件
|
||||||
//g.StaticFS("/upload", http.Dir(global.Config.Gin.ResourcesPath+"/upload"))
|
//g.StaticFS("/upload", http.Dir(global.Config.Gin.ResourcesPath+"/upload"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func RustdeskCmdBind(adg *gin.RouterGroup) {
|
||||||
|
cont := &admin.Rustdesk{}
|
||||||
|
rg := adg.Group("/rustdesk")
|
||||||
|
rg.POST("/sendCmd", cont.SendCmd)
|
||||||
|
rg.GET("/cmdList", cont.CmdList)
|
||||||
|
rg.POST("/cmdDelete", cont.CmdDelete)
|
||||||
|
rg.POST("/cmdCreate", cont.CmdCreate)
|
||||||
|
}
|
||||||
func LoginBind(rg *gin.RouterGroup) {
|
func LoginBind(rg *gin.RouterGroup) {
|
||||||
cont := &admin.Login{}
|
cont := &admin.Login{}
|
||||||
rg.POST("/login", cont.Login)
|
rg.POST("/login", cont.Login)
|
||||||
|
rg.GET("/captcha", cont.Captcha)
|
||||||
rg.POST("/logout", cont.Logout)
|
rg.POST("/logout", cont.Logout)
|
||||||
rg.GET("/login-options", cont.LoginOptions)
|
rg.GET("/login-options", cont.LoginOptions)
|
||||||
rg.POST("/oidc/auth", cont.OidcAuth)
|
rg.POST("/oidc/auth", cont.OidcAuth)
|
||||||
@@ -62,7 +77,7 @@ func UserBind(rg *gin.RouterGroup) {
|
|||||||
aR.GET("/current", cont.Current)
|
aR.GET("/current", cont.Current)
|
||||||
aR.POST("/changeCurPwd", cont.ChangeCurPwd)
|
aR.POST("/changeCurPwd", cont.ChangeCurPwd)
|
||||||
aR.POST("/myOauth", cont.MyOauth)
|
aR.POST("/myOauth", cont.MyOauth)
|
||||||
aR.GET("/myPeer", cont.MyPeer)
|
//aR.GET("/myPeer", cont.MyPeer)
|
||||||
aR.POST("/groupUsers", cont.GroupUsers)
|
aR.POST("/groupUsers", cont.GroupUsers)
|
||||||
}
|
}
|
||||||
aRP := rg.Group("/user").Use(middleware.AdminPrivilege())
|
aRP := rg.Group("/user").Use(middleware.AdminPrivilege())
|
||||||
@@ -90,7 +105,7 @@ func GroupBind(rg *gin.RouterGroup) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TagBind(rg *gin.RouterGroup) {
|
func TagBind(rg *gin.RouterGroup) {
|
||||||
aR := rg.Group("/tag")
|
aR := rg.Group("/tag").Use(middleware.AdminPrivilege())
|
||||||
{
|
{
|
||||||
cont := &admin.Tag{}
|
cont := &admin.Tag{}
|
||||||
aR.GET("/list", cont.List)
|
aR.GET("/list", cont.List)
|
||||||
@@ -105,16 +120,14 @@ func AddressBookBind(rg *gin.RouterGroup) {
|
|||||||
aR := rg.Group("/address_book")
|
aR := rg.Group("/address_book")
|
||||||
{
|
{
|
||||||
cont := &admin.AddressBook{}
|
cont := &admin.AddressBook{}
|
||||||
aR.GET("/list", cont.List)
|
|
||||||
aR.GET("/detail/:id", cont.Detail)
|
|
||||||
aR.POST("/create", cont.Create)
|
|
||||||
aR.POST("/update", cont.Update)
|
|
||||||
aR.POST("/delete", cont.Delete)
|
|
||||||
aR.POST("/shareByWebClient", cont.ShareByWebClient)
|
aR.POST("/shareByWebClient", cont.ShareByWebClient)
|
||||||
|
|
||||||
aR.POST("/batchUpdateTags", cont.BatchUpdateTags)
|
|
||||||
|
|
||||||
arp := aR.Use(middleware.AdminPrivilege())
|
arp := aR.Use(middleware.AdminPrivilege())
|
||||||
|
arp.GET("/list", cont.List)
|
||||||
|
//arp.GET("/detail/:id", cont.Detail)
|
||||||
|
arp.POST("/create", cont.Create)
|
||||||
|
arp.POST("/update", cont.Update)
|
||||||
|
arp.POST("/delete", cont.Delete)
|
||||||
arp.POST("/batchCreate", cont.BatchCreate)
|
arp.POST("/batchCreate", cont.BatchCreate)
|
||||||
arp.POST("/batchCreateFromPeers", cont.BatchCreateFromPeers)
|
arp.POST("/batchCreateFromPeers", cont.BatchCreateFromPeers)
|
||||||
|
|
||||||
@@ -158,8 +171,8 @@ func OauthBind(rg *gin.RouterGroup) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
func LoginLogBind(rg *gin.RouterGroup) {
|
func LoginLogBind(rg *gin.RouterGroup) {
|
||||||
aR := rg.Group("/login_log")
|
|
||||||
cont := &admin.LoginLog{}
|
cont := &admin.LoginLog{}
|
||||||
|
aR := rg.Group("/login_log").Use(middleware.AdminPrivilege())
|
||||||
aR.GET("/list", cont.List)
|
aR.GET("/list", cont.List)
|
||||||
aR.POST("/delete", cont.Delete)
|
aR.POST("/delete", cont.Delete)
|
||||||
aR.POST("/batchDelete", cont.BatchDelete)
|
aR.POST("/batchDelete", cont.BatchDelete)
|
||||||
@@ -176,7 +189,7 @@ func AuditBind(rg *gin.RouterGroup) {
|
|||||||
afR.POST("/batchDelete", cont.BatchFileDelete)
|
afR.POST("/batchDelete", cont.BatchFileDelete)
|
||||||
}
|
}
|
||||||
func AddressBookCollectionBind(rg *gin.RouterGroup) {
|
func AddressBookCollectionBind(rg *gin.RouterGroup) {
|
||||||
aR := rg.Group("/address_book_collection")
|
aR := rg.Group("/address_book_collection").Use(middleware.AdminPrivilege())
|
||||||
{
|
{
|
||||||
cont := &admin.AddressBookCollection{}
|
cont := &admin.AddressBookCollection{}
|
||||||
aR.GET("/list", cont.List)
|
aR.GET("/list", cont.List)
|
||||||
@@ -188,7 +201,7 @@ func AddressBookCollectionBind(rg *gin.RouterGroup) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
func AddressBookCollectionRuleBind(rg *gin.RouterGroup) {
|
func AddressBookCollectionRuleBind(rg *gin.RouterGroup) {
|
||||||
aR := rg.Group("/address_book_collection_rule")
|
aR := rg.Group("/address_book_collection_rule").Use(middleware.AdminPrivilege())
|
||||||
{
|
{
|
||||||
cont := &admin.AddressBookCollectionRule{}
|
cont := &admin.AddressBookCollectionRule{}
|
||||||
aR.GET("/list", cont.List)
|
aR.GET("/list", cont.List)
|
||||||
@@ -228,13 +241,56 @@ func FileBind(rg *gin.RouterGroup) {
|
|||||||
|
|
||||||
func MyBind(rg *gin.RouterGroup) {
|
func MyBind(rg *gin.RouterGroup) {
|
||||||
{
|
{
|
||||||
msr := &my.ShareRecord{}
|
cont := &my.ShareRecord{}
|
||||||
rg.GET("/my/share_record/list", msr.List)
|
rg.GET("/my/share_record/list", cont.List)
|
||||||
rg.POST("/my/share_record/delete", msr.Delete)
|
rg.POST("/my/share_record/delete", cont.Delete)
|
||||||
rg.POST("/my/share_record/batchDelete", msr.BatchDelete)
|
rg.POST("/my/share_record/batchDelete", cont.BatchDelete)
|
||||||
|
}
|
||||||
|
|
||||||
mab := &my.AddressBook{}
|
{
|
||||||
rg.POST("/my/address_book/batchCreateFromPeers", mab.BatchCreateFromPeers)
|
cont := &my.AddressBook{}
|
||||||
|
rg.GET("/my/address_book/list", cont.List)
|
||||||
|
rg.POST("/my/address_book/create", cont.Create)
|
||||||
|
rg.POST("/my/address_book/update", cont.Update)
|
||||||
|
rg.POST("/my/address_book/delete", cont.Delete)
|
||||||
|
rg.POST("/my/address_book/batchCreateFromPeers", cont.BatchCreateFromPeers)
|
||||||
|
rg.POST("/my/address_book/batchUpdateTags", cont.BatchUpdateTags)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
cont := &my.Tag{}
|
||||||
|
rg.GET("/my/tag/list", cont.List)
|
||||||
|
rg.POST("/my/tag/create", cont.Create)
|
||||||
|
rg.POST("/my/tag/update", cont.Update)
|
||||||
|
rg.POST("/my/tag/delete", cont.Delete)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
cont := &my.AddressBookCollection{}
|
||||||
|
rg.GET("/my/address_book_collection/list", cont.List)
|
||||||
|
rg.POST("/my/address_book_collection/create", cont.Create)
|
||||||
|
rg.POST("/my/address_book_collection/update", cont.Update)
|
||||||
|
rg.POST("/my/address_book_collection/delete", cont.Delete)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
cont := &my.AddressBookCollectionRule{}
|
||||||
|
rg.GET("/my/address_book_collection_rule/list", cont.List)
|
||||||
|
rg.POST("/my/address_book_collection_rule/create", cont.Create)
|
||||||
|
rg.POST("/my/address_book_collection_rule/update", cont.Update)
|
||||||
|
rg.POST("/my/address_book_collection_rule/delete", cont.Delete)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
cont := &my.Peer{}
|
||||||
|
rg.GET("/my/peer/list", cont.List)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
cont := &my.LoginLog{}
|
||||||
|
rg.GET("/my/login_log/list", cont.List)
|
||||||
|
rg.POST("/my/login_log/delete", cont.Delete)
|
||||||
|
rg.POST("/my/login_log/batchDelete", cont.BatchDelete)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,9 @@ func ApiInit(g *gin.Engine) {
|
|||||||
|
|
||||||
//g.Use(middleware.Cors())
|
//g.Use(middleware.Cors())
|
||||||
//swagger
|
//swagger
|
||||||
g.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler, ginSwagger.InstanceName("api")))
|
if global.Config.App.ShowSwagger == 1 {
|
||||||
|
g.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler, ginSwagger.InstanceName("api")))
|
||||||
|
}
|
||||||
|
|
||||||
frg := g.Group("/api")
|
frg := g.Group("/api")
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
package jwt
|
package jwt
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rsa"
|
"fmt"
|
||||||
"github.com/golang-jwt/jwt/v5"
|
"github.com/golang-jwt/jwt/v5"
|
||||||
"os"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Jwt struct {
|
type Jwt struct {
|
||||||
privateKey *rsa.PrivateKey
|
Key []byte
|
||||||
TokenExpireDuration time.Duration
|
TokenExpireDuration time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -17,31 +16,28 @@ type UserClaims struct {
|
|||||||
jwt.RegisteredClaims
|
jwt.RegisteredClaims
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewJwt(privateKeyFile string, tokenExpireDuration time.Duration) *Jwt {
|
func NewJwt(key string, tokenExpireDuration time.Duration) *Jwt {
|
||||||
privateKeyContent, err := os.ReadFile(privateKeyFile)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
privateKey, err := jwt.ParseRSAPrivateKeyFromPEM(privateKeyContent)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return &Jwt{
|
return &Jwt{
|
||||||
privateKey: privateKey,
|
Key: []byte(key),
|
||||||
TokenExpireDuration: tokenExpireDuration,
|
TokenExpireDuration: tokenExpireDuration,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Jwt) GenerateToken(userId uint) string {
|
func (s *Jwt) GenerateToken(userId uint) string {
|
||||||
t := jwt.NewWithClaims(jwt.SigningMethodRS256,
|
if len(s.Key) == 0 {
|
||||||
|
fmt.Println("jwt key is nil")
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
t := jwt.NewWithClaims(jwt.SigningMethodHS256,
|
||||||
UserClaims{
|
UserClaims{
|
||||||
UserId: userId,
|
UserId: userId,
|
||||||
RegisteredClaims: jwt.RegisteredClaims{
|
RegisteredClaims: jwt.RegisteredClaims{
|
||||||
ExpiresAt: jwt.NewNumericDate(time.Now().Add(s.TokenExpireDuration)),
|
ExpiresAt: jwt.NewNumericDate(time.Now().Add(s.TokenExpireDuration)),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
token, err := t.SignedString(s.privateKey)
|
token, err := t.SignedString(s.Key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
fmt.Printf("jwt token generate error: %v", err)
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
return token
|
return token
|
||||||
@@ -49,7 +45,7 @@ func (s *Jwt) GenerateToken(userId uint) string {
|
|||||||
|
|
||||||
func (s *Jwt) ParseToken(tokenString string) (uint, error) {
|
func (s *Jwt) ParseToken(tokenString string) (uint, error) {
|
||||||
token, err := jwt.ParseWithClaims(tokenString, &UserClaims{}, func(token *jwt.Token) (interface{}, error) {
|
token, err := jwt.ParseWithClaims(tokenString, &UserClaims{}, func(token *jwt.Token) (interface{}, error) {
|
||||||
return s.privateKey.Public(), nil
|
return s.Key, nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
|
|||||||
@@ -4,12 +4,13 @@ type LoginLog struct {
|
|||||||
IdModel
|
IdModel
|
||||||
UserId uint `json:"user_id" gorm:"default:0;not null;"`
|
UserId uint `json:"user_id" gorm:"default:0;not null;"`
|
||||||
Client string `json:"client"` //webadmin,webclient,app,
|
Client string `json:"client"` //webadmin,webclient,app,
|
||||||
DeviceId string `json:"device_id"`
|
DeviceId string `json:"device_id"`
|
||||||
Uuid string `json:"uuid"`
|
Uuid string `json:"uuid"`
|
||||||
Ip string `json:"ip"`
|
Ip string `json:"ip"`
|
||||||
Type string `json:"type"` //account,oauth
|
Type string `json:"type"` //account,oauth
|
||||||
Platform string `json:"platform"` //windows,linux,mac,android,ios
|
Platform string `json:"platform"` //windows,linux,mac,android,ios
|
||||||
UserTokenId uint `json:"user_token_id" gorm:"default:0;not null;"`
|
UserTokenId uint `json:"user_token_id" gorm:"default:0;not null;"`
|
||||||
|
IsDeleted uint `json:"is_deleted" gorm:"default:0;not null;"`
|
||||||
TimeModel
|
TimeModel
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,6 +25,11 @@ const (
|
|||||||
LoginLogTypeOauth = "oauth"
|
LoginLogTypeOauth = "oauth"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
IsDeletedNo = 0
|
||||||
|
IsDeletedYes = 1
|
||||||
|
)
|
||||||
|
|
||||||
type LoginLogList struct {
|
type LoginLogList struct {
|
||||||
LoginLogs []*LoginLog `json:"list"`
|
LoginLogs []*LoginLog `json:"list"`
|
||||||
Pagination
|
Pagination
|
||||||
|
|||||||
61
model/serverCmd.go
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
type ServerCmd struct {
|
||||||
|
IdModel
|
||||||
|
Cmd string `json:"cmd" gorm:"default:'';not null;"`
|
||||||
|
Alias string `json:"alias" gorm:"default:'';not null;"`
|
||||||
|
Option string `json:"option" gorm:"default:'';not null;"`
|
||||||
|
Explain string `json:"explain" gorm:"default:'';not null;"`
|
||||||
|
Target string `json:"target" gorm:"default:'';not null;"`
|
||||||
|
TimeModel
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerCmdList struct {
|
||||||
|
ServerCmds []*ServerCmd `json:"list"`
|
||||||
|
Pagination
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
ServerCmdTargetIdServer = "21115"
|
||||||
|
ServerCmdTargetRelayServer = "21117"
|
||||||
|
)
|
||||||
|
|
||||||
|
var SysIdServerCmds = []*ServerCmd{
|
||||||
|
{Cmd: "h", Option: "", Explain: "show help", Target: ServerCmdTargetIdServer},
|
||||||
|
{Cmd: "relay-servers", Alias: "rs", Option: "<separated by ,>", Explain: "set or show relay servers", Target: ServerCmdTargetIdServer},
|
||||||
|
{Cmd: "ip-blocker", Alias: "ib", Option: "[<ip>|<number>] [-]", Explain: "block or unblock ip or show blocked ip", Target: ServerCmdTargetIdServer},
|
||||||
|
{Cmd: "ip-changes", Alias: "ic", Option: "[<id>|<number>] [-]", Explain: "ip-changes(ic) [<id>|<number>] [-]", Target: ServerCmdTargetIdServer},
|
||||||
|
{Cmd: "always-use-relay", Alias: "aur", Option: "[y|n]", Explain: "always use relay", Target: ServerCmdTargetIdServer},
|
||||||
|
{Cmd: "test-geo", Alias: "tg", Option: "<ip1> <ip2>", Explain: "test geo", Target: ServerCmdTargetIdServer},
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
"blacklist-add(ba) <ip>",
|
||||||
|
"blacklist-remove(br) <ip>",
|
||||||
|
"blacklist(b) <ip>",
|
||||||
|
"blocklist-add(Ba) <ip>",
|
||||||
|
"blocklist-remove(Br) <ip>",
|
||||||
|
"blocklist(B) <ip>",
|
||||||
|
"downgrade-threshold(dt) [value]",
|
||||||
|
"downgrade-start-check(t) [value(second)]",
|
||||||
|
"limit-speed(ls) [value(Mb/s)]",
|
||||||
|
"total-bandwidth(tb) [value(Mb/s)]",
|
||||||
|
"single-bandwidth(sb) [value(Mb/s)]",
|
||||||
|
"usage(u)"
|
||||||
|
*/
|
||||||
|
|
||||||
|
var SysRelayServerCmds = []*ServerCmd{
|
||||||
|
{Cmd: "h", Option: "", Explain: "show help", Target: ServerCmdTargetRelayServer},
|
||||||
|
{Cmd: "blacklist-add", Alias: "ba", Option: "<ip>", Explain: "blacklist-add(ba) <ip>", Target: ServerCmdTargetRelayServer},
|
||||||
|
{Cmd: "blacklist-remove", Alias: "br", Option: "<ip>", Explain: "blacklist-remove(br) <ip>", Target: ServerCmdTargetRelayServer},
|
||||||
|
{Cmd: "blacklist", Alias: "b", Option: "<ip>", Explain: "blacklist(b) <ip>", Target: ServerCmdTargetRelayServer},
|
||||||
|
{Cmd: "blocklist-add", Alias: "Ba", Option: "<ip>", Explain: "blocklist-add(Ba) <ip>", Target: ServerCmdTargetRelayServer},
|
||||||
|
{Cmd: "blocklist-remove", Alias: "Br", Option: "<ip>", Explain: "blocklist-remove(Br) <ip>", Target: ServerCmdTargetRelayServer},
|
||||||
|
{Cmd: "blocklist", Alias: "B", Option: "<ip>", Explain: "blocklist(B) <ip>", Target: ServerCmdTargetRelayServer},
|
||||||
|
{Cmd: "downgrade-threshold", Alias: "dt", Option: "[value]", Explain: "downgrade-threshold(dt) [value]", Target: ServerCmdTargetRelayServer},
|
||||||
|
{Cmd: "downgrade-start-check", Alias: "t", Option: "[value(second)]", Explain: "downgrade-start-check(t) [value(second)]", Target: ServerCmdTargetRelayServer},
|
||||||
|
{Cmd: "limit-speed", Alias: "ls", Option: "[value(Mb/s)]", Explain: "limit-speed(ls) [value(Mb/s)]", Target: ServerCmdTargetRelayServer},
|
||||||
|
{Cmd: "total-bandwidth", Alias: "tb", Option: "[value(Mb/s)]", Explain: "total-bandwidth(tb) [value(Mb/s)]", Target: ServerCmdTargetRelayServer},
|
||||||
|
{Cmd: "single-bandwidth", Alias: "sb", Option: "[value(Mb/s)]", Explain: "single-bandwidth(sb) [value(Mb/s)]", Target: ServerCmdTargetRelayServer},
|
||||||
|
{Cmd: "usage", Alias: "u", Option: "", Explain: "usage(u)", Target: ServerCmdTargetRelayServer},
|
||||||
|
}
|
||||||
@@ -27,3 +27,8 @@ type UserList struct {
|
|||||||
Users []*User `json:"list,omitempty"`
|
Users []*User `json:"list,omitempty"`
|
||||||
Pagination
|
Pagination
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var UserRouteNames = []string{
|
||||||
|
"MyTagList", "MyAddressBookList", "MyInfo", "MyAddressBookCollection", "MyPeer", "MyShareRecordList", "MyLoginLog",
|
||||||
|
}
|
||||||
|
var AdminRouteNames = []string{"*"}
|
||||||
|
|||||||
@@ -123,3 +123,13 @@ other = "Share Group"
|
|||||||
description = "Register closed."
|
description = "Register closed."
|
||||||
one = "Register closed."
|
one = "Register closed."
|
||||||
other = "Register closed."
|
other = "Register closed."
|
||||||
|
|
||||||
|
[CaptchaRequired]
|
||||||
|
description = "Captcha required."
|
||||||
|
one = "Captcha required."
|
||||||
|
other = "Captcha required."
|
||||||
|
|
||||||
|
[CaptchaError]
|
||||||
|
description = "Captcha error."
|
||||||
|
one = "Captcha error."
|
||||||
|
other = "Captcha error."
|
||||||
|
|||||||
@@ -132,3 +132,13 @@ other = "Grupo compartido"
|
|||||||
description = "Register closed."
|
description = "Register closed."
|
||||||
one = "Registro cerrado."
|
one = "Registro cerrado."
|
||||||
other = "Registro cerrado."
|
other = "Registro cerrado."
|
||||||
|
|
||||||
|
[CaptchaRequired]
|
||||||
|
description = "Captcha required."
|
||||||
|
one = "Captcha requerido."
|
||||||
|
other = "Captcha requerido."
|
||||||
|
|
||||||
|
[CaptchaError]
|
||||||
|
description = "Captcha error."
|
||||||
|
one = "Error de captcha."
|
||||||
|
other = "Error de captcha."
|
||||||
144
resources/i18n/fr.toml
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
[Test]
|
||||||
|
description = "test"
|
||||||
|
one = "test1 "
|
||||||
|
other = "Test2 {{.P0}}"
|
||||||
|
|
||||||
|
[ParamsError]
|
||||||
|
description = "Params validation failed."
|
||||||
|
one = "La validation des paramètres a échoué."
|
||||||
|
other = "La validation des paramètres a échoué."
|
||||||
|
|
||||||
|
[OperationFailed]
|
||||||
|
description = "OperationFailed."
|
||||||
|
one = "l'opération a échoué."
|
||||||
|
other = "l'opération a échoué."
|
||||||
|
|
||||||
|
[OperationSuccess]
|
||||||
|
description = "OperationSuccess."
|
||||||
|
one = "Opération réussie"
|
||||||
|
other = "Opération réussie"
|
||||||
|
|
||||||
|
[ItemExists]
|
||||||
|
description = "Item already exists."
|
||||||
|
one = "L'élément existe déjà."
|
||||||
|
other = "L'élément existe déjà."
|
||||||
|
|
||||||
|
[ItemNotFound]
|
||||||
|
description = "Item not found."
|
||||||
|
one = "Article introuvable."
|
||||||
|
other = "Article introuvable."
|
||||||
|
|
||||||
|
[NoAccess]
|
||||||
|
description = "No access."
|
||||||
|
one = "Aucun d'access."
|
||||||
|
other = "Aucun d'access."
|
||||||
|
|
||||||
|
[UsernameOrPasswordError]
|
||||||
|
description = "Username or password error."
|
||||||
|
one = "Nom d'utilisateur ou de mot de passe incorrect."
|
||||||
|
other = "Nom d'utilisateur ou de mot de passe incorrect."
|
||||||
|
|
||||||
|
[SystemError]
|
||||||
|
description = "System error."
|
||||||
|
one = "Erreur system."
|
||||||
|
other = "Erreur system."
|
||||||
|
|
||||||
|
[ConfigNotFound]
|
||||||
|
description = "Config not found."
|
||||||
|
one = "Configuration introuvable."
|
||||||
|
other = "Configuration introuvable."
|
||||||
|
|
||||||
|
[OauthExpired]
|
||||||
|
description = "Oauth expired."
|
||||||
|
one = "Oauth a expiré, veuillez réessayer."
|
||||||
|
other = "Oauth a expiré, veuillez réessayer."
|
||||||
|
|
||||||
|
[OauthFailed]
|
||||||
|
description = "Oauth failed."
|
||||||
|
one = "Oauth a échoué."
|
||||||
|
other = "Oauth a échoué."
|
||||||
|
|
||||||
|
[OauthHasBindOtherUser]
|
||||||
|
description = "Oauth has bind other user."
|
||||||
|
one = "Oauth a lié un autre utilisateur."
|
||||||
|
other = "Oauth a lié un autre utilisateur."
|
||||||
|
|
||||||
|
[ParamIsEmpty]
|
||||||
|
description = "Param is empty."
|
||||||
|
one = "{{.P0}} est vide."
|
||||||
|
other = "{{.P0}} est vide."
|
||||||
|
|
||||||
|
[BindFail]
|
||||||
|
description = "Bind fail."
|
||||||
|
one = "Échec de la liaison."
|
||||||
|
other = "Échec de la liaison."
|
||||||
|
|
||||||
|
[BindSuccess]
|
||||||
|
description = "Bind success."
|
||||||
|
one = "Succès de la liaison."
|
||||||
|
other = "Succès de la liaison."
|
||||||
|
|
||||||
|
[OauthHasBeenSuccess]
|
||||||
|
description = "Oauth has been success."
|
||||||
|
one = "Oauth a été réussi avec Succès."
|
||||||
|
other = "Oauth a été réussi avec Succès."
|
||||||
|
|
||||||
|
[OauthSuccess]
|
||||||
|
description = "Oauth success."
|
||||||
|
one = "Oauh réussi avec succès."
|
||||||
|
other = "Oauh réussi avec succès."
|
||||||
|
|
||||||
|
[OauthRegisterSuccess]
|
||||||
|
description = "Oauth register success."
|
||||||
|
one = "Succès de l'enregistrement Oauth."
|
||||||
|
other = "Succès de l'enregistrement Oauth."
|
||||||
|
|
||||||
|
[OauthRegisterFailed]
|
||||||
|
description = "Oauth register failed."
|
||||||
|
one = "L'inscription Oauth a échoué."
|
||||||
|
other = "L'inscription Oauth a échoué."
|
||||||
|
|
||||||
|
[GetOauthTokenError]
|
||||||
|
description = "Get oauth token error."
|
||||||
|
one = "Erreur de l'obtention du jeton oauth."
|
||||||
|
other = "Erreur de l'obtention du jeton oauth."
|
||||||
|
|
||||||
|
[GetOauthUserInfoError]
|
||||||
|
description = "Get oauth user info error."
|
||||||
|
one = "Erreur d'obtention d'informations sur l'utilisateur oauth."
|
||||||
|
other = "Erreur d'obtention d'informations sur l'utilisateur oauth."
|
||||||
|
|
||||||
|
[DecodeOauthUserInfoError]
|
||||||
|
description = "Decode oauth user info error."
|
||||||
|
one = "Erreur de décodage des informations utilisateur oauth."
|
||||||
|
other = "Erreur de décodage des informations utilisateur oauth."
|
||||||
|
|
||||||
|
[OldPasswordError]
|
||||||
|
description = "Old password error."
|
||||||
|
one = "Ancien mot de passe incorrect."
|
||||||
|
other = "Ancien mot de passe incorrect."
|
||||||
|
|
||||||
|
[DefaultGroup]
|
||||||
|
description = "Default group"
|
||||||
|
one = "Groupe Défaut"
|
||||||
|
other = "Groupe Défaut"
|
||||||
|
|
||||||
|
[ShareGroup]
|
||||||
|
description = "Share group"
|
||||||
|
one = "Groupe partagé"
|
||||||
|
other = "Groupe partagé"
|
||||||
|
|
||||||
|
[RegisterClosed]
|
||||||
|
description = "Register closed."
|
||||||
|
one = "Inscription fermée."
|
||||||
|
other = "Inscription fermée."
|
||||||
|
|
||||||
|
[CaptchaRequired]
|
||||||
|
description = "Captcha required."
|
||||||
|
one = "Captcha requis."
|
||||||
|
other = "Captcha requis."
|
||||||
|
|
||||||
|
[CaptchaError]
|
||||||
|
description = "Captcha error."
|
||||||
|
one = "Erreur de captcha."
|
||||||
|
other = "Erreur de captcha."
|
||||||
@@ -125,4 +125,14 @@ other = "공유 그룹"
|
|||||||
[RegisterClosed]
|
[RegisterClosed]
|
||||||
description = "Register closed."
|
description = "Register closed."
|
||||||
one = "가입이 종료되었습니다."
|
one = "가입이 종료되었습니다."
|
||||||
other = "가입이 종료되었습니다."
|
other = "가입이 종료되었습니다."
|
||||||
|
|
||||||
|
[CaptchaRequired]
|
||||||
|
description = "Captcha required."
|
||||||
|
one = "Captcha가 필요합니다."
|
||||||
|
other = "Captcha가 필요합니다."
|
||||||
|
|
||||||
|
[CaptchaError]
|
||||||
|
description = "Captcha error."
|
||||||
|
one = "Captcha 오류."
|
||||||
|
other = "Captcha 오류."
|
||||||
@@ -131,4 +131,14 @@ other = "Общая группа"
|
|||||||
[RegisterClosed]
|
[RegisterClosed]
|
||||||
description = "Register closed."
|
description = "Register closed."
|
||||||
one = "Регистрация закрыта."
|
one = "Регистрация закрыта."
|
||||||
other = "Регистрация закрыта."
|
other = "Регистрация закрыта."
|
||||||
|
|
||||||
|
[CaptchaRequired]
|
||||||
|
description = "Captcha required."
|
||||||
|
one = "Требуется капча."
|
||||||
|
other = "Требуется капча."
|
||||||
|
|
||||||
|
[CaptchaError]
|
||||||
|
description = "Captcha error."
|
||||||
|
one = "Ошибка капчи."
|
||||||
|
other = "Ошибка капчи."
|
||||||
@@ -124,4 +124,14 @@ other = "共享组"
|
|||||||
[RegisterClosed]
|
[RegisterClosed]
|
||||||
description = "Register closed."
|
description = "Register closed."
|
||||||
one = "注册已关闭。"
|
one = "注册已关闭。"
|
||||||
other = "注册已关闭。"
|
other = "注册已关闭。"
|
||||||
|
|
||||||
|
[CaptchaRequired]
|
||||||
|
description = "Captcha required."
|
||||||
|
one = "需要验证码。"
|
||||||
|
other = "需要验证码。"
|
||||||
|
|
||||||
|
[CaptchaError]
|
||||||
|
description = "Captcha error."
|
||||||
|
one = "验证码错误。"
|
||||||
|
other = "验证码错误。"
|
||||||
4
resources/web2/index.html
vendored
@@ -32,7 +32,7 @@
|
|||||||
<title>RustDesk</title>
|
<title>RustDesk</title>
|
||||||
<script src="/webclient-config/index.js"></script>
|
<script src="/webclient-config/index.js"></script>
|
||||||
<link rel="manifest" href="manifest.json" />
|
<link rel="manifest" href="manifest.json" />
|
||||||
<script type="module" crossorigin src="js/dist/index.js?v=893935a2"></script>
|
<script type="module" crossorigin src="js/dist/index.js?v=1bbc8b94"></script>
|
||||||
<link rel="modulepreload" href="js/dist/vendor.js?v=0b990c6e" />
|
<link rel="modulepreload" href="js/dist/vendor.js?v=0b990c6e" />
|
||||||
<style>
|
<style>
|
||||||
html,
|
html,
|
||||||
@@ -259,7 +259,7 @@
|
|||||||
}
|
}
|
||||||
scriptLoaded = true;
|
scriptLoaded = true;
|
||||||
var scriptTag = document.createElement("script");
|
var scriptTag = document.createElement("script");
|
||||||
scriptTag.src = "main.dart.js?v=df360f45";
|
scriptTag.src = "main.dart.js?v=f6f842b3";
|
||||||
scriptTag.type = "application/javascript";
|
scriptTag.type = "application/javascript";
|
||||||
document.body.append(scriptTag);
|
document.body.append(scriptTag);
|
||||||
}
|
}
|
||||||
|
|||||||
2003
resources/web2/js/dist/index.js
vendored
485
resources/web2/js/dist/lang.js
vendored
86468
resources/web2/main.dart.js
vendored
@@ -47,3 +47,12 @@ func (us *LoginLogService) Update(u *model.LoginLog) error {
|
|||||||
func (us *LoginLogService) BatchDelete(ids []uint) error {
|
func (us *LoginLogService) BatchDelete(ids []uint) error {
|
||||||
return global.DB.Where("id in (?)", ids).Delete(&model.LoginLog{}).Error
|
return global.DB.Where("id in (?)", ids).Delete(&model.LoginLog{}).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (us *LoginLogService) SoftDelete(l *model.LoginLog) error {
|
||||||
|
l.IsDeleted = model.IsDeletedYes
|
||||||
|
return us.Update(l)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (us *LoginLogService) BatchSoftDelete(uid uint, ids []uint) error {
|
||||||
|
return global.DB.Model(&model.LoginLog{}).Where("user_id = ? and id in (?)", uid, ids).Update("is_deleted", model.IsDeletedYes).Error
|
||||||
|
}
|
||||||
|
|||||||
99
service/serverCmd.go
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"Gwen/global"
|
||||||
|
"Gwen/model"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ServerCmdService struct{}
|
||||||
|
|
||||||
|
// List
|
||||||
|
func (is *ServerCmdService) List(page, pageSize uint) (res *model.ServerCmdList) {
|
||||||
|
res = &model.ServerCmdList{}
|
||||||
|
res.Page = int64(page)
|
||||||
|
res.PageSize = int64(pageSize)
|
||||||
|
tx := global.DB.Model(&model.ServerCmd{})
|
||||||
|
tx.Count(&res.Total)
|
||||||
|
tx.Scopes(Paginate(page, pageSize))
|
||||||
|
tx.Find(&res.ServerCmds)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Info
|
||||||
|
func (is *ServerCmdService) Info(id uint) *model.ServerCmd {
|
||||||
|
u := &model.ServerCmd{}
|
||||||
|
global.DB.Where("id = ?", id).First(u)
|
||||||
|
return u
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete
|
||||||
|
func (is *ServerCmdService) Delete(u *model.ServerCmd) error {
|
||||||
|
return global.DB.Delete(u).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create
|
||||||
|
func (is *ServerCmdService) Create(u *model.ServerCmd) error {
|
||||||
|
res := global.DB.Create(u).Error
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendCmd 发送命令
|
||||||
|
func (is *ServerCmdService) SendCmd(target string, cmd string, arg string) (string, error) {
|
||||||
|
port := 0
|
||||||
|
switch target {
|
||||||
|
case model.ServerCmdTargetIdServer:
|
||||||
|
port = global.Config.Rustdesk.IdServerPort - 1
|
||||||
|
case model.ServerCmdTargetRelayServer:
|
||||||
|
port = global.Config.Rustdesk.RelayServerPort
|
||||||
|
}
|
||||||
|
//组装命令
|
||||||
|
cmd = cmd + " " + arg
|
||||||
|
res, err := is.SendSocketCmd("v6", port, cmd)
|
||||||
|
if err == nil {
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
//v6连接失败,尝试v4
|
||||||
|
res, err = is.SendSocketCmd("v4", port, cmd)
|
||||||
|
if err == nil {
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendSocketCmd
|
||||||
|
func (is *ServerCmdService) SendSocketCmd(ty string, port int, cmd string) (string, error) {
|
||||||
|
addr := "[::1]"
|
||||||
|
tcp := "tcp6"
|
||||||
|
if ty == "v4" {
|
||||||
|
tcp = "tcp"
|
||||||
|
addr = "127.0.0.1"
|
||||||
|
}
|
||||||
|
conn, err := net.Dial(tcp, fmt.Sprintf("%s:%v", addr, port))
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Debugf("%s connect to id server failed: %v", ty, err)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
//发送命令
|
||||||
|
_, err = conn.Write([]byte(cmd))
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Debugf("%s send cmd failed: %v", ty, err)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
//读取返回
|
||||||
|
buf := make([]byte, 1024)
|
||||||
|
n, err := conn.Read(buf)
|
||||||
|
if err != nil && err.Error() != "EOF" {
|
||||||
|
global.Logger.Debugf("%s read response failed: %v", ty, err)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return string(buf[:n]), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (is *ServerCmdService) Update(f *model.ServerCmd) error {
|
||||||
|
return global.DB.Model(f).Updates(f).Error
|
||||||
|
}
|
||||||
@@ -17,6 +17,7 @@ type Service struct {
|
|||||||
*LoginLogService
|
*LoginLogService
|
||||||
*AuditService
|
*AuditService
|
||||||
*ShareRecordService
|
*ShareRecordService
|
||||||
|
*ServerCmdService
|
||||||
}
|
}
|
||||||
|
|
||||||
func New() *Service {
|
func New() *Service {
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package service
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"Gwen/global"
|
"Gwen/global"
|
||||||
adResp "Gwen/http/response/admin"
|
|
||||||
"Gwen/model"
|
"Gwen/model"
|
||||||
"Gwen/utils"
|
"Gwen/utils"
|
||||||
"errors"
|
"errors"
|
||||||
@@ -69,6 +68,9 @@ func (us *UserService) InfoByAccessToken(token string) (*model.User, *model.User
|
|||||||
|
|
||||||
// GenerateToken 生成token
|
// GenerateToken 生成token
|
||||||
func (us *UserService) GenerateToken(u *model.User) string {
|
func (us *UserService) GenerateToken(u *model.User) string {
|
||||||
|
if len(global.Jwt.Key) > 0 {
|
||||||
|
return global.Jwt.GenerateToken(u.Id)
|
||||||
|
}
|
||||||
return utils.Md5(u.Username + time.Now().String())
|
return utils.Md5(u.Username + time.Now().String())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -272,9 +274,9 @@ func (us *UserService) IsAdmin(u *model.User) bool {
|
|||||||
// RouteNames
|
// RouteNames
|
||||||
func (us *UserService) RouteNames(u *model.User) []string {
|
func (us *UserService) RouteNames(u *model.User) []string {
|
||||||
if us.IsAdmin(u) {
|
if us.IsAdmin(u) {
|
||||||
return adResp.AdminRouteNames
|
return model.AdminRouteNames
|
||||||
}
|
}
|
||||||
return adResp.UserRouteNames
|
return model.UserRouteNames
|
||||||
}
|
}
|
||||||
|
|
||||||
// InfoByOauthId 根据oauth的name和openId取用户信息
|
// InfoByOauthId 根据oauth的name和openId取用户信息
|
||||||
@@ -462,3 +464,7 @@ func (us *UserService) AutoRefreshAccessToken(ut *model.UserToken) {
|
|||||||
func (us *UserService) BatchDeleteUserToken(ids []uint) error {
|
func (us *UserService) BatchDeleteUserToken(ids []uint) error {
|
||||||
return global.DB.Where("id in ?", ids).Delete(&model.UserToken{}).Error
|
return global.DB.Where("id in ?", ids).Delete(&model.UserToken{}).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (us *UserService) VerifyJWT(token string) (uint, error) {
|
||||||
|
return global.Jwt.ParseToken(token)
|
||||||
|
}
|
||||||
|
|||||||
18
systemd/rustdesk-api.service
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Rustdesk api Server
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
LimitNOFILE=1000000
|
||||||
|
ExecStart=/usr/bin/rustdesk-api
|
||||||
|
WorkingDirectory=/var/lib/rustdesk-api/
|
||||||
|
User=
|
||||||
|
Group=
|
||||||
|
Restart=always
|
||||||
|
StandardOutput=append:/var/log/rustdesk-api/rustdesk-api.log
|
||||||
|
StandardError=append:/var/log/rustdesk-api/rustdesk-api.error
|
||||||
|
# Restart service after 10 seconds if node service crashes
|
||||||
|
RestartSec=10
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||