Compare commits
176 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
855beb7fa9 | ||
|
|
f57816b1b0 | ||
|
|
ff08fefc30 | ||
|
|
f792ab9055 | ||
|
|
63af103a4e | ||
|
|
0a36d44cec | ||
|
|
a1f4e1de84 | ||
|
|
05b20d47db | ||
|
|
6b746f13d1 | ||
|
|
e838d5bcd2 | ||
|
|
0dcc21260e | ||
|
|
3c30ad145c | ||
|
|
06b0a8e873 | ||
|
|
b7de2ccadd | ||
|
|
b52c5cfca1 | ||
|
|
fe910c37cf | ||
|
|
337ef330eb | ||
|
|
ffa47177aa | ||
|
|
46a76853c3 | ||
|
|
2cd7dfb2b3 | ||
|
|
fee2808bca | ||
|
|
49e5eb186a | ||
|
|
dee2865466 | ||
|
|
eb340b2615 | ||
|
|
e714549a95 | ||
|
|
a1367bcd3d | ||
|
|
642351dafd | ||
|
|
90b9b5adba | ||
|
|
5bf4bbe45f | ||
|
|
036f928fa3 | ||
|
|
94e7b31fb6 | ||
|
|
be4742382d | ||
|
|
70d2f1a055 | ||
|
|
877fe50049 | ||
|
|
7d505705ee | ||
|
|
38f81a03b5 | ||
|
|
d549d23819 | ||
|
|
934675e0f0 | ||
|
|
2be397aa38 | ||
|
|
a0a422ed45 | ||
|
|
fcce10c695 | ||
|
|
30eb14702f | ||
|
|
3679fcc874 | ||
|
|
d085b4e3c2 | ||
|
|
dc8fcdf214 | ||
|
|
8bab23b65b | ||
|
|
f64022e411 | ||
|
|
2d37302cf9 | ||
|
|
1a1856257d | ||
|
|
24f570b64f | ||
|
|
6322177b71 | ||
|
|
d2390d1cb3 | ||
|
|
6fe6f6b708 | ||
|
|
a656f4fec3 | ||
|
|
399c32da7d | ||
|
|
62167836dc | ||
|
|
caef3897a0 | ||
|
|
5ef6810e3f | ||
|
|
ae2079f583 | ||
|
|
aa65382a0f | ||
|
|
18c61a2bfc | ||
|
|
7cc1a8a58a | ||
|
|
cf9feac702 | ||
|
|
a963cd0209 | ||
|
|
6a5408f9b8 | ||
|
|
9aad62d1e4 | ||
|
|
867eab40f8 | ||
|
|
eb5c7efc4c | ||
|
|
857abc16e7 | ||
|
|
28b9866c42 | ||
|
|
a27deb0a41 | ||
|
|
8e026de20b | ||
|
|
718ecc2372 | ||
|
|
56d46722f4 | ||
|
|
b6463cd715 | ||
|
|
bd3ae0cbfe | ||
|
|
83c3aa894f | ||
|
|
1b88d26fea | ||
|
|
588287fdb4 | ||
|
|
688e544b07 | ||
|
|
3e3f812e83 | ||
|
|
b551c7abe4 | ||
|
|
6d1e7a4c05 | ||
|
|
3341a4bc8e | ||
|
|
1c84980d36 | ||
|
|
833b25881d | ||
|
|
9dbf58903c | ||
|
|
ad007f0d91 | ||
|
|
4b06973a52 | ||
|
|
159a67f15d | ||
|
|
7c03b9953b | ||
|
|
f90987de8d | ||
|
|
70e4ff7820 | ||
|
|
a99356f54b | ||
|
|
c5bc9534cc | ||
|
|
a40733424f | ||
|
|
a937efc60e | ||
|
|
6adb0e8415 | ||
|
|
ff9ffb2f12 | ||
|
|
9be4f472ae | ||
|
|
8581d74b08 | ||
|
|
dafe9bd6b6 | ||
|
|
3ae5772360 | ||
|
|
4628dbccfb | ||
|
|
572b1d4c14 | ||
|
|
bdb70e9859 | ||
|
|
38bda17271 | ||
|
|
455e1d2e5b | ||
|
|
89cd724bab | ||
|
|
b9109b4d0e | ||
|
|
945958f552 | ||
|
|
78eb0d5c06 | ||
|
|
bc6eae711e | ||
|
|
f0a4bf6164 | ||
|
|
fc3b5e3ac3 | ||
|
|
f7235ac847 | ||
|
|
231f4ddb7f | ||
|
|
3cad3994cb | ||
|
|
8c97cc8686 | ||
|
|
7ae976ee5d | ||
|
|
e91b53eb32 | ||
|
|
90311536a7 | ||
|
|
e951b7f2f9 | ||
|
|
e0f94b62cf | ||
|
|
8cb701ec85 | ||
|
|
9afd11e3b8 | ||
|
|
a1d495f2db | ||
|
|
652fa93910 | ||
|
|
ef414855f0 | ||
|
|
76cf35cdf0 | ||
|
|
9d7aa05032 | ||
|
|
62a22c697d | ||
|
|
be497a5aa7 | ||
|
|
fc3e16bc63 | ||
|
|
df35912461 | ||
|
|
0ddc66a854 | ||
|
|
716d557d66 | ||
|
|
f9edcb9d47 | ||
|
|
d4623c5bc9 | ||
|
|
02ba4a3330 | ||
|
|
a72d4eaf78 | ||
|
|
4d3abb2dc0 | ||
|
|
ad6a8d1f7a | ||
|
|
a5c8c2ac97 | ||
|
|
1237004cb1 | ||
|
|
c2bcd17df7 | ||
|
|
bb49cbdd50 | ||
|
|
6ad770a824 | ||
|
|
593eeb3ac3 | ||
|
|
6a7be3ef84 | ||
|
|
3addc12f45 | ||
|
|
1e3403e3c5 | ||
|
|
ae4672174a | ||
|
|
f935fed5f7 | ||
|
|
989990c869 | ||
|
|
8b8a44e46b | ||
|
|
2ed2884118 | ||
|
|
03bd34cadd | ||
|
|
1c3f87fecb | ||
|
|
d45e85d29b | ||
|
|
a4b413dadb | ||
|
|
ebd1feb8d1 | ||
|
|
815ec0f75d | ||
|
|
4c7919e6b7 | ||
|
|
f3218d481c | ||
|
|
5e6e90788b | ||
|
|
579a6400ca | ||
|
|
db4ad69d8f | ||
|
|
b1293189ad | ||
|
|
c8b3d41d9d | ||
|
|
fefb679cfc | ||
|
|
352941a20f | ||
|
|
3006f5bd7c | ||
|
|
d9ed2127da | ||
|
|
f2ee00d49e | ||
|
|
c827a4e335 |
26
.dockerignore
Normal file
@@ -0,0 +1,26 @@
|
||||
# Ignore Docker Compose configuration files
|
||||
docker-compose.yaml
|
||||
|
||||
# Ignore development Dockerfile
|
||||
Dockerfile.dev
|
||||
|
||||
# Ignore the data directory
|
||||
data/
|
||||
|
||||
# Ignore version control system directories
|
||||
.git/
|
||||
|
||||
# Ignore log and temporary files
|
||||
*.log
|
||||
*.tmp
|
||||
*.swp
|
||||
|
||||
# Ignore editor/IDE configuration files
|
||||
.vscode/
|
||||
.idea/
|
||||
|
||||
# Ignore binaries and build cache
|
||||
release/
|
||||
bin/
|
||||
*.exe
|
||||
*.out
|
||||
1
.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
||||
resources/web/**/* linguist-vendored
|
||||
298
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,298 @@
|
||||
name: Build
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
BASE_IMAGE_NAMESPACE:
|
||||
description: 'Base image namespace (Default: Your Github username)'
|
||||
required: false
|
||||
default: ''
|
||||
DOCKERHUB_IMAGE_NAMESPACE:
|
||||
description: 'Docker Hub image namespace (Default: Your Github username)'
|
||||
required: false
|
||||
default: ''
|
||||
GHCR_IMAGE_NAMESPACE:
|
||||
description: 'GitHub Container Registry image namespace (Default: Your Github username)'
|
||||
required: false
|
||||
default: ''
|
||||
SKIP_DOCKER_HUB:
|
||||
description: 'Set to true to skip pushing to Docker Hub (default: false)'
|
||||
required: false
|
||||
default: 'false'
|
||||
SKIP_GHCR:
|
||||
description: 'Set to true to skip pushing to GHCR (default: false)'
|
||||
required: false
|
||||
default: 'false'
|
||||
WEBCLIENT_SOURCE_LOCATION:
|
||||
description: 'Web Client API Repository'
|
||||
required: true
|
||||
default: 'https://github.com/lejianwen/rustdesk-api-web'
|
||||
push:
|
||||
tags:
|
||||
- 'v*.*.*' # 当推送带有版本号的 tag(例如 v1.0.0)时触发工作流
|
||||
- 'test*'
|
||||
|
||||
env:
|
||||
LATEST_TAG: latest
|
||||
WEBCLIENT_SOURCE_LOCATION: ${{ github.event.inputs.WEBCLIENT_SOURCE_LOCATION || 'https://github.com/lejianwen/rustdesk-api-web' }}
|
||||
BASE_IMAGE_NAMESPACE: ${{ github.event.inputs.BASE_IMAGE_NAMESPACE || github.actor }}
|
||||
DOCKERHUB_IMAGE_NAMESPACE: ${{ github.event.inputs.DOCKERHUB_IMAGE_NAMESPACE || github.actor }}
|
||||
GHCR_IMAGE_NAMESPACE: ${{ github.event.inputs.GHCR_IMAGE_NAMESPACE || github.actor }}
|
||||
SKIP_DOCKER_HUB: ${{ github.event.inputs.SKIP_DOCKER_HUB || 'false' }}
|
||||
SKIP_GHCR: ${{ github.event.inputs.SKIP_GHCR || 'false' }}
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
job:
|
||||
- { platform: "amd64", goos: "linux", file_ext: "tar.gz" }
|
||||
- { platform: "arm64", goos: "linux", file_ext: "tar.gz" }
|
||||
- { platform: "armv7l", goos: "linux", file_ext: "tar.gz" }
|
||||
- { platform: "amd64", goos: "windows", file_ext: "zip" }
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Go environment
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: '1.22' # 选择 Go 版本
|
||||
|
||||
- name: Set up npm
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '20'
|
||||
|
||||
|
||||
- name: build rustdesk-api-web
|
||||
run: |
|
||||
git clone ${{ env.WEBCLIENT_SOURCE_LOCATION }}
|
||||
cd rustdesk-api-web
|
||||
npm install
|
||||
npm run build
|
||||
mkdir ../resources/admin/ -p
|
||||
cp -ar dist/* ../resources/admin/
|
||||
|
||||
- name: tidy
|
||||
run: go mod tidy
|
||||
|
||||
- name: swag
|
||||
run: |
|
||||
go install github.com/swaggo/swag/cmd/swag@latest
|
||||
swag init -g cmd/apimain.go --output docs/api --instanceName api --exclude http/controller/admin
|
||||
swag init -g cmd/apimain.go --output docs/admin --instanceName admin --exclude http/controller/api
|
||||
|
||||
- name: Build for ${{ matrix.job.goos }}-${{ matrix.job.platform }}
|
||||
run: |
|
||||
mkdir release -p
|
||||
cp -ar resources release/
|
||||
cp -ar docs release/
|
||||
cp -ar conf release/
|
||||
mkdir -p release/data
|
||||
mkdir -p release/runtime
|
||||
if [ "${{ matrix.job.goos }}" = "windows" ]; then
|
||||
sudo apt-get install gcc-mingw-w64-x86-64 zip -y
|
||||
GOOS=${{ matrix.job.goos }} GOARCH=${{ matrix.job.platform }} CC=x86_64-w64-mingw32-gcc CGO_LDFLAGS="-static" CGO_ENABLED=1 go build -ldflags "-s -w" -o ./release/apimain.exe ./cmd/apimain.go
|
||||
zip -r ${{ matrix.job.goos}}-${{ matrix.job.platform }}.${{matrix.job.file_ext}} ./release
|
||||
else
|
||||
if [ "${{ matrix.job.platform }}" = "arm64" ]; then
|
||||
wget https://musl.cc/aarch64-linux-musl-cross.tgz
|
||||
tar -xf aarch64-linux-musl-cross.tgz
|
||||
export PATH=$PATH:$PWD/aarch64-linux-musl-cross/bin
|
||||
GOOS=${{ matrix.job.goos }} GOARCH=${{ matrix.job.platform }} CC=aarch64-linux-musl-gcc CGO_LDFLAGS="-static" CGO_ENABLED=1 go build -ldflags "-s -w" -o ./release/apimain ./cmd/apimain.go
|
||||
elif [ "${{ matrix.job.platform }}" = "armv7l" ]; then
|
||||
wget https://musl.cc/armv7l-linux-musleabihf-cross.tgz
|
||||
tar -xf armv7l-linux-musleabihf-cross.tgz
|
||||
export PATH=$PATH:$PWD/armv7l-linux-musleabihf-cross/bin
|
||||
GOOS=${{ matrix.job.goos }} GOARCH=arm GOARM=7 CC=armv7l-linux-musleabihf-gcc CGO_LDFLAGS="-static" CGO_ENABLED=1 go build -ldflags "-s -w" -o ./release/apimain ./cmd/apimain.go
|
||||
else
|
||||
sudo apt-get install musl musl-dev musl-tools -y
|
||||
GOOS=${{ matrix.job.goos }} GOARCH=${{ matrix.job.platform }} CC=musl-gcc CGO_LDFLAGS="-static" CGO_ENABLED=1 go build -ldflags "-s -w" -o ./release/apimain ./cmd/apimain.go
|
||||
fi
|
||||
tar -czf ${{ matrix.job.goos}}-${{ matrix.job.platform }}.${{matrix.job.file_ext}} ./release
|
||||
fi
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: rustdesk-api-${{ matrix.job.goos }}-${{ matrix.job.platform }}
|
||||
path: |
|
||||
${{ matrix.job.goos}}-${{ matrix.job.platform }}.${{matrix.job.file_ext}}
|
||||
- name: Upload to GitHub Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
files: |
|
||||
${{ matrix.job.goos}}-${{ matrix.job.platform }}.${{matrix.job.file_ext}}
|
||||
# tag_name: ${{ env.LATEST_TAG }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
docker:
|
||||
name: Push Docker Image
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
job:
|
||||
- { platform: "amd64", goos: "linux", docker_platform: "linux/amd64" }
|
||||
- { platform: "arm64", goos: "linux", docker_platform: "linux/arm64" }
|
||||
- { platform: "armv7l", goos: "linux", docker_platform: "linux/arm/v7" }
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Log in to Docker Hub
|
||||
if: ${{ env.SKIP_DOCKER_HUB == 'false' }} # Only log in if SKIP_DOCKER_HUB is false
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
|
||||
|
||||
- name: Log in to GitHub Container Registry
|
||||
if: ${{ env.SKIP_GHCR == 'false' }} # Only log in if GHCR push is enabled
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Extract version from tag
|
||||
id: vars
|
||||
run: |
|
||||
if [[ "${GITHUB_REF}" == refs/tags/* ]]; then
|
||||
echo "TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
|
||||
else
|
||||
echo "TAG=latest" >> $GITHUB_ENV # Default to 'latest' if not a tag
|
||||
fi
|
||||
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@v4
|
||||
with:
|
||||
images: ${{ env.BASE_IMAGE_NAMESPACE }}/rustdesk-api
|
||||
|
||||
- name: Download binaries
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rustdesk-api-${{ matrix.job.goos }}-${{ matrix.job.platform }}
|
||||
path: ./
|
||||
|
||||
- name: Unzip binaries
|
||||
run: |
|
||||
mkdir -p ${{ matrix.job.platform }}
|
||||
tar -xzf ${{ matrix.job.goos }}-${{ matrix.job.platform }}.tar.gz -C ${{ matrix.job.platform }}
|
||||
file ${{ matrix.job.platform }}/apimain
|
||||
|
||||
- name: Build and push Docker image to Docker Hub ${{ matrix.job.platform }}
|
||||
if: ${{ env.SKIP_DOCKER_HUB == 'false' }} # Only run this step if SKIP_DOCKER_HUB is false
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: "."
|
||||
file: ./Dockerfile
|
||||
platforms: ${{ matrix.job.docker_platform }}
|
||||
push: true
|
||||
provenance: false
|
||||
build-args: |
|
||||
BUILDARCH=${{ matrix.job.platform }}
|
||||
tags: |
|
||||
${{ env.DOCKERHUB_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.LATEST_TAG }}-${{ matrix.job.platform }},
|
||||
${{ env.DOCKERHUB_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}-${{ matrix.job.platform }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
|
||||
- name: Build and push Docker image to GHCR ${{ matrix.job.platform }}
|
||||
if: ${{ env.SKIP_GHCR == 'false' }} # Only run this step if SKIP_GHCR is false
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: "."
|
||||
file: ./Dockerfile
|
||||
platforms: ${{ matrix.job.docker_platform }}
|
||||
push: true
|
||||
provenance: false
|
||||
build-args: |
|
||||
BUILDARCH=${{ matrix.job.platform }}
|
||||
tags: |
|
||||
ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.LATEST_TAG }}-${{ matrix.job.platform }},
|
||||
ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}-${{ matrix.job.platform }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
|
||||
#
|
||||
docker-manifest:
|
||||
name: Push Docker Manifest
|
||||
needs: docker
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Extract version from tag
|
||||
id: vars
|
||||
run: |
|
||||
if [[ "${GITHUB_REF}" == refs/tags/* ]]; then
|
||||
echo "TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
|
||||
else
|
||||
echo "TAG=latest" >> $GITHUB_ENV # Default to 'latest' if not a tag
|
||||
fi
|
||||
|
||||
- name: Log in to Docker Hub
|
||||
if: ${{ env.SKIP_DOCKER_HUB == 'false' }} # Only log in if Docker Hub push is enabled
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
|
||||
|
||||
- name: Log in to GitHub Container Registry
|
||||
if: ${{ env.SKIP_GHCR == 'false' }} # Only log in if GHCR push is enabled
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Create and push manifest Docker Hub (:version)
|
||||
if: ${{ env.SKIP_DOCKER_HUB == 'false' }}
|
||||
uses: Noelware/docker-manifest-action@master
|
||||
with:
|
||||
base-image: ${{ env.BASE_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}
|
||||
extra-images: ${{ env.DOCKERHUB_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}-amd64,
|
||||
${{ env.DOCKERHUB_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}-armv7l,
|
||||
${{ env.DOCKERHUB_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}-arm64
|
||||
push: true
|
||||
|
||||
- name: Create and push manifest GHCR (:version)
|
||||
if: ${{ env.SKIP_GHCR == 'false' }}
|
||||
uses: Noelware/docker-manifest-action@master
|
||||
with:
|
||||
base-image: ghcr.io/${{ env.BASE_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}
|
||||
extra-images: ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}-amd64,
|
||||
ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}-armv7l,
|
||||
ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}-arm64
|
||||
push: true
|
||||
amend: true
|
||||
|
||||
- name: Create and push manifest Docker Hub (:latest)
|
||||
if: ${{ env.SKIP_DOCKER_HUB == 'false' }}
|
||||
uses: Noelware/docker-manifest-action@master
|
||||
with:
|
||||
base-image: ${{ env.BASE_IMAGE_NAMESPACE }}/rustdesk-api:latest
|
||||
extra-images: ${{ env.DOCKERHUB_IMAGE_NAMESPACE }}/rustdesk-api:latest-amd64,
|
||||
${{ env.DOCKERHUB_IMAGE_NAMESPACE }}/rustdesk-api:latest-armv7l,
|
||||
${{ env.DOCKERHUB_IMAGE_NAMESPACE }}/rustdesk-api:latest-arm64
|
||||
push: true
|
||||
|
||||
- name: Create and push manifest GHCR (:latest)
|
||||
if: ${{ env.SKIP_GHCR == 'false' }}
|
||||
uses: Noelware/docker-manifest-action@master
|
||||
with:
|
||||
base-image: ghcr.io/${{ env.BASE_IMAGE_NAMESPACE }}/rustdesk-api:latest
|
||||
extra-images: ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:latest-amd64,
|
||||
ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:latest-armv7l,
|
||||
ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:latest-arm64
|
||||
push: true
|
||||
amend: true
|
||||
95
.github/workflows/release.yml
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
name: Build and Release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
# tags:
|
||||
# - 'v*.*.*' # 当推送带有版本号的 tag(例如 v1.0.0)时触发工作流
|
||||
#on:
|
||||
# push:
|
||||
# branches: [ "master" ]
|
||||
# pull_request:
|
||||
# branches: [ "master" ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
goos: [ linux, windows ] # 指定要构建的操作系统
|
||||
goarch: [ amd64 ] # 指定架构
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Go environment
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: '1.22' # 选择 Go 版本
|
||||
|
||||
- name: Set up npm
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '20'
|
||||
|
||||
- name: install gcc zip musl
|
||||
run: |
|
||||
if [ "${{ matrix.goos }}" = "windows" ]; then
|
||||
sudo apt-get install gcc-mingw-w64-x86-64 zip -y
|
||||
else
|
||||
sudo apt-get install musl musl-dev musl-tools -y
|
||||
fi
|
||||
|
||||
|
||||
- name: build rustdesk-api-web
|
||||
run: |
|
||||
git clone https://github.com/lejianwen/rustdesk-api-web
|
||||
cd rustdesk-api-web
|
||||
npm install
|
||||
npm run build
|
||||
mkdir ../resources/admin/ -p
|
||||
cp -ar dist/* ../resources/admin/
|
||||
|
||||
- name: tidy
|
||||
run: go mod tidy
|
||||
|
||||
|
||||
- name: swag
|
||||
run: |
|
||||
go install github.com/swaggo/swag/cmd/swag@latest
|
||||
swag init -g cmd/apimain.go --output docs/api --instanceName api --exclude http/controller/admin
|
||||
swag init -g cmd/apimain.go --output docs/admin --instanceName admin --exclude http/controller/api
|
||||
|
||||
- name: Build for ${{ matrix.goos }}-${{ matrix.goarch }}
|
||||
run: |
|
||||
mkdir release -p
|
||||
cp -ar resources release/
|
||||
cp -ar docs release/
|
||||
cp -ar conf release/
|
||||
mkdir -p release/data
|
||||
mkdir -p release/runtime
|
||||
if [ "${{ matrix.goos }}" = "windows" ]; then
|
||||
GOOS=${{ matrix.goos }} GOARCH=${{ matrix.goarch }} CC=x86_64-w64-mingw32-gcc CGO_LDFLAGS="-static" CGO_ENABLED=1 go build -ldflags "-s -w" -o ./release/apimain.exe ./cmd/apimain.go
|
||||
zip -r ${{ matrix.goos}}-${{ matrix.goarch }}.zip ./release
|
||||
else
|
||||
GOOS=${{ matrix.goos }} GOARCH=${{ matrix.goarch }} CC=musl-gcc CGO_LDFLAGS="-static" CGO_ENABLED=1 go build -ldflags "-s -w" -o ./release/apimain ./cmd/apimain.go
|
||||
tar -czf ${{ matrix.goos}}-${{ matrix.goarch }}.tar.gz ./release
|
||||
fi
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: myapp-${{ matrix.goos }}-${{ matrix.goarch }}
|
||||
path: |
|
||||
${{ matrix.goos}}-${{ matrix.goarch }}.tar.gz
|
||||
${{ matrix.goos}}-${{ matrix.goarch }}.zip
|
||||
|
||||
- name: Upload to GitHub Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
files: |
|
||||
${{ matrix.goos}}-${{ matrix.goarch }}.tar.gz
|
||||
${{ matrix.goos}}-${{ matrix.goarch }}.zip
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
1
.gitignore
vendored
@@ -5,5 +5,6 @@ go.sum
|
||||
resources/*
|
||||
!resources/public/upload/.gitignore
|
||||
!resources/web
|
||||
!resources/i18n
|
||||
release
|
||||
data
|
||||
11
Dockerfile
Normal file
@@ -0,0 +1,11 @@
|
||||
FROM alpine
|
||||
|
||||
ARG BUILDARCH
|
||||
WORKDIR /app
|
||||
RUN apk add --no-cache tzdata file
|
||||
COPY ./${BUILDARCH}/release /app/
|
||||
RUN file /app/apimain
|
||||
VOLUME /app/data
|
||||
|
||||
EXPOSE 21114
|
||||
CMD ["./apimain"]
|
||||
72
Dockerfile.dev
Normal file
@@ -0,0 +1,72 @@
|
||||
# Use build arguments for Go version and architecture
|
||||
ARG GO_VERSION=1.22
|
||||
ARG BUILDARCH=amd64
|
||||
|
||||
# Stage 1: Builder Stage
|
||||
# FROM golang:${GO_VERSION}-alpine AS builder
|
||||
FROM crazymax/xgo:${GO_VERSION} AS builder-backend
|
||||
|
||||
# Set up working directory
|
||||
WORKDIR /app
|
||||
|
||||
# Step 1: Copy the source code
|
||||
COPY . .
|
||||
|
||||
# Step 2: Download dependencies
|
||||
RUN go mod tidy && go mod download
|
||||
|
||||
|
||||
# Step 3: Install swag and Run the build script
|
||||
RUN go install github.com/swaggo/swag/cmd/swag@latest && \
|
||||
swag init -g cmd/apimain.go --output docs/api --instanceName api --exclude http/controller/admin && \
|
||||
swag init -g cmd/apimain.go --output docs/admin --instanceName admin --exclude http/controller/api
|
||||
|
||||
# Build the Go application with CGO enabled and specified ldflags
|
||||
RUN CGO_ENABLED=1 GOOS=linux go build -a \
|
||||
-ldflags "-s -w --extldflags '-static -fpic'" \
|
||||
-installsuffix cgo -o release/apimain cmd/apimain.go
|
||||
|
||||
# Stage 2: Frontend Build Stage (builder2)
|
||||
FROM node:18-alpine AS builder-admin-frontend
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /frontend
|
||||
|
||||
RUN apk update && apk add git --no-cache
|
||||
|
||||
# Clone the frontend repository
|
||||
RUN git clone https://github.com/lejianwen/rustdesk-api-web .
|
||||
|
||||
# Install npm dependencies and build the frontend
|
||||
RUN npm install && npm run build
|
||||
|
||||
# Stage 2: Final Image
|
||||
FROM alpine:latest
|
||||
|
||||
# Set up working directory
|
||||
WORKDIR /app
|
||||
|
||||
# Install necessary runtime dependencies
|
||||
RUN apk add --no-cache tzdata file
|
||||
|
||||
# Copy the built application and resources from the builder stage
|
||||
COPY --from=builder-backend /app/release /app/
|
||||
COPY --from=builder-backend /app/conf /app/conf/
|
||||
COPY --from=builder-backend /app/resources /app/resources/
|
||||
COPY --from=builder-backend /app/docs /app/docs/
|
||||
# Copy frontend build from builder2 stage
|
||||
COPY --from=builder-admin-frontend /frontend/dist/ /app/resources/admin/
|
||||
|
||||
# Ensure the binary is correctly built and linked
|
||||
RUN file /app/apimain && \
|
||||
mkdir -p /app/data && \
|
||||
mkdir -p /app/runtime
|
||||
|
||||
# Set up a volume for persistent data
|
||||
VOLUME /app/data
|
||||
|
||||
# Expose the necessary port
|
||||
EXPOSE 21114
|
||||
|
||||
# Define the command to run the application
|
||||
CMD ["./apimain"]
|
||||
414
README.md
@@ -1,67 +1,169 @@
|
||||
# RustDesk API
|
||||
|
||||
本项目使用 Go 实现了 RustDesk 的 API,并包含了 Web UI 和 Web 客户端。RustDesk 是一个远程桌面软件,提供了自托管的解决方案。
|
||||
[English Doc](README_EN.md)
|
||||
|
||||
本项目使用 Go 实现了 RustDesk 的 API,并包含了 Web Admin 和 Web 客户端。RustDesk 是一个远程桌面软件,提供了自托管的解决方案。
|
||||
|
||||
<div align=center>
|
||||
<img src="https://img.shields.io/badge/golang-1.22-blue"/>
|
||||
<img src="https://img.shields.io/badge/gin-v1.9.0-lightBlue"/>
|
||||
<img src="https://img.shields.io/badge/gorm-v1.25.7-green"/>
|
||||
<img src="https://img.shields.io/badge/swag-v1.16.3-yellow"/>
|
||||
<img src="https://github.com/lejianwen/rustdesk-api/actions/workflows/build.yml/badge.svg"/>
|
||||
</div>
|
||||
|
||||
# 特性
|
||||
|
||||
- PC端API
|
||||
- 个人版API
|
||||
- 登录
|
||||
- 地址簿
|
||||
- 群组
|
||||
- 授权登录,支持`github`, `google` 和 `OIDC` 登录,支持`web后台`授权登录
|
||||
- i18n
|
||||
- Web Admin
|
||||
- 用户管理
|
||||
- 设备管理
|
||||
- 地址簿管理
|
||||
- 标签管理
|
||||
- 群组管理
|
||||
- Oauth 管理
|
||||
- 登录日志
|
||||
- 链接日志
|
||||
- 文件传输日志
|
||||
- 快速使用web client
|
||||
- i18n
|
||||
- 通过 web client 分享给游客
|
||||
- Web Client
|
||||
- 自动获取API server
|
||||
- 自动获取ID服务器和KEY
|
||||
- 自动获取地址簿
|
||||
- 游客通过临时分享链接直接远程到设备
|
||||
|
||||
## 使用前准备
|
||||
|
||||
### Rustdesk
|
||||
### [Rustdesk](https://github.com/rustdesk/rustdesk)
|
||||
|
||||
1. PC客户端使用的是 ***1.3.0***,经测试 ***1.2.6+*** 都可以
|
||||
2. server端必须指定key,不能用自带的生成的key,否则可能链接不上或者超时
|
||||
#### PC客户端使用的是 ***1.3.0***,经测试 ***1.2.6+*** 都可以
|
||||
|
||||
```bash
|
||||
hbbs -r <relay-server-ip[:port]> -k 123456789
|
||||
hbbr -k 123456789
|
||||
```
|
||||
#### 关于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 服务**: 基本实现了PC端基础的接口。
|
||||
### API 服务
|
||||
基本实现了PC端基础的接口。支持Personal版本接口,可以通过配置文件`rustdesk.personal`或环境变量`RUSTDESK_API_RUSTDESK_PERSONAL`来控制是否启用
|
||||
|
||||
#### 登录
|
||||
|
||||
- 添加了`github`, `google` 以及`OIDC`授权登录,需要在后台配置好就可以用了,具体可看后台OAuth配置
|
||||
- 添加了web后台授权登录,点击后直接登录后台就自动登录客户端了
|
||||
|
||||

|
||||
|
||||
#### 地址簿
|
||||
|
||||

|
||||
|
||||
#### 群组
|
||||
群组分为`共享组`和`普通组`,共享组中所有人都能看到小组成员的设备,普通组只有管理员能看到所有小组成员的设备
|
||||
|
||||

|
||||
|
||||
### **Web UI**: 使用前后端分离,提供用户友好的管理界面,主要用来管理和展示。
|
||||
### Web Admin:
|
||||
|
||||
***前端代码在[rustdesk-api-web](https://github.com/lejianwen/rustdesk-api-web)***
|
||||
* 使用前后端分离,提供用户友好的管理界面,主要用来管理和展示。前端代码在[rustdesk-api-web](https://github.com/lejianwen/rustdesk-api-web)
|
||||
|
||||
***初次安装管理员为用户名密码为admin admin,请即时更改密码***
|
||||
* 后台访问地址是`http://<your server>[:port]/_admin/`初次安装管理员为用户名密码为`admin` `admin`,请即时更改密码
|
||||
|
||||
1. 管理员界面
|
||||

|
||||
2. 普通用户界面
|
||||

|
||||
3. 更改密码在右上角
|
||||

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

|
||||
|
||||

|
||||
|
||||
4. 分组可以自定义,方便管理,暂时支持两种类型: `共享组` 和 `普通组`
|
||||
3. 分组可以自定义,方便管理,暂时支持两种类型: `共享组` 和 `普通组`
|
||||

|
||||
4. 可以直接打开webclient,方便使用;也可以分享给游客,游客可以直接通过webclient远程到设备
|
||||
|
||||
### **Web 客户端**:
|
||||

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

|
||||
- 对于`Google` 和 `Github`, `Issuer` 和 `Scopes`不需要填写.
|
||||
- 对于`OIDC`, `Issuer`是必须的。`Scopes`是可选的,默认为 `openid,profile,email`. 确保可以获取 `sub`,`email` 和`preferred_username`
|
||||
- `github oauth app`在`Settings`->`Developer settings`->`OAuth Apps`->`New OAuth App`
|
||||
中创建,地址 [https://github.com/settings/developers](https://github.com/settings/developers)
|
||||
- `Authorization callback URL`填写`http://<your server[:port]>/api/oauth/callback`
|
||||
,比如`http://127.0.0.1:21114/api/oauth/callback`
|
||||
|
||||
### Web Client:
|
||||
|
||||
1. 如果已经登录了后台,web client将自动直接登录
|
||||
2. 如果没登录后台,点击右上角登录即可,api server已经自动配置好了
|
||||
3. 登录后台后,会将地址簿自动保存到web client中,方便使用
|
||||

|
||||
3. 登录后,会自动同步ID服务器和KEY
|
||||
4. 登录后,会将地址簿自动保存到web client中,方便使用
|
||||
|
||||
### **自动化文档**: 使用 Swag 生成 API 文档,方便开发者理解和使用 API。
|
||||
### 自动化文档: 使用 Swag 生成 API 文档,方便开发者理解和使用 API。
|
||||
|
||||
1. 后台文档 <youer server>/admin/swagger/index.html
|
||||
2. PC端文档 <youer server>/swagger/index.html
|
||||
1. 后台文档 `<youer server[:port]>/admin/swagger/index.html`
|
||||
2. PC端文档 `<youer server[:port]>/swagger/index.html`
|
||||

|
||||
|
||||
## 安装与运行
|
||||
|
||||
### 相关配置
|
||||
|
||||
* 参考`conf/config.yaml`配置文件,修改相关配置。如果`gorm.type`是`sqlite`,则不需要配置mysql相关配置。
|
||||
* 参考`conf/config.yaml`配置文件,修改相关配置。
|
||||
* 如果`gorm.type`是`sqlite`,则不需要配置mysql相关配置。
|
||||
* 语言如果不设置默认为`zh-CN`
|
||||
|
||||
```yaml
|
||||
lang: "en"
|
||||
app:
|
||||
web-client: 1 # 1:启用 0:禁用
|
||||
gin:
|
||||
api-addr: "0.0.0.0:21114"
|
||||
mode: "release"
|
||||
resources-path: 'resources'
|
||||
trust-proxy: ""
|
||||
gorm:
|
||||
type: "sqlite"
|
||||
max-idle-conns: 10
|
||||
@@ -76,14 +178,251 @@ rustdesk:
|
||||
relay-server: "192.168.1.66:21117"
|
||||
api-server: "http://192.168.1.66:21114"
|
||||
key: "123456789"
|
||||
personal: 1
|
||||
logger:
|
||||
path: "./runtime/log.txt"
|
||||
level: "warn" #trace,debug,info,warn,error,fatal
|
||||
report-caller: true
|
||||
proxy:
|
||||
enable: false
|
||||
host: ""
|
||||
```
|
||||
|
||||
### 安装步骤
|
||||
### 环境变量
|
||||
变量名前缀是`RUSTDESK_API`,环境变量如果存在将覆盖配置文件中的配置
|
||||
|
||||
| 变量名 | 说明 | 示例 |
|
||||
|------------------------------------|--------------------------------------|-----------------------------|
|
||||
| TZ | 时区 | Asia/Shanghai |
|
||||
| RUSTDESK_API_LANG | 语言 | `en`,`zh-CN` |
|
||||
| RUSTDESK_API_APP_WEB_CLIENT | 是否启用web-client; 1:启用,0:不启用; 默认启用 | 1 |
|
||||
| -----GIN配置----- | ---------- | ---------- |
|
||||
| RUSTDESK_API_GIN_TRUST_PROXY | 信任的代理IP列表,以`,`分割,默认信任所有 | 192.168.1.2,192.168.1.3 |
|
||||
| -----------GORM配置---------------- | ------------------------------------ | --------------------------- |
|
||||
| RUSTDESK_API_GORM_TYPE | 数据库类型sqlite或者mysql,默认sqlite | sqlite |
|
||||
| RUSTDESK_API_GORM_MAX_IDLE_CONNS | 数据库最大空闲连接数 | 10 |
|
||||
| RUSTDESK_API_GORM_MAX_OPEN_CONNS | 数据库最大打开连接数 | 100 |
|
||||
| RUSTDESK_API_RUSTDESK_PERSONAL | 是否启用个人版API, 1:启用,0:不启用; 默认启用 | 1 |
|
||||
| -----MYSQL配置----- | ---------- | ---------- |
|
||||
| RUSTDESK_API_MYSQL_USERNAME | mysql用户名 | root |
|
||||
| RUSTDESK_API_MYSQL_PASSWORD | mysql密码 | 111111 |
|
||||
| RUSTDESK_API_MYSQL_ADDR | mysql地址 | 192.168.1.66:3306 |
|
||||
| RUSTDESK_API_MYSQL_DBNAME | mysql数据库名 | rustdesk |
|
||||
| -----RUSTDESK配置----- | --------------- | ---------- |
|
||||
| RUSTDESK_API_RUSTDESK_ID_SERVER | Rustdesk的id服务器地址 | 192.168.1.66:21116 |
|
||||
| RUSTDESK_API_RUSTDESK_RELAY_SERVER | Rustdesk的relay服务器地址 | 192.168.1.66:21117 |
|
||||
| RUSTDESK_API_RUSTDESK_API_SERVER | Rustdesk的api服务器地址 | http://192.168.1.66:21114 |
|
||||
| RUSTDESK_API_RUSTDESK_KEY | Rustdesk的key | 123456789 |
|
||||
| ----PROXY配置----- | --------------- | ---------- |
|
||||
| RUSTDESK_API_PROXY_ENABLE | 是否启用代理:`false`, `true` | `false` |
|
||||
| RUSTDESK_API_PROXY_HOST | 代理地址 | `http://127.0.0.1:1080` |
|
||||
|
||||
|
||||
### 运行
|
||||
|
||||
#### docker运行
|
||||
|
||||
1. 直接docker运行,配置可以通过挂载配置文件`/app/conf/config.yaml`来修改,或者通过环境变量覆盖配置文件中的配置
|
||||
|
||||
```bash
|
||||
docker run -d --name rustdesk-api -p 21114:21114 \
|
||||
-v /data/rustdesk/api:/app/data \
|
||||
-e TZ=Asia/Shanghai \
|
||||
-e RUSTDESK_API_LANG=zh-CN \
|
||||
-e RUSTDESK_API_RUSTDESK_ID_SERVER=192.168.1.66:21116 \
|
||||
-e RUSTDESK_API_RUSTDESK_RELAY_SERVER=192.168.1.66:21117 \
|
||||
-e RUSTDESK_API_RUSTDESK_API_SERVER=http://192.168.1.66:21114 \
|
||||
-e RUSTDESK_API_RUSTDESK_KEY=<key> \
|
||||
lejianwen/rustdesk-api
|
||||
```
|
||||
|
||||
2. 使用`docker compose`
|
||||
- 简单示例
|
||||
```yaml
|
||||
services:
|
||||
rustdesk-api:
|
||||
container_name: rustdesk-api
|
||||
environment:
|
||||
- TZ=Asia/Shanghai
|
||||
- RUSTDESK_API_RUSTDESK_ID_SERVER=192.168.1.66:21116
|
||||
- RUSTDESK_API_RUSTDESK_RELAY_SERVER=192.168.1.66:21117
|
||||
- RUSTDESK_API_RUSTDESK_API_SERVER=http://192.168.1.66:21114
|
||||
- RUSTDESK_API_RUSTDESK_KEY=<key>
|
||||
ports:
|
||||
- 21114:21114
|
||||
image: lejianwen/rustdesk-api
|
||||
volumes:
|
||||
- /data/rustdesk/api:/app/data #将数据库挂载出来方便备份
|
||||
networks:
|
||||
- rustdesk-net
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
- 根据rustdesk官方提供的示例,加上自己的rustdesk-api
|
||||
- 如果是使用的系统生成的KEY,去掉`-k <key>`参数,在启动后运行`docker-compose logs hbbs`或者`cat ./data/id_ed25519.pub`查看KEY,然后再修改`RUSTDESK_API_RUSTDESK_KEY=<key>`再执行`docker-compose up -d`
|
||||
```yaml
|
||||
networks:
|
||||
rustdesk-net:
|
||||
external: false
|
||||
services:
|
||||
hbbs:
|
||||
container_name: hbbs
|
||||
ports:
|
||||
- 21115:21115
|
||||
- 21116:21116 # 自定义 hbbs 映射端口
|
||||
- 21116:21116/udp # 自定义 hbbs 映射端口
|
||||
- 21118:21118 # web client
|
||||
image: rustdesk/rustdesk-server
|
||||
command: hbbs -r <relay-server-ip[:port]> -k <key> # 填入个人域名或 IP + hbbr 暴露端口
|
||||
volumes:
|
||||
- ./data:/root # 自定义挂载目录
|
||||
networks:
|
||||
- rustdesk-net
|
||||
depends_on:
|
||||
- hbbr
|
||||
restart: unless-stopped
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 64M
|
||||
hbbr:
|
||||
container_name: hbbr
|
||||
ports:
|
||||
- 21117:21117 # 自定义 hbbr 映射端口
|
||||
- 21119:21119 # web client
|
||||
image: rustdesk/rustdesk-server
|
||||
command: hbbr -k <key>
|
||||
volumes:
|
||||
- ./data:/root
|
||||
networks:
|
||||
- rustdesk-net
|
||||
restart: unless-stopped
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 64M
|
||||
rustdesk-api:
|
||||
container_name: rustdesk-api
|
||||
environment:
|
||||
- TZ=Asia/Shanghai
|
||||
- RUSTDESK_API_RUSTDESK_ID_SERVER=192.168.1.66:21116
|
||||
- RUSTDESK_API_RUSTDESK_RELAY_SERVER=192.168.1.66:21117
|
||||
- RUSTDESK_API_RUSTDESK_API_SERVER=http://192.168.1.66:21114
|
||||
- RUSTDESK_API_RUSTDESK_KEY=<key>
|
||||
ports:
|
||||
- 21114:21114
|
||||
image: lejianwen/rustdesk-api
|
||||
volumes:
|
||||
- /data/rustdesk/api:/app/data #将数据库挂载出来方便备份
|
||||
networks:
|
||||
- rustdesk-net
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
- S6的镜像
|
||||
- 如果使用***自定义KEY***,会需要修改启动脚本,覆盖镜像中的`/etc/s6-overlay/s6-rc.d/hbbr/run`和`/etc/s6-overlay/s6-rc.d/hbbr/run`
|
||||
1. 创建`hbbr/run`,自定义KEY才需要
|
||||
```bash
|
||||
#!/command/with-contenv sh
|
||||
cd /data
|
||||
PARAMS=
|
||||
[ "${ENCRYPTED_ONLY}" = "1" ] && PARAMS="-k ${KEY}"
|
||||
/usr/bin/hbbr $PARAMS
|
||||
```
|
||||
2. 创建`hbbs/run`,自定义KEY才需要
|
||||
```bash
|
||||
#!/command/with-contenv sh
|
||||
sleep 2
|
||||
cd /data
|
||||
PARAMS=
|
||||
[ "${ENCRYPTED_ONLY}" = "1" ] && PARAMS="-k ${KEY}"
|
||||
/usr/bin/hbbs -r $RELAY $PARAMS
|
||||
```
|
||||
3. 修改`docker-compose.yml`中的`s6`部分
|
||||
```yaml
|
||||
networks:
|
||||
rustdesk-net:
|
||||
external: false
|
||||
services:
|
||||
rustdesk-server:
|
||||
container_name: rustdesk-server
|
||||
ports:
|
||||
- 21115:21115
|
||||
- 21116:21116
|
||||
- 21116:21116/udp
|
||||
- 21117:21117
|
||||
- 21118:21118
|
||||
- 21119:21119
|
||||
image: rustdesk/rustdesk-server-s6:latest
|
||||
environment:
|
||||
- RELAY=192.168.1.66:21117
|
||||
- ENCRYPTED_ONLY=1
|
||||
- KEY=<key> #自定义KEY
|
||||
volumes:
|
||||
- ./data:/data
|
||||
- ./hbbr/run:/etc/s6-overlay/s6-rc.d/hbbr/run
|
||||
- ./hbbs/run:/etc/s6-overlay/s6-rc.d/hbbs/run
|
||||
restart: unless-stopped
|
||||
rustdesk-api:
|
||||
container_name: rustdesk-api
|
||||
ports:
|
||||
- 21114:21114
|
||||
image: lejianwen/rustdesk-api
|
||||
environment:
|
||||
- TZ=Asia/Shanghai
|
||||
- RUSTDESK_API_RUSTDESK_ID_SERVER=192.168.1.66:21116
|
||||
- RUSTDESK_API_RUSTDESK_RELAY_SERVER=192.168.1.66:21117
|
||||
- RUSTDESK_API_RUSTDESK_API_SERVER=http://192.168.1.66:21114
|
||||
- RUSTDESK_API_RUSTDESK_KEY=<key>
|
||||
volumes:
|
||||
- /data/rustdesk/api:/app/data #将数据库挂载
|
||||
networks:
|
||||
- rustdesk-net
|
||||
restart: unless-stopped
|
||||
```
|
||||
- 如果使用***系统生成的KEY***或者***自定义KEY_PUB,KEY_PRIV***,不需要修改启动脚本,但要在生成KEY后获取到KEY再`docker-compose up -d`
|
||||
```yaml
|
||||
networks:
|
||||
rustdesk-net:
|
||||
external: false
|
||||
services:
|
||||
rustdesk-server:
|
||||
container_name: rustdesk-server
|
||||
ports:
|
||||
- 21115:21115
|
||||
- 21116:21116
|
||||
- 21116:21116/udp
|
||||
- 21117:21117
|
||||
- 21118:21118
|
||||
- 21119:21119
|
||||
image: rustdesk/rustdesk-server-s6:latest
|
||||
environment:
|
||||
- RELAY=192.168.1.66:21117
|
||||
- ENCRYPTED_ONLY=1
|
||||
volumes:
|
||||
- ./data:/data
|
||||
restart: unless-stopped
|
||||
rustdesk-api:
|
||||
container_name: rustdesk-api
|
||||
ports:
|
||||
- 21114:21114
|
||||
image: lejianwen/rustdesk-api
|
||||
environment:
|
||||
- TZ=Asia/Shanghai
|
||||
- RUSTDESK_API_RUSTDESK_ID_SERVER=192.168.1.66:21116
|
||||
- RUSTDESK_API_RUSTDESK_RELAY_SERVER=192.168.1.66:21117
|
||||
- RUSTDESK_API_RUSTDESK_API_SERVER=http://192.168.1.66:21114
|
||||
- RUSTDESK_API_RUSTDESK_KEY=<key> #系统生成的KEY
|
||||
volumes:
|
||||
- /data/rustdesk/api:/app/data #将数据库挂载
|
||||
networks:
|
||||
- rustdesk-net
|
||||
restart: unless-stopped
|
||||
```
|
||||
#### 下载release直接运行
|
||||
|
||||
[下载地址](https://github.com/lejianwen/rustdesk-api/releases)
|
||||
|
||||
#### 源码安装
|
||||
|
||||
1. 克隆仓库
|
||||
@@ -91,15 +430,19 @@ rustdesk:
|
||||
git clone https://github.com/lejianwen/rustdesk-api.git
|
||||
cd rustdesk-api
|
||||
```
|
||||
|
||||
2. 安装依赖
|
||||
|
||||
```bash
|
||||
go mod tidy
|
||||
#安装swag,如果不需要生成文档,可以不安装
|
||||
go install github.com/swaggo/swag/cmd/swag@latest
|
||||
```
|
||||
|
||||
3. 编译后台前端,前端代码在[rustdesk-api-web](https://github.com/lejianwen/rustdesk-api-web)中
|
||||
```bash
|
||||
cd resources
|
||||
mkdir -p admin
|
||||
git clone https://github.com/lejianwen/rustdesk-api-web
|
||||
cd rustdesk-api-web
|
||||
npm install
|
||||
@@ -114,5 +457,26 @@ rustdesk:
|
||||
go generate generate_api.go
|
||||
```
|
||||
5. 编译,如果想自己编译,先cd到项目根目录,然后windows下直接运行`build.bat`,linux下运行`build.sh`,编译后会在`release`
|
||||
目录下生成对应的可执行文件。
|
||||
目录下生成对应的可执行文件。直接运行编译后的可执行文件即可。
|
||||
|
||||
6. 打开浏览器访问`http://<your server[:port]>/_admin/`,默认用户名密码为`admin`,请及时更改密码。
|
||||
|
||||
#### nginx反代
|
||||
在`nginx`中配置反代
|
||||
```
|
||||
server {
|
||||
listen <your port>;
|
||||
server_name <your server>;
|
||||
location / {
|
||||
proxy_pass http://<api-server[:port]>;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
}
|
||||
```
|
||||
## 其他
|
||||
|
||||
- [修改客户端ID](https://github.com/abdullah-erturk/RustDesk-ID-Changer)
|
||||
- [webclient来源](https://hub.docker.com/r/keyurbhole/flutter_web_desk)
|
||||
493
README_EN.md
Normal file
@@ -0,0 +1,493 @@
|
||||
# RustDesk API
|
||||
|
||||
This project implements the RustDesk API using Go, and includes both a web UI and web client. RustDesk is a remote
|
||||
desktop software that provides self-hosted solutions.
|
||||
|
||||
<div align=center>
|
||||
<img src="https://img.shields.io/badge/golang-1.22-blue"/>
|
||||
<img src="https://img.shields.io/badge/gin-v1.9.0-lightBlue"/>
|
||||
<img src="https://img.shields.io/badge/gorm-v1.25.7-green"/>
|
||||
<img src="https://img.shields.io/badge/swag-v1.16.3-yellow"/>
|
||||
<img src="https://github.com/lejianwen/rustdesk-api/actions/workflows/build.yml/badge.svg"/>
|
||||
</div>
|
||||
|
||||
# Features
|
||||
|
||||
- PC API
|
||||
- Personal API
|
||||
- Login
|
||||
- Address Book
|
||||
- Groups
|
||||
- Authorized login, supports `GitHub`, `Google` and `OIDC` login, supports `web admin` authorized login
|
||||
- i18n
|
||||
- Web Admin
|
||||
- User Management
|
||||
- Device Management
|
||||
- Address Book Management
|
||||
- Tag Management
|
||||
- Group Management
|
||||
- OAuth Management
|
||||
- Login Logs
|
||||
- Connection Logs
|
||||
- File Transfer Logs
|
||||
- Quick access to web client
|
||||
- i18n
|
||||
- Share to guest by web client
|
||||
- Web Client
|
||||
- Automatically obtain API server
|
||||
- Automatically obtain ID server and KEY
|
||||
- Automatically obtain address book
|
||||
- Visitors are remotely to the device via a temporary sharing link
|
||||
|
||||
## 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
|
||||
|
||||
### API Service
|
||||
Basic implementation of the PC client's primary interfaces.Supports the Personal version api, which can be enabled by configuring the `rustdesk.personal` file or the `RUSTDESK_API_RUSTDESK_PERSONAL` environment variable.
|
||||
|
||||
#### Login
|
||||
|
||||
- Added `GitHub`, `Google` and `OIDC` login, which can be used after configuration in the admin panel. See the OAuth
|
||||
configuration section for details.
|
||||
- Added authorization login for the web admin panel.
|
||||
|
||||

|
||||
|
||||
#### Address Book
|
||||
|
||||

|
||||
|
||||
#### Groups
|
||||
Groups are divided into `shared groups` and `regular groups`. In shared groups, everyone can see the peers of all group members, while in regular groups, only administrators can see all members' peers.
|
||||
|
||||

|
||||
|
||||
### Web Admin
|
||||
|
||||
* The frontend and backend are separated to provide a user-friendly management interface, primarily for managing and
|
||||
displaying data.Frontend code is available at [rustdesk-api-web](https://github.com/lejianwen/rustdesk-api-web)
|
||||
|
||||
* Admin panel URL: `http://<your server[:port]>/_admin/`. The default username and password for the initial
|
||||
installation are `admin` `admin`, please change the password immediately.
|
||||
|
||||
1. Admin interface:
|
||||

|
||||
2. Regular user interface:
|
||||

|
||||
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`.
|
||||

|
||||
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. OAuth support: Currently, `GitHub`, `Google` and `OIDC` are supported. You need to create an `OAuth App` and configure it in
|
||||
the admin panel.
|
||||

|
||||
- For `Google` and `Github`, you don't need to fill the `Issuer` and `Scpoes`
|
||||
- For `OIDC`, you must set the `Issuer`. And `Scopes` is optional which default is `openid,email,profile`, please make sure this `Oauth App` can access `sub`, `email` and `preferred_username`
|
||||
- Create a `GitHub OAuth App`
|
||||
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`,
|
||||
e.g., `http://127.0.0.1:21114/api/oauth/callback`.
|
||||
|
||||
### Web Client:
|
||||
|
||||
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
|
||||
pre-configured.
|
||||

|
||||
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.
|
||||
|
||||
### Automated Documentation : API documentation is generated using Swag, making it easier for developers to understand and use the API.
|
||||
|
||||
1. Admin panel docs: `<your server[:port]>/admin/swagger/index.html`
|
||||
2. PC client docs: `<your server[:port]>/swagger/index.html`
|
||||

|
||||
|
||||
## Installation and Setup
|
||||
|
||||
### Configuration
|
||||
|
||||
* Modify the configuration in `conf/config.yaml`.
|
||||
* If `gorm.type` is set to `sqlite`, MySQL-related configurations are not required.
|
||||
* Language support: `en` and `zh-CN` are supported. The default is `zh-CN`.
|
||||
|
||||
```yaml
|
||||
lang: "en"
|
||||
app:
|
||||
web-client: 1 # web client route 1:open 0:close
|
||||
gin:
|
||||
api-addr: "0.0.0.0:21114"
|
||||
mode: "release"
|
||||
resources-path: 'resources'
|
||||
trust-proxy: ""
|
||||
gorm:
|
||||
type: "sqlite"
|
||||
max-idle-conns: 10
|
||||
max-open-conns: 100
|
||||
mysql:
|
||||
username: "root"
|
||||
password: "111111"
|
||||
addr: "192.168.1.66:3308"
|
||||
dbname: "rustdesk"
|
||||
rustdesk:
|
||||
id-server: "192.168.1.66:21116"
|
||||
relay-server: "192.168.1.66:21117"
|
||||
api-server: "http://192.168.1.66:21114"
|
||||
key: "123456789"
|
||||
personal: 1
|
||||
logger:
|
||||
path: "./runtime/log.txt"
|
||||
level: "warn" #trace,debug,info,warn,error,fatal
|
||||
report-caller: true
|
||||
proxy:
|
||||
enable: false
|
||||
host: ""
|
||||
```
|
||||
|
||||
### Environment Variables
|
||||
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 |
|
||||
|------------------------------------|-----------------------------------------------------------|-------------------------------|
|
||||
| TZ | timezone | Asia/Shanghai |
|
||||
| RUSTDESK_API_LANG | Language | `en`,`zh-CN` |
|
||||
| RUSTDESK_API_APP_WEB_CLIENT | web client on/off; 1: on, 0 off, deault 1 | 1 |
|
||||
| ----- GIN Configuration ----- | --------------------------------------- | ----------------------------- |
|
||||
| RUSTDESK_API_GIN_TRUST_PROXY | Trusted proxy IPs, separated by commas. | 192.168.1.2,192.168.1.3 |
|
||||
| ----- GORM Configuration ----- | --------------------------------------- | ----------------------------- |
|
||||
| RUSTDESK_API_GORM_TYPE | Database type (`sqlite` or `mysql`). Default is `sqlite`. | sqlite |
|
||||
| RUSTDESK_API_GORM_MAX_IDLE_CONNS | Maximum idle connections | 10 |
|
||||
| RUSTDESK_API_GORM_MAX_OPEN_CONNS | Maximum open connections | 100 |
|
||||
| RUSTDESK_API_RUSTDESK_PERSONAL | Open Personal Api 1:Enable,0:Disable | 1 |
|
||||
| ----- MYSQL Configuration ----- | --------------------------------------- | ----------------------------- |
|
||||
| RUSTDESK_API_MYSQL_USERNAME | MySQL username | root |
|
||||
| RUSTDESK_API_MYSQL_PASSWORD | MySQL password | 111111 |
|
||||
| RUSTDESK_API_MYSQL_ADDR | MySQL address | 192.168.1.66:3306 |
|
||||
| RUSTDESK_API_MYSQL_DBNAME | MySQL database name | rustdesk |
|
||||
| ----- RUSTDESK Configuration ----- | --------------------------------------- | ----------------------------- |
|
||||
| RUSTDESK_API_RUSTDESK_ID_SERVER | Rustdesk ID server address | 192.168.1.66:21116 |
|
||||
| RUSTDESK_API_RUSTDESK_RELAY_SERVER | Rustdesk relay server address | 192.168.1.66:21117 |
|
||||
| RUSTDESK_API_RUSTDESK_API_SERVER | Rustdesk API server address | http://192.168.1.66:21114 |
|
||||
| RUSTDESK_API_RUSTDESK_KEY | Rustdesk key | 123456789 |
|
||||
| ---- PROXY ----- | --------------- | ---------- |
|
||||
| RUSTDESK_API_PROXY_ENABLE | proxy_enable :`false`, `true` | `false` |
|
||||
| RUSTDESK_API_PROXY_HOST | proxy_host | `http://127.0.0.1:1080` |
|
||||
|
||||
### Installation Steps
|
||||
|
||||
#### Running via Docker
|
||||
|
||||
1. Run directly with Docker. Configuration can be modified by mounting the config file `/app/conf/config.yaml`, or by
|
||||
using environment variables to override settings.
|
||||
|
||||
```bash
|
||||
docker run -d --name rustdesk-api -p 21114:21114 \
|
||||
-v /data/rustdesk/api:/app/data \
|
||||
-e RUSTDESK_API_LANG=en \
|
||||
-e RUSTDESK_API_RUSTDESK_ID_SERVER=192.168.1.66:21116 \
|
||||
-e RUSTDESK_API_RUSTDESK_RELAY_SERVER=192.168.1.66:21117 \
|
||||
-e RUSTDESK_API_RUSTDESK_API_SERVER=http://192.168.1.66:21114 \
|
||||
-e RUSTDESK_API_RUSTDESK_KEY=abc123456 \
|
||||
lejianwen/rustdesk-api
|
||||
```
|
||||
|
||||
2. Using `docker-compose`
|
||||
- Simple example:
|
||||
```yaml
|
||||
services:
|
||||
rustdesk-api:
|
||||
container_name: rustdesk-api
|
||||
environment:
|
||||
- RUSTDESK_API_LANG=en
|
||||
- RUSTDESK_API_RUSTDESK_ID_SERVER=192.168.1.66:21116
|
||||
- RUSTDESK_API_RUSTDESK_RELAY_SERVER=192.168.1.66:21117
|
||||
- RUSTDESK_API_RUSTDESK_API_SERVER=http://192.168.1.66:21114
|
||||
- RUSTDESK_API_RUSTDESK_KEY=<key>
|
||||
ports:
|
||||
- 21114:21114
|
||||
image: lejianwen/rustdesk-api
|
||||
volumes:
|
||||
- /data/rustdesk/api:/app/data # Mount the database for easy backup
|
||||
networks:
|
||||
- rustdesk-net
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
- Example with RustDesk's official Docker Compose file, adding your `rustdesk-api` service:
|
||||
- If you are using a system-generated KEY, remove the `-k <key>` parameter. However, after the first startup, run `docker-compose logs hbbs` or `cat ./data/id_ed25519.pub` to view the KEY, then modify `RUSTDESK_API_RUSTDESK_KEY=<key>` and execute `docker-compose up -d` again.
|
||||
```yaml
|
||||
networks:
|
||||
rustdesk-net:
|
||||
external: false
|
||||
services:
|
||||
hbbs:
|
||||
container_name: hbbs
|
||||
ports:
|
||||
- 21115:21115
|
||||
- 21116:21116 # 自定义 hbbs 映射端口
|
||||
- 21116:21116/udp # 自定义 hbbs 映射端口
|
||||
- 21118:21118 # web client
|
||||
image: rustdesk/rustdesk-server
|
||||
command: hbbs -r <relay-server-ip[:port]> -k <key> # 填入个人域名或 IP + hbbr 暴露端口
|
||||
volumes:
|
||||
- ./data:/root # 自定义挂载目录
|
||||
networks:
|
||||
- rustdesk-net
|
||||
depends_on:
|
||||
- hbbr
|
||||
restart: unless-stopped
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 64M
|
||||
hbbr:
|
||||
container_name: hbbr
|
||||
ports:
|
||||
- 21117:21117 # 自定义 hbbr 映射端口
|
||||
- 21119:21119 # web client
|
||||
image: rustdesk/rustdesk-server
|
||||
command: hbbr -k <key>
|
||||
volumes:
|
||||
- ./data:/root
|
||||
networks:
|
||||
- rustdesk-net
|
||||
restart: unless-stopped
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 64M
|
||||
rustdesk-api:
|
||||
container_name: rustdesk-api
|
||||
environment:
|
||||
- TZ=Asia/Shanghai
|
||||
- RUSTDESK_API_RUSTDESK_ID_SERVER=192.168.1.66:21116
|
||||
- RUSTDESK_API_RUSTDESK_RELAY_SERVER=192.168.1.66:21117
|
||||
- RUSTDESK_API_RUSTDESK_API_SERVER=http://192.168.1.66:21114
|
||||
- RUSTDESK_API_RUSTDESK_KEY=<key>
|
||||
ports:
|
||||
- 21114:21114
|
||||
image: lejianwen/rustdesk-api
|
||||
volumes:
|
||||
- /data/rustdesk/api:/app/data #将数据库挂载出来方便备份
|
||||
networks:
|
||||
- rustdesk-net
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
- S6 image
|
||||
- - If using ***custom KEY***, you will need to modify the startup script to override the `/etc/s6-overlay/s6-rc.d/hbbr/run` and `/etc/s6-overlay/s6-rc.d/hbbr/run` in the image.
|
||||
1. Create `hbbr/run`, only needed for custom KEY
|
||||
```bash
|
||||
#!/command/with-contenv sh
|
||||
cd /data
|
||||
PARAMS=
|
||||
[ "${ENCRYPTED_ONLY}" = "1" ] && PARAMS="-k ${KEY}"
|
||||
/usr/bin/hbbr $PARAMS
|
||||
```
|
||||
2. Create `hbbs/run`, only needed for custom KEY
|
||||
```bash
|
||||
#!/command/with-contenv sh
|
||||
sleep 2
|
||||
cd /data
|
||||
PARAMS=
|
||||
[ "${ENCRYPTED_ONLY}" = "1" ] && PARAMS="-k ${KEY}"
|
||||
/usr/bin/hbbs -r $RELAY $PARAMS
|
||||
```
|
||||
3. Modify the `s6` section in `docker-compose.yml`
|
||||
```yaml
|
||||
networks:
|
||||
rustdesk-net:
|
||||
external: false
|
||||
services:
|
||||
rustdesk-server:
|
||||
container_name: rustdesk-server
|
||||
ports:
|
||||
- 21115:21115
|
||||
- 21116:21116
|
||||
- 21116:21116/udp
|
||||
- 21117:21117
|
||||
- 21118:21118
|
||||
- 21119:21119
|
||||
image: rustdesk/rustdesk-server-s6:latest
|
||||
environment:
|
||||
- RELAY=192.168.1.66:21117
|
||||
- ENCRYPTED_ONLY=1
|
||||
- KEY=<key> #KEY
|
||||
volumes:
|
||||
- ./data:/data
|
||||
- ./hbbr/run:/etc/s6-overlay/s6-rc.d/hbbr/run
|
||||
- ./hbbs/run:/etc/s6-overlay/s6-rc.d/hbbs/run
|
||||
restart: unless-stopped
|
||||
rustdesk-api:
|
||||
container_name: rustdesk-api
|
||||
ports:
|
||||
- 21114:21114
|
||||
image: lejianwen/rustdesk-api
|
||||
environment:
|
||||
- TZ=Asia/Shanghai
|
||||
- RUSTDESK_API_RUSTDESK_ID_SERVER=192.168.1.66:21116
|
||||
- RUSTDESK_API_RUSTDESK_RELAY_SERVER=192.168.1.66:21117
|
||||
- RUSTDESK_API_RUSTDESK_API_SERVER=http://192.168.1.66:21114
|
||||
- RUSTDESK_API_RUSTDESK_KEY=<key>
|
||||
volumes:
|
||||
- /data/rustdesk/api:/app/data
|
||||
networks:
|
||||
- rustdesk-net
|
||||
restart: unless-stopped
|
||||
```
|
||||
- If using ***system-generated KEY*** or ***custom KEY_PUB, KEY_PRIV***, you do not need to modify the startup script, but you need to obtain the KEY after it is generated and then run `docker-compose up -d`
|
||||
```yaml
|
||||
networks:
|
||||
rustdesk-net:
|
||||
external: false
|
||||
services:
|
||||
rustdesk-server:
|
||||
container_name: rustdesk-server
|
||||
ports:
|
||||
- 21115:21115
|
||||
- 21116:21116
|
||||
- 21116:21116/udp
|
||||
- 21117:21117
|
||||
- 21118:21118
|
||||
- 21119:21119
|
||||
image: rustdesk/rustdesk-server-s6:latest
|
||||
environment:
|
||||
- RELAY=192.168.1.66:21117
|
||||
- ENCRYPTED_ONLY=1
|
||||
volumes:
|
||||
- ./data:/data
|
||||
restart: unless-stopped
|
||||
rustdesk-api:
|
||||
container_name: rustdesk-api
|
||||
ports:
|
||||
- 21114:21114
|
||||
image: lejianwen/rustdesk-api
|
||||
environment:
|
||||
- TZ=Asia/Shanghai
|
||||
- RUSTDESK_API_RUSTDESK_ID_SERVER=192.168.1.66:21116
|
||||
- RUSTDESK_API_RUSTDESK_RELAY_SERVER=192.168.1.66:21117
|
||||
- RUSTDESK_API_RUSTDESK_API_SERVER=http://192.168.1.66:21114
|
||||
- RUSTDESK_API_RUSTDESK_KEY=<key>
|
||||
volumes:
|
||||
- /data/rustdesk/api:/app/data
|
||||
networks:
|
||||
- rustdesk-net
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
#### Running from Release
|
||||
|
||||
Download the release from [release](https://github.com/lejianwen/rustdesk-api/releases).
|
||||
|
||||
#### Source Installation
|
||||
|
||||
1. Clone the repository:
|
||||
```bash
|
||||
git clone https://github.com/lejianwen/rustdesk-api.git
|
||||
cd rustdesk-api
|
||||
```
|
||||
|
||||
2. Install dependencies:
|
||||
|
||||
```bash
|
||||
go mod tidy
|
||||
# Install Swag if you need to generate documentation; otherwise, you can skip this step
|
||||
go install github.com/swaggo/swag/cmd/swag@latest
|
||||
```
|
||||
|
||||
3. Build the admin front-end (the front-end code is
|
||||
in [rustdesk-api-web](https://github.com/lejianwen/rustdesk-api-web)):
|
||||
```bash
|
||||
cd resources
|
||||
mkdir -p admin
|
||||
git clone https://github.com/lejianwen/rustdesk-api-web
|
||||
cd rustdesk-api-web
|
||||
npm install
|
||||
npm run build
|
||||
cp -ar dist/* ../admin/
|
||||
```
|
||||
|
||||
4. Run:
|
||||
```bash
|
||||
# Run directly
|
||||
go run cmd/apimain.go
|
||||
# Or generate and run the API using generate_api.go
|
||||
go generate generate_api.go
|
||||
```
|
||||
|
||||
5. To compile, change to the project root directory. For Windows, run `build.bat`, and for Linux, run `build.sh`. After
|
||||
compiling, the corresponding executables will be generated in the `release` directory. Run the compiled executables
|
||||
directly.
|
||||
|
||||
6. Open your browser and visit `http://<your server[:port]>/_admin/`, with default credentials `admin admin`. Please
|
||||
change the password promptly.
|
||||
|
||||
#### nginx reverse proxy
|
||||
Configure reverse proxy in `nginx`
|
||||
```
|
||||
server {
|
||||
listen <your port>;
|
||||
server_name <your server>;
|
||||
location / {
|
||||
proxy_pass http://<api-server[:port]>;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
}
|
||||
```
|
||||
## Others
|
||||
|
||||
- [Change client ID](https://github.com/abdullah-erturk/RustDesk-ID-Changer)
|
||||
- [Web client source](https://hub.docker.com/r/keyurbhole/flutter_web_desk)
|
||||
@@ -1,4 +1,5 @@
|
||||
@echo off
|
||||
rmdir /s /q release
|
||||
go env -w GO111MODULE=on
|
||||
go env -w GOPROXY=https://goproxy.cn,direct
|
||||
go env -w CGO_ENABLED=1
|
||||
|
||||
41
build.sh
Normal file → Executable file
@@ -1,15 +1,46 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
# Automatically get the current environment's GOARCH; if not defined, use the detected system architecture
|
||||
GOARCH=${GOARCH:-$(go env GOARCH)}
|
||||
DOCS="true"
|
||||
# Safely remove the old release directory
|
||||
rm -rf release
|
||||
|
||||
# Set Go environment variables
|
||||
go env -w GO111MODULE=on
|
||||
go env -w GOPROXY=https://goproxy.cn,direct
|
||||
go env -w CGO_ENABLED=1
|
||||
go env -w GOOS=linux
|
||||
go env -w GOARCH=amd64
|
||||
swag init -g cmd/apimain.go --output docs/api --instanceName api --exclude http/controller/admin
|
||||
swag init -g cmd/apimain.go --output docs/admin --instanceName admin --exclude http/controller/api
|
||||
go env -w GOARCH=${GOARCH}
|
||||
|
||||
|
||||
# Generate Swagger documentation if DOCS is not empty
|
||||
if [ -n "${DOCS}" ]; then
|
||||
# Check if swag is installed
|
||||
if ! command -v swag &> /dev/null; then
|
||||
echo "swag command not found. Please install it using:"
|
||||
echo "go install github.com/swaggo/swag/cmd/swag@latest"
|
||||
echo "Skipping Swagger documentation generation due to missing swag tool."
|
||||
else
|
||||
echo "Generating Swagger documentation..."
|
||||
swag init -g cmd/apimain.go --output docs/api --instanceName api --exclude http/controller/admin
|
||||
swag init -g cmd/apimain.go --output docs/admin --instanceName admin --exclude http/controller/api
|
||||
fi
|
||||
else
|
||||
echo "Skipping Swagger documentation generation due to DOCS is empty."
|
||||
fi
|
||||
|
||||
# Compile the Go code and output it to the release directory
|
||||
go build -o release/apimain cmd/apimain.go
|
||||
|
||||
# Copy resource files to the release directory
|
||||
cp -ar resources release/
|
||||
cp -ar docs release/
|
||||
cp -ar conf release/
|
||||
mkdir release/data
|
||||
mkdir release/runtime
|
||||
|
||||
# Create necessary directory structures
|
||||
mkdir -p release/data
|
||||
mkdir -p release/runtime
|
||||
|
||||
echo "Build and setup completed successfully."
|
||||
@@ -12,13 +12,8 @@ import (
|
||||
"Gwen/model"
|
||||
"Gwen/service"
|
||||
"fmt"
|
||||
"github.com/go-playground/locales/en"
|
||||
"github.com/go-playground/locales/zh_Hans_CN"
|
||||
ut "github.com/go-playground/universal-translator"
|
||||
"github.com/go-playground/validator/v10"
|
||||
zh_translations "github.com/go-playground/validator/v10/translations/zh"
|
||||
"github.com/go-redis/redis/v8"
|
||||
"reflect"
|
||||
"github.com/nicksnyder/go-i18n/v2/i18n"
|
||||
)
|
||||
|
||||
// @title 管理系统API
|
||||
@@ -33,9 +28,7 @@ import (
|
||||
// @name Authorization
|
||||
func main() {
|
||||
//配置解析
|
||||
global.Viper = config.Init(&global.Config, func() {
|
||||
fmt.Println(global.Config)
|
||||
})
|
||||
global.Viper = config.Init(&global.Config)
|
||||
|
||||
//日志
|
||||
global.Logger = logger.New(&logger.Config{
|
||||
@@ -44,6 +37,8 @@ func main() {
|
||||
ReportCaller: global.Config.Logger.ReportCaller,
|
||||
})
|
||||
|
||||
global.InitI18n()
|
||||
|
||||
//redis
|
||||
global.Redis = redis.NewClient(&redis.Options{
|
||||
Addr: global.Config.Redis.Addr,
|
||||
@@ -81,7 +76,7 @@ func main() {
|
||||
DatabaseAutoUpdate()
|
||||
|
||||
//validator
|
||||
ApiInitValidator()
|
||||
global.ApiInitValidator()
|
||||
|
||||
//oss
|
||||
global.Oss = &upload.Oss{
|
||||
@@ -105,61 +100,8 @@ func main() {
|
||||
|
||||
}
|
||||
|
||||
func ApiInitValidator() {
|
||||
validate := validator.New()
|
||||
enT := en.New()
|
||||
cn := zh_Hans_CN.New()
|
||||
uni := ut.New(enT, cn)
|
||||
trans, _ := uni.GetTranslator("cn")
|
||||
err := zh_translations.RegisterDefaultTranslations(validate, trans)
|
||||
if err != nil {
|
||||
//退出
|
||||
panic(err)
|
||||
}
|
||||
validate.RegisterTagNameFunc(func(field reflect.StructField) string {
|
||||
label := field.Tag.Get("label")
|
||||
if label == "" {
|
||||
return field.Name
|
||||
}
|
||||
return label
|
||||
})
|
||||
global.Validator.Validate = validate
|
||||
global.Validator.VTrans = trans
|
||||
|
||||
global.Validator.ValidStruct = func(i interface{}) []string {
|
||||
err := global.Validator.Validate.Struct(i)
|
||||
errList := make([]string, 0, 10)
|
||||
if err != nil {
|
||||
if _, ok := err.(*validator.InvalidValidationError); ok {
|
||||
errList = append(errList, err.Error())
|
||||
return errList
|
||||
}
|
||||
for _, err2 := range err.(validator.ValidationErrors) {
|
||||
errList = append(errList, err2.Translate(global.Validator.VTrans))
|
||||
}
|
||||
}
|
||||
return errList
|
||||
}
|
||||
global.Validator.ValidVar = func(field interface{}, tag string) []string {
|
||||
err := global.Validator.Validate.Var(field, tag)
|
||||
fmt.Println(err)
|
||||
errList := make([]string, 0, 10)
|
||||
if err != nil {
|
||||
if _, ok := err.(*validator.InvalidValidationError); ok {
|
||||
errList = append(errList, err.Error())
|
||||
return errList
|
||||
}
|
||||
for _, err2 := range err.(validator.ValidationErrors) {
|
||||
errList = append(errList, err2.Translate(global.Validator.VTrans))
|
||||
}
|
||||
}
|
||||
return errList
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func DatabaseAutoUpdate() {
|
||||
version := 100
|
||||
version := 243
|
||||
|
||||
db := global.DB
|
||||
|
||||
@@ -217,6 +159,14 @@ func Migrate(version uint) {
|
||||
&model.AddressBook{},
|
||||
&model.Peer{},
|
||||
&model.Group{},
|
||||
&model.UserThird{},
|
||||
&model.Oauth{},
|
||||
&model.LoginLog{},
|
||||
&model.ShareRecord{},
|
||||
&model.AuditConn{},
|
||||
&model.AuditFile{},
|
||||
&model.AddressBookCollection{},
|
||||
&model.AddressBookCollectionRule{},
|
||||
)
|
||||
if err != nil {
|
||||
fmt.Println("migrate err :=>", err)
|
||||
@@ -226,13 +176,21 @@ func Migrate(version uint) {
|
||||
var vc int64
|
||||
global.DB.Model(&model.Version{}).Count(&vc)
|
||||
if vc == 1 {
|
||||
localizer := global.Localizer("")
|
||||
defaultGroup, _ := localizer.LocalizeMessage(&i18n.Message{
|
||||
ID: "DefaultGroup",
|
||||
})
|
||||
group := &model.Group{
|
||||
Name: "默认组",
|
||||
Name: defaultGroup,
|
||||
Type: model.GroupTypeDefault,
|
||||
}
|
||||
service.AllService.GroupService.Create(group)
|
||||
|
||||
shareGroup, _ := localizer.LocalizeMessage(&i18n.Message{
|
||||
ID: "ShareGroup",
|
||||
})
|
||||
groupShare := &model.Group{
|
||||
Name: "共享组",
|
||||
Name: shareGroup,
|
||||
Type: model.GroupTypeShare,
|
||||
}
|
||||
service.AllService.GroupService.Create(groupShare)
|
||||
@@ -240,7 +198,7 @@ func Migrate(version uint) {
|
||||
is_admin := true
|
||||
admin := &model.User{
|
||||
Username: "admin",
|
||||
Nickname: "管理员",
|
||||
Nickname: "Admin",
|
||||
Status: model.COMMON_STATUS_ENABLE,
|
||||
IsAdmin: &is_admin,
|
||||
GroupId: 1,
|
||||
|
||||
@@ -1,34 +1,42 @@
|
||||
lang: "zh-CN"
|
||||
app:
|
||||
web-client: 1 # 1:启用 0:禁用
|
||||
gin:
|
||||
api-addr: "0.0.0.0:21114"
|
||||
mode: "release" #release,debug,test
|
||||
resources-path: 'resources' #对外静态文件目录
|
||||
trust-proxy: ""
|
||||
gorm:
|
||||
type: "sqlite"
|
||||
max-idle-conns: 10
|
||||
max-open-conns: 100
|
||||
mysql:
|
||||
username: "root"
|
||||
password: "111111"
|
||||
addr: "192.168.1.66:3308"
|
||||
dbname: "rustdesk2"
|
||||
username: ""
|
||||
password: ""
|
||||
addr: ""
|
||||
dbname: ""
|
||||
rustdesk:
|
||||
id-server: "124.220.134.240:21116"
|
||||
relay-server: "124.220.134.240:21117"
|
||||
id-server: "192.168.1.66:21116"
|
||||
relay-server: "192.168.1.66:21117"
|
||||
api-server: "http://127.0.0.1:21114"
|
||||
key: "ljw19891989"
|
||||
key: "123456789"
|
||||
personal: 1
|
||||
logger:
|
||||
path: "./runtime/log.txt"
|
||||
level: "warn" #trace,debug,info,warn,error,fatal
|
||||
report-caller: true
|
||||
proxy:
|
||||
enable: false
|
||||
host: "http://127.0.0.1:1080"
|
||||
redis:
|
||||
addr: "127.0.0.1:6379"
|
||||
password: ""
|
||||
db: 0
|
||||
logger:
|
||||
path: "./runtime/log.txt"
|
||||
level: "error" #trace,debug,info,warn,error,fatal
|
||||
report-caller: true
|
||||
cache:
|
||||
type: "file"
|
||||
file-dir: "./runtime/cache"
|
||||
redis-addr: "127.0.0.1:6379"
|
||||
redis-pwd: "ljw19891989"
|
||||
redis-pwd: ""
|
||||
redis-db: 0
|
||||
oss:
|
||||
access-key-id: ""
|
||||
@@ -39,4 +47,4 @@ oss:
|
||||
max-byte: 10240
|
||||
jwt:
|
||||
private-key: "./conf/jwt_pri.pem"
|
||||
expire-duration: 360000
|
||||
expire-duration: 360000
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"github.com/fsnotify/fsnotify"
|
||||
"github.com/spf13/viper"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -13,7 +14,13 @@ const (
|
||||
DefaultConfig = "conf/config.yaml"
|
||||
)
|
||||
|
||||
type App struct {
|
||||
WebClient int `mapstructure:"web-client"`
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Lang string `mapstructure:"lang"`
|
||||
App App
|
||||
Gorm Gorm
|
||||
Mysql Mysql
|
||||
Gin Gin
|
||||
@@ -23,10 +30,11 @@ type Config struct {
|
||||
Oss Oss
|
||||
Jwt Jwt
|
||||
Rustdesk Rustdesk
|
||||
Proxy Proxy
|
||||
}
|
||||
|
||||
// Init 初始化配置
|
||||
func Init(rowVal interface{}, cb func()) *viper.Viper {
|
||||
func Init(rowVal interface{}) *viper.Viper {
|
||||
var config string
|
||||
flag.StringVar(&config, "c", "", "choose config file.")
|
||||
flag.Parse()
|
||||
@@ -34,6 +42,9 @@ func Init(rowVal interface{}, cb func()) *viper.Viper {
|
||||
config = DefaultConfig
|
||||
}
|
||||
v := viper.New()
|
||||
v.AutomaticEnv()
|
||||
v.SetEnvKeyReplacer(strings.NewReplacer(".", "_", "-", "_"))
|
||||
v.SetEnvPrefix("RUSTDESK_API")
|
||||
v.SetConfigFile(config)
|
||||
v.SetConfigType("yaml")
|
||||
err := v.ReadInConfig()
|
||||
@@ -47,10 +58,19 @@ func Init(rowVal interface{}, cb func()) *viper.Viper {
|
||||
if err2 := v.Unmarshal(rowVal); err2 != nil {
|
||||
fmt.Println(err2)
|
||||
}
|
||||
cb()
|
||||
})
|
||||
if err := v.Unmarshal(rowVal); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// ReadEnv 读取环境变量
|
||||
func ReadEnv(rowVal interface{}) *viper.Viper {
|
||||
v := viper.New()
|
||||
v.AutomaticEnv()
|
||||
if err := v.Unmarshal(rowVal); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
@@ -5,4 +5,5 @@ type Gin struct {
|
||||
AdminAddr string `mapstructure:"admin-addr"`
|
||||
Mode string
|
||||
ResourcesPath string `mapstructure:"resources-path"`
|
||||
TrustProxy string `mapstructure:"trust-proxy"`
|
||||
}
|
||||
|
||||
20
config/oauth.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package config
|
||||
|
||||
type GithubOauth struct {
|
||||
ClientId string `mapstructure:"client-id"`
|
||||
ClientSecret string `mapstructure:"client-secret"`
|
||||
RedirectUrl string `mapstructure:"redirect-url"`
|
||||
}
|
||||
|
||||
type GoogleOauth struct {
|
||||
ClientId string `mapstructure:"client-id"`
|
||||
ClientSecret string `mapstructure:"client-secret"`
|
||||
RedirectUrl string `mapstructure:"redirect-url"`
|
||||
}
|
||||
|
||||
type OidcOauth struct {
|
||||
Issuer string `mapstructure:"issuer"`
|
||||
ClientId string `mapstructure:"client-id"`
|
||||
ClientSecret string `mapstructure:"client-secret"`
|
||||
RedirectUrl string `mapstructure:"redirect-url"`
|
||||
}
|
||||
6
config/proxy.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package config
|
||||
|
||||
type Proxy struct {
|
||||
Enable bool `mapstructure:"enable"`
|
||||
Host string `mapstructure:"host"`
|
||||
}
|
||||
@@ -5,4 +5,5 @@ type Rustdesk struct {
|
||||
RelayServer string `mapstructure:"relay-server"`
|
||||
ApiServer string `mapstructure:"api-server"`
|
||||
Key string `mapstructure:"key"`
|
||||
Personal int `mapstructure:"personal"`
|
||||
}
|
||||
|
||||
20
docker-compose-dev.yaml
Normal file
@@ -0,0 +1,20 @@
|
||||
services:
|
||||
rustdesk-api:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile.dev
|
||||
# image: lejianwen/rustdesk-api
|
||||
container_name: rustdesk-api
|
||||
environment:
|
||||
- TZ=Asia/Shanghai
|
||||
- RUSTDESK_API_RUSTDESK_ID_SERVER=192.168.1.66:21116
|
||||
- RUSTDESK_API_RUSTDESK_RELAY_SERVER=192.168.1.66:21117
|
||||
- RUSTDESK_API_RUSTDESK_API_SERVER=http://127.0.0.1:21114
|
||||
- RUSTDESK_API_RUSTDESK_KEY=123456789
|
||||
ports:
|
||||
- 21114:21114
|
||||
volumes:
|
||||
- ./data/rustdesk/api:/app/data #将数据库挂载出来方便备份
|
||||
- ./conf:/app/conf # config
|
||||
# - ./resources:/app/resources # 静态资源
|
||||
restart: unless-stopped
|
||||
17
docker-compose.yaml
Normal file
@@ -0,0 +1,17 @@
|
||||
services:
|
||||
rustdesk-api:
|
||||
image: lejianwen/rustdesk-api
|
||||
container_name: rustdesk-api
|
||||
environment:
|
||||
- TZ=Asia/Shanghai
|
||||
- RUSTDESK_API_RUSTDESK_ID_SERVER=192.168.1.66:21116
|
||||
- RUSTDESK_API_RUSTDESK_RELAY_SERVER=192.168.1.66:21117
|
||||
- RUSTDESK_API_RUSTDESK_API_SERVER=http://127.0.0.1:21114
|
||||
- RUSTDESK_API_RUSTDESK_KEY=123456789
|
||||
ports:
|
||||
- 21114:21114
|
||||
volumes:
|
||||
- ./data/rustdesk/api:/app/data # database
|
||||
# - ./conf:/app/conf # config
|
||||
# - ./resources:/app/resources # 静态资源
|
||||
restart: unless-stopped
|
||||
BIN
docs/admin_webclient.png
Normal file
|
After Width: | Height: | Size: 51 KiB |
@@ -121,14 +121,14 @@ const docTemplateapi = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/ab/add": {
|
||||
"/ab/peer/add/{guid}": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": []
|
||||
}
|
||||
],
|
||||
"description": "标签",
|
||||
"description": "添加地址",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
@@ -136,9 +136,59 @@ const docTemplateapi = `{
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"地址"
|
||||
"地址[Personal]"
|
||||
],
|
||||
"summary": "添加地址",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "guid",
|
||||
"name": "guid",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": []
|
||||
}
|
||||
],
|
||||
"description": "删除地址",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"地址[Personal]"
|
||||
],
|
||||
"summary": "删除地址",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "guid",
|
||||
"name": "guid",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"summary": "标签添加",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
@@ -155,14 +205,14 @@ const docTemplateapi = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/ab/personal": {
|
||||
"post": {
|
||||
"/ab/peer/update/{guid}": {
|
||||
"put": {
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": []
|
||||
}
|
||||
],
|
||||
"description": "个人信息",
|
||||
"description": "更新地址",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
@@ -170,9 +220,106 @@ const docTemplateapi = `{
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"用户"
|
||||
"地址[Personal]"
|
||||
],
|
||||
"summary": "个人信息",
|
||||
"summary": "更新地址",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "guid",
|
||||
"name": "guid",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/ab/peers": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": []
|
||||
}
|
||||
],
|
||||
"description": "地址",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"地址[Personal]"
|
||||
],
|
||||
"summary": "地址列表",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "页码",
|
||||
"name": "current",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "每页数量",
|
||||
"name": "pageSize",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "guid",
|
||||
"name": "ab",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.Response"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.Response"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/ab/personal": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": []
|
||||
}
|
||||
],
|
||||
"description": "个人地址",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"地址[Personal]"
|
||||
],
|
||||
"summary": "个人地址",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "string valid",
|
||||
@@ -199,6 +346,313 @@ const docTemplateapi = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/ab/settings": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": []
|
||||
}
|
||||
],
|
||||
"description": "设置",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"地址[Personal]"
|
||||
],
|
||||
"summary": "设置",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "string valid",
|
||||
"name": "string",
|
||||
"in": "body",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.Response"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.Response"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/ab/shared/profiles": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": []
|
||||
}
|
||||
],
|
||||
"description": "共享",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"地址[Personal]"
|
||||
],
|
||||
"summary": "共享地址簿",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "页码",
|
||||
"name": "current",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "每页数量",
|
||||
"name": "pageSize",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.Response"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.Response"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/ab/tag/add/{guid}": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": []
|
||||
}
|
||||
],
|
||||
"description": "标签",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"地址[Personal]"
|
||||
],
|
||||
"summary": "标签添加",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "guid",
|
||||
"name": "guid",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/ab/tag/rename/{guid}": {
|
||||
"put": {
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": []
|
||||
}
|
||||
],
|
||||
"description": "标签",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"地址[Personal]"
|
||||
],
|
||||
"summary": "标签重命名",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "guid",
|
||||
"name": "guid",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/ab/tag/update/{guid}": {
|
||||
"put": {
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": []
|
||||
}
|
||||
],
|
||||
"description": "标签",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"地址[Personal]"
|
||||
],
|
||||
"summary": "标签修改颜色",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "guid",
|
||||
"name": "guid",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/ab/tag/{guid}": {
|
||||
"delete": {
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": []
|
||||
}
|
||||
],
|
||||
"description": "标签",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"地址[Personal]"
|
||||
],
|
||||
"summary": "标签删除",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "guid",
|
||||
"name": "guid",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/ab/tags/{guid}": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": []
|
||||
}
|
||||
],
|
||||
"description": "标签",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"地址[Personal]"
|
||||
],
|
||||
"summary": "标签",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "guid",
|
||||
"name": "guid",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/model.TagList"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api": {
|
||||
"get": {
|
||||
"security": [
|
||||
@@ -233,14 +687,9 @@ const docTemplateapi = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/currentUser": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"token": []
|
||||
}
|
||||
],
|
||||
"description": "用户信息",
|
||||
"/audit/conn": {
|
||||
"post": {
|
||||
"description": "审计连接",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
@@ -248,14 +697,65 @@ const docTemplateapi = `{
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"用户"
|
||||
"审计"
|
||||
],
|
||||
"summary": "审计连接",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "审计连接",
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.AuditConnForm"
|
||||
}
|
||||
}
|
||||
],
|
||||
"summary": "用户信息",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.UserPayload"
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.Response"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/audit/file": {
|
||||
"post": {
|
||||
"description": "审计文件",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"审计"
|
||||
],
|
||||
"summary": "审计文件",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "审计文件",
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.AuditFileForm"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
@@ -334,7 +834,7 @@ const docTemplateapi = `{
|
||||
}
|
||||
},
|
||||
"/login-options": {
|
||||
"post": {
|
||||
"get": {
|
||||
"description": "登录选项",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
@@ -394,6 +894,93 @@ const docTemplateapi = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/oauth/callback": {
|
||||
"get": {
|
||||
"description": "OauthCallback",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Oauth"
|
||||
],
|
||||
"summary": "OauthCallback",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.LoginRes"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/oidc/auth": {
|
||||
"post": {
|
||||
"description": "OidcAuth",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Oauth"
|
||||
],
|
||||
"summary": "OidcAuth",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.LoginRes"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/oidc/auth-query": {
|
||||
"get": {
|
||||
"description": "OidcAuthQuery",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Oauth"
|
||||
],
|
||||
"summary": "OidcAuthQuery",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.LoginRes"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/peers": {
|
||||
"get": {
|
||||
"security": [
|
||||
@@ -488,13 +1075,37 @@ const docTemplateapi = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/shared-peer": {
|
||||
"post": {
|
||||
"description": "分享的peer",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"WEBCLIENT"
|
||||
],
|
||||
"summary": "分享的peer",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.Response"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.Response"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/sysinfo": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": []
|
||||
}
|
||||
],
|
||||
"description": "提交系统信息",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
@@ -656,21 +1267,108 @@ const docTemplateapi = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"api.AuditConnForm": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"action": {
|
||||
"type": "string"
|
||||
},
|
||||
"conn_id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"ip": {
|
||||
"type": "string"
|
||||
},
|
||||
"peer": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"session_id": {
|
||||
"type": "number"
|
||||
},
|
||||
"type": {
|
||||
"type": "integer"
|
||||
},
|
||||
"uuid": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"api.AuditFileForm": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"info": {
|
||||
"type": "string"
|
||||
},
|
||||
"is_file": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
},
|
||||
"peer_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "integer"
|
||||
},
|
||||
"uuid": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"api.DeviceInfoInLogin": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"os": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"api.LoginForm": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"username"
|
||||
],
|
||||
"properties": {
|
||||
"autoLogin": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"deviceInfo": {
|
||||
"$ref": "#/definitions/api.DeviceInfoInLogin"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"password": {
|
||||
"type": "string",
|
||||
"maxLength": 20,
|
||||
"minLength": 4
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
},
|
||||
"username": {
|
||||
"type": "string",
|
||||
"maxLength": 10,
|
||||
"minLength": 4
|
||||
},
|
||||
"uuid": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -729,6 +1427,10 @@ const docTemplateapi = `{
|
||||
"email": {
|
||||
"type": "string"
|
||||
},
|
||||
"info": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
},
|
||||
"is_admin": {
|
||||
"type": "boolean"
|
||||
},
|
||||
@@ -743,9 +1445,38 @@ const docTemplateapi = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"model.AddressBookCollection": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"name"
|
||||
],
|
||||
"properties": {
|
||||
"created_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"updated_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"user_id": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"model.Tag": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"collection": {
|
||||
"$ref": "#/definitions/model.AddressBookCollection"
|
||||
},
|
||||
"collection_id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"color": {
|
||||
"description": "color 是flutter的颜色值,从0x00000000 到 0xFFFFFFFF; 前两位表示透明度,后面6位表示颜色, 可以转成rgba",
|
||||
"type": "integer"
|
||||
@@ -767,6 +1498,26 @@ const docTemplateapi = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"model.TagList": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"list": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/model.Tag"
|
||||
}
|
||||
},
|
||||
"page": {
|
||||
"type": "integer"
|
||||
},
|
||||
"page_size": {
|
||||
"type": "integer"
|
||||
},
|
||||
"total": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"response.DataResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
||||
@@ -114,14 +114,14 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/ab/add": {
|
||||
"/ab/peer/add/{guid}": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": []
|
||||
}
|
||||
],
|
||||
"description": "标签",
|
||||
"description": "添加地址",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
@@ -129,9 +129,59 @@
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"地址"
|
||||
"地址[Personal]"
|
||||
],
|
||||
"summary": "添加地址",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "guid",
|
||||
"name": "guid",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": []
|
||||
}
|
||||
],
|
||||
"description": "删除地址",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"地址[Personal]"
|
||||
],
|
||||
"summary": "删除地址",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "guid",
|
||||
"name": "guid",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"summary": "标签添加",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
@@ -148,14 +198,14 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/ab/personal": {
|
||||
"post": {
|
||||
"/ab/peer/update/{guid}": {
|
||||
"put": {
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": []
|
||||
}
|
||||
],
|
||||
"description": "个人信息",
|
||||
"description": "更新地址",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
@@ -163,9 +213,106 @@
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"用户"
|
||||
"地址[Personal]"
|
||||
],
|
||||
"summary": "个人信息",
|
||||
"summary": "更新地址",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "guid",
|
||||
"name": "guid",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/ab/peers": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": []
|
||||
}
|
||||
],
|
||||
"description": "地址",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"地址[Personal]"
|
||||
],
|
||||
"summary": "地址列表",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "页码",
|
||||
"name": "current",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "每页数量",
|
||||
"name": "pageSize",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "guid",
|
||||
"name": "ab",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.Response"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.Response"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/ab/personal": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": []
|
||||
}
|
||||
],
|
||||
"description": "个人地址",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"地址[Personal]"
|
||||
],
|
||||
"summary": "个人地址",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "string valid",
|
||||
@@ -192,6 +339,313 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/ab/settings": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": []
|
||||
}
|
||||
],
|
||||
"description": "设置",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"地址[Personal]"
|
||||
],
|
||||
"summary": "设置",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "string valid",
|
||||
"name": "string",
|
||||
"in": "body",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.Response"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.Response"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/ab/shared/profiles": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": []
|
||||
}
|
||||
],
|
||||
"description": "共享",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"地址[Personal]"
|
||||
],
|
||||
"summary": "共享地址簿",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "页码",
|
||||
"name": "current",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "每页数量",
|
||||
"name": "pageSize",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.Response"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.Response"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/ab/tag/add/{guid}": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": []
|
||||
}
|
||||
],
|
||||
"description": "标签",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"地址[Personal]"
|
||||
],
|
||||
"summary": "标签添加",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "guid",
|
||||
"name": "guid",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/ab/tag/rename/{guid}": {
|
||||
"put": {
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": []
|
||||
}
|
||||
],
|
||||
"description": "标签",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"地址[Personal]"
|
||||
],
|
||||
"summary": "标签重命名",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "guid",
|
||||
"name": "guid",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/ab/tag/update/{guid}": {
|
||||
"put": {
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": []
|
||||
}
|
||||
],
|
||||
"description": "标签",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"地址[Personal]"
|
||||
],
|
||||
"summary": "标签修改颜色",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "guid",
|
||||
"name": "guid",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/ab/tag/{guid}": {
|
||||
"delete": {
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": []
|
||||
}
|
||||
],
|
||||
"description": "标签",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"地址[Personal]"
|
||||
],
|
||||
"summary": "标签删除",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "guid",
|
||||
"name": "guid",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/ab/tags/{guid}": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": []
|
||||
}
|
||||
],
|
||||
"description": "标签",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"地址[Personal]"
|
||||
],
|
||||
"summary": "标签",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "guid",
|
||||
"name": "guid",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/model.TagList"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api": {
|
||||
"get": {
|
||||
"security": [
|
||||
@@ -226,14 +680,9 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/currentUser": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"token": []
|
||||
}
|
||||
],
|
||||
"description": "用户信息",
|
||||
"/audit/conn": {
|
||||
"post": {
|
||||
"description": "审计连接",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
@@ -241,14 +690,65 @@
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"用户"
|
||||
"审计"
|
||||
],
|
||||
"summary": "审计连接",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "审计连接",
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.AuditConnForm"
|
||||
}
|
||||
}
|
||||
],
|
||||
"summary": "用户信息",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.UserPayload"
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.Response"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/audit/file": {
|
||||
"post": {
|
||||
"description": "审计文件",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"审计"
|
||||
],
|
||||
"summary": "审计文件",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "审计文件",
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.AuditFileForm"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
@@ -327,7 +827,7 @@
|
||||
}
|
||||
},
|
||||
"/login-options": {
|
||||
"post": {
|
||||
"get": {
|
||||
"description": "登录选项",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
@@ -387,6 +887,93 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/oauth/callback": {
|
||||
"get": {
|
||||
"description": "OauthCallback",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Oauth"
|
||||
],
|
||||
"summary": "OauthCallback",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.LoginRes"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/oidc/auth": {
|
||||
"post": {
|
||||
"description": "OidcAuth",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Oauth"
|
||||
],
|
||||
"summary": "OidcAuth",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.LoginRes"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/oidc/auth-query": {
|
||||
"get": {
|
||||
"description": "OidcAuthQuery",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Oauth"
|
||||
],
|
||||
"summary": "OidcAuthQuery",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.LoginRes"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/peers": {
|
||||
"get": {
|
||||
"security": [
|
||||
@@ -481,13 +1068,37 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/shared-peer": {
|
||||
"post": {
|
||||
"description": "分享的peer",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"WEBCLIENT"
|
||||
],
|
||||
"summary": "分享的peer",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.Response"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.Response"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/sysinfo": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": []
|
||||
}
|
||||
],
|
||||
"description": "提交系统信息",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
@@ -649,21 +1260,108 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"api.AuditConnForm": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"action": {
|
||||
"type": "string"
|
||||
},
|
||||
"conn_id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"ip": {
|
||||
"type": "string"
|
||||
},
|
||||
"peer": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"session_id": {
|
||||
"type": "number"
|
||||
},
|
||||
"type": {
|
||||
"type": "integer"
|
||||
},
|
||||
"uuid": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"api.AuditFileForm": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"info": {
|
||||
"type": "string"
|
||||
},
|
||||
"is_file": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
},
|
||||
"peer_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "integer"
|
||||
},
|
||||
"uuid": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"api.DeviceInfoInLogin": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"os": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"api.LoginForm": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"username"
|
||||
],
|
||||
"properties": {
|
||||
"autoLogin": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"deviceInfo": {
|
||||
"$ref": "#/definitions/api.DeviceInfoInLogin"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"password": {
|
||||
"type": "string",
|
||||
"maxLength": 20,
|
||||
"minLength": 4
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
},
|
||||
"username": {
|
||||
"type": "string",
|
||||
"maxLength": 10,
|
||||
"minLength": 4
|
||||
},
|
||||
"uuid": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -722,6 +1420,10 @@
|
||||
"email": {
|
||||
"type": "string"
|
||||
},
|
||||
"info": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
},
|
||||
"is_admin": {
|
||||
"type": "boolean"
|
||||
},
|
||||
@@ -736,9 +1438,38 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"model.AddressBookCollection": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"name"
|
||||
],
|
||||
"properties": {
|
||||
"created_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"updated_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"user_id": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"model.Tag": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"collection": {
|
||||
"$ref": "#/definitions/model.AddressBookCollection"
|
||||
},
|
||||
"collection_id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"color": {
|
||||
"description": "color 是flutter的颜色值,从0x00000000 到 0xFFFFFFFF; 前两位表示透明度,后面6位表示颜色, 可以转成rgba",
|
||||
"type": "integer"
|
||||
@@ -760,6 +1491,26 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"model.TagList": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"list": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/model.Tag"
|
||||
}
|
||||
},
|
||||
"page": {
|
||||
"type": "integer"
|
||||
},
|
||||
"page_size": {
|
||||
"type": "integer"
|
||||
},
|
||||
"total": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"response.DataResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
||||
@@ -6,16 +6,73 @@ definitions:
|
||||
example: '{"tags":["tag1","tag2","tag3"],"peers":[{"id":"abc","username":"abv-l","hostname":"","platform":"Windows","alias":"","tags":["tag1","tag2"],"hash":"hash"}],"tag_colors":"{\"tag1\":4288585374,\"tag2\":4278238420,\"tag3\":4291681337}"}'
|
||||
type: string
|
||||
type: object
|
||||
api.AuditConnForm:
|
||||
properties:
|
||||
action:
|
||||
type: string
|
||||
conn_id:
|
||||
type: integer
|
||||
id:
|
||||
type: string
|
||||
ip:
|
||||
type: string
|
||||
peer:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
session_id:
|
||||
type: number
|
||||
type:
|
||||
type: integer
|
||||
uuid:
|
||||
type: string
|
||||
type: object
|
||||
api.AuditFileForm:
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
info:
|
||||
type: string
|
||||
is_file:
|
||||
type: boolean
|
||||
path:
|
||||
type: string
|
||||
peer_id:
|
||||
type: string
|
||||
type:
|
||||
type: integer
|
||||
uuid:
|
||||
type: string
|
||||
type: object
|
||||
api.DeviceInfoInLogin:
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
os:
|
||||
type: string
|
||||
type:
|
||||
type: string
|
||||
type: object
|
||||
api.LoginForm:
|
||||
properties:
|
||||
autoLogin:
|
||||
type: boolean
|
||||
deviceInfo:
|
||||
$ref: '#/definitions/api.DeviceInfoInLogin'
|
||||
id:
|
||||
type: string
|
||||
password:
|
||||
maxLength: 20
|
||||
minLength: 4
|
||||
type: string
|
||||
type:
|
||||
type: string
|
||||
username:
|
||||
maxLength: 10
|
||||
minLength: 4
|
||||
type: string
|
||||
uuid:
|
||||
type: string
|
||||
required:
|
||||
- username
|
||||
type: object
|
||||
@@ -55,6 +112,9 @@ definitions:
|
||||
properties:
|
||||
email:
|
||||
type: string
|
||||
info:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
is_admin:
|
||||
type: boolean
|
||||
name:
|
||||
@@ -64,8 +124,27 @@ definitions:
|
||||
status:
|
||||
type: integer
|
||||
type: object
|
||||
model.AddressBookCollection:
|
||||
properties:
|
||||
created_at:
|
||||
type: string
|
||||
id:
|
||||
type: integer
|
||||
name:
|
||||
type: string
|
||||
updated_at:
|
||||
type: string
|
||||
user_id:
|
||||
type: integer
|
||||
required:
|
||||
- name
|
||||
type: object
|
||||
model.Tag:
|
||||
properties:
|
||||
collection:
|
||||
$ref: '#/definitions/model.AddressBookCollection'
|
||||
collection_id:
|
||||
type: integer
|
||||
color:
|
||||
description: color 是flutter的颜色值,从0x00000000 到 0xFFFFFFFF; 前两位表示透明度,后面6位表示颜色,
|
||||
可以转成rgba
|
||||
@@ -81,6 +160,19 @@ definitions:
|
||||
user_id:
|
||||
type: integer
|
||||
type: object
|
||||
model.TagList:
|
||||
properties:
|
||||
list:
|
||||
items:
|
||||
$ref: '#/definitions/model.Tag'
|
||||
type: array
|
||||
page:
|
||||
type: integer
|
||||
page_size:
|
||||
type: integer
|
||||
total:
|
||||
type: integer
|
||||
type: object
|
||||
response.DataResponse:
|
||||
properties:
|
||||
data: {}
|
||||
@@ -173,11 +265,17 @@ paths:
|
||||
summary: 地址更新
|
||||
tags:
|
||||
- 地址
|
||||
/ab/add:
|
||||
post:
|
||||
/ab/peer/add/{guid}:
|
||||
delete:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 标签
|
||||
description: 删除地址
|
||||
parameters:
|
||||
- description: guid
|
||||
in: path
|
||||
name: guid
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
@@ -191,14 +289,101 @@ paths:
|
||||
$ref: '#/definitions/response.ErrorResponse'
|
||||
security:
|
||||
- BearerAuth: []
|
||||
summary: 标签添加
|
||||
summary: 删除地址
|
||||
tags:
|
||||
- 地址
|
||||
- 地址[Personal]
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 添加地址
|
||||
parameters:
|
||||
- description: guid
|
||||
in: path
|
||||
name: guid
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
type: string
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/response.ErrorResponse'
|
||||
security:
|
||||
- BearerAuth: []
|
||||
summary: 添加地址
|
||||
tags:
|
||||
- 地址[Personal]
|
||||
/ab/peer/update/{guid}:
|
||||
put:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 更新地址
|
||||
parameters:
|
||||
- description: guid
|
||||
in: path
|
||||
name: guid
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
type: string
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/response.ErrorResponse'
|
||||
security:
|
||||
- BearerAuth: []
|
||||
summary: 更新地址
|
||||
tags:
|
||||
- 地址[Personal]
|
||||
/ab/peers:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 地址
|
||||
parameters:
|
||||
- description: 页码
|
||||
in: query
|
||||
name: current
|
||||
type: integer
|
||||
- description: 每页数量
|
||||
in: query
|
||||
name: pageSize
|
||||
type: integer
|
||||
- description: guid
|
||||
in: query
|
||||
name: ab
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/response.Response'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/response.Response'
|
||||
security:
|
||||
- BearerAuth: []
|
||||
summary: 地址列表
|
||||
tags:
|
||||
- 地址[Personal]
|
||||
/ab/personal:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 个人信息
|
||||
description: 个人地址
|
||||
parameters:
|
||||
- description: string valid
|
||||
in: body
|
||||
@@ -218,9 +403,201 @@ paths:
|
||||
$ref: '#/definitions/response.Response'
|
||||
security:
|
||||
- BearerAuth: []
|
||||
summary: 个人信息
|
||||
summary: 个人地址
|
||||
tags:
|
||||
- 用户
|
||||
- 地址[Personal]
|
||||
/ab/settings:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 设置
|
||||
parameters:
|
||||
- description: string valid
|
||||
in: body
|
||||
name: string
|
||||
schema:
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/response.Response'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/response.Response'
|
||||
security:
|
||||
- BearerAuth: []
|
||||
summary: 设置
|
||||
tags:
|
||||
- 地址[Personal]
|
||||
/ab/shared/profiles:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 共享
|
||||
parameters:
|
||||
- description: 页码
|
||||
in: query
|
||||
name: current
|
||||
type: integer
|
||||
- description: 每页数量
|
||||
in: query
|
||||
name: pageSize
|
||||
type: integer
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/response.Response'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/response.Response'
|
||||
security:
|
||||
- BearerAuth: []
|
||||
summary: 共享地址簿
|
||||
tags:
|
||||
- 地址[Personal]
|
||||
/ab/tag/{guid}:
|
||||
delete:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 标签
|
||||
parameters:
|
||||
- description: guid
|
||||
in: path
|
||||
name: guid
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
type: string
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/response.ErrorResponse'
|
||||
security:
|
||||
- BearerAuth: []
|
||||
summary: 标签删除
|
||||
tags:
|
||||
- 地址[Personal]
|
||||
/ab/tag/add/{guid}:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 标签
|
||||
parameters:
|
||||
- description: guid
|
||||
in: path
|
||||
name: guid
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
type: string
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/response.ErrorResponse'
|
||||
security:
|
||||
- BearerAuth: []
|
||||
summary: 标签添加
|
||||
tags:
|
||||
- 地址[Personal]
|
||||
/ab/tag/rename/{guid}:
|
||||
put:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 标签
|
||||
parameters:
|
||||
- description: guid
|
||||
in: path
|
||||
name: guid
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
type: string
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/response.ErrorResponse'
|
||||
security:
|
||||
- BearerAuth: []
|
||||
summary: 标签重命名
|
||||
tags:
|
||||
- 地址[Personal]
|
||||
/ab/tag/update/{guid}:
|
||||
put:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 标签
|
||||
parameters:
|
||||
- description: guid
|
||||
in: path
|
||||
name: guid
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
type: string
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/response.ErrorResponse'
|
||||
security:
|
||||
- BearerAuth: []
|
||||
summary: 标签修改颜色
|
||||
tags:
|
||||
- 地址[Personal]
|
||||
/ab/tags/{guid}:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 标签
|
||||
parameters:
|
||||
- description: guid
|
||||
in: path
|
||||
name: guid
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/model.TagList'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/response.ErrorResponse'
|
||||
security:
|
||||
- BearerAuth: []
|
||||
summary: 标签
|
||||
tags:
|
||||
- 地址[Personal]
|
||||
/api:
|
||||
get:
|
||||
consumes:
|
||||
@@ -242,27 +619,58 @@ paths:
|
||||
summary: 用户信息
|
||||
tags:
|
||||
- 用户
|
||||
/currentUser:
|
||||
get:
|
||||
/audit/conn:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 用户信息
|
||||
description: 审计连接
|
||||
parameters:
|
||||
- description: 审计连接
|
||||
in: body
|
||||
name: body
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/api.AuditConnForm'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/api.UserPayload'
|
||||
type: string
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/response.Response'
|
||||
security:
|
||||
- token: []
|
||||
summary: 用户信息
|
||||
summary: 审计连接
|
||||
tags:
|
||||
- 用户
|
||||
- 审计
|
||||
/audit/file:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 审计文件
|
||||
parameters:
|
||||
- description: 审计文件
|
||||
in: body
|
||||
name: body
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/api.AuditFileForm'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
type: string
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/response.Response'
|
||||
summary: 审计文件
|
||||
tags:
|
||||
- 审计
|
||||
/heartbeat:
|
||||
post:
|
||||
consumes:
|
||||
@@ -307,7 +715,7 @@ paths:
|
||||
tags:
|
||||
- 登录
|
||||
/login-options:
|
||||
post:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 登录选项
|
||||
@@ -346,6 +754,63 @@ paths:
|
||||
summary: 登出
|
||||
tags:
|
||||
- 登录
|
||||
/oauth/callback:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: OauthCallback
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/api.LoginRes'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/response.ErrorResponse'
|
||||
summary: OauthCallback
|
||||
tags:
|
||||
- Oauth
|
||||
/oidc/auth:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: OidcAuth
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/api.LoginRes'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/response.ErrorResponse'
|
||||
summary: OidcAuth
|
||||
tags:
|
||||
- Oauth
|
||||
/oidc/auth-query:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: OidcAuthQuery
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/api.LoginRes'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/response.ErrorResponse'
|
||||
summary: OidcAuthQuery
|
||||
tags:
|
||||
- Oauth
|
||||
/peers:
|
||||
get:
|
||||
consumes:
|
||||
@@ -405,6 +870,25 @@ paths:
|
||||
summary: 服务配置
|
||||
tags:
|
||||
- WEBCLIENT
|
||||
/shared-peer:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 分享的peer
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/response.Response'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/response.Response'
|
||||
summary: 分享的peer
|
||||
tags:
|
||||
- WEBCLIENT
|
||||
/sysinfo:
|
||||
post:
|
||||
consumes:
|
||||
@@ -428,8 +912,6 @@ paths:
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/response.ErrorResponse'
|
||||
security:
|
||||
- BearerAuth: []
|
||||
summary: 提交系统信息
|
||||
tags:
|
||||
- 地址
|
||||
|
||||
BIN
docs/en_img/admin_webclient.png
Normal file
|
After Width: | Height: | Size: 54 KiB |
BIN
docs/en_img/pc_ab.png
Normal file
|
After Width: | Height: | Size: 53 KiB |
BIN
docs/en_img/pc_gr.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
docs/en_img/pc_login.png
Normal file
|
After Width: | Height: | Size: 46 KiB |
BIN
docs/en_img/web_admin.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
docs/en_img/web_admin_gr.png
Normal file
|
After Width: | Height: | Size: 8.5 KiB |
BIN
docs/en_img/web_admin_oauth.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
docs/en_img/web_admin_user.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
docs/en_img/web_resetpwd.png
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
docs/pc_login.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 7.7 KiB After Width: | Height: | Size: 8.5 KiB |
BIN
docs/web_admin_oauth.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
docs/web_admin_user.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 4.7 KiB |
|
Before Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 13 KiB |
3
generate_run.go
Normal file
@@ -0,0 +1,3 @@
|
||||
package Gwen
|
||||
|
||||
//go:generate go run cmd/apimain.go
|
||||
124
global/apiValidator.go
Normal file
@@ -0,0 +1,124 @@
|
||||
package global
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-playground/locales/en"
|
||||
"github.com/go-playground/locales/ko"
|
||||
"github.com/go-playground/locales/ru"
|
||||
"github.com/go-playground/locales/zh_Hans_CN"
|
||||
|
||||
ut "github.com/go-playground/universal-translator"
|
||||
"github.com/go-playground/validator/v10"
|
||||
en_translations "github.com/go-playground/validator/v10/translations/en"
|
||||
ru_translations "github.com/go-playground/validator/v10/translations/ru"
|
||||
zh_translations "github.com/go-playground/validator/v10/translations/zh"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
func ApiInitValidator() {
|
||||
validate := validator.New()
|
||||
|
||||
// 定义不同的语言翻译
|
||||
enT := en.New()
|
||||
cn := zh_Hans_CN.New()
|
||||
koT := ko.New()
|
||||
ruT := ru.New()
|
||||
|
||||
uni := ut.New(enT, cn, koT, ruT)
|
||||
|
||||
enTrans, _ := uni.GetTranslator("en")
|
||||
zhTrans, _ := uni.GetTranslator("zh_Hans_CN")
|
||||
koTrans, _ := uni.GetTranslator("ko")
|
||||
ruTrans, _ := uni.GetTranslator("ru")
|
||||
|
||||
err := zh_translations.RegisterDefaultTranslations(validate, zhTrans)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
err = en_translations.RegisterDefaultTranslations(validate, enTrans)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
//validate没有ko的翻译,使用zh的翻译
|
||||
err = zh_translations.RegisterDefaultTranslations(validate, koTrans)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
err = ru_translations.RegisterDefaultTranslations(validate, ruTrans)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
validate.RegisterTagNameFunc(func(field reflect.StructField) string {
|
||||
label := field.Tag.Get("label")
|
||||
if label == "" {
|
||||
return field.Name
|
||||
}
|
||||
return label
|
||||
})
|
||||
Validator.Validate = validate
|
||||
Validator.UT = uni // 存储 Universal Translator
|
||||
Validator.VTrans = zhTrans
|
||||
|
||||
Validator.ValidStruct = func(ctx *gin.Context, i interface{}) []string {
|
||||
err := Validator.Validate.Struct(i)
|
||||
lang := ctx.GetHeader("Accept-Language")
|
||||
if lang == "" {
|
||||
lang = Config.Lang
|
||||
}
|
||||
trans := getTranslatorForLang(lang)
|
||||
errList := make([]string, 0, 10)
|
||||
if err != nil {
|
||||
if _, ok := err.(*validator.InvalidValidationError); ok {
|
||||
errList = append(errList, err.Error())
|
||||
return errList
|
||||
}
|
||||
for _, err2 := range err.(validator.ValidationErrors) {
|
||||
errList = append(errList, err2.Translate(trans))
|
||||
}
|
||||
}
|
||||
return errList
|
||||
}
|
||||
Validator.ValidVar = func(ctx *gin.Context, field interface{}, tag string) []string {
|
||||
err := Validator.Validate.Var(field, tag)
|
||||
lang := ctx.GetHeader("Accept-Language")
|
||||
if lang == "" {
|
||||
lang = Config.Lang
|
||||
}
|
||||
trans := getTranslatorForLang(lang)
|
||||
errList := make([]string, 0, 10)
|
||||
if err != nil {
|
||||
if _, ok := err.(*validator.InvalidValidationError); ok {
|
||||
errList = append(errList, err.Error())
|
||||
return errList
|
||||
}
|
||||
for _, err2 := range err.(validator.ValidationErrors) {
|
||||
errList = append(errList, err2.Translate(trans))
|
||||
}
|
||||
}
|
||||
return errList
|
||||
}
|
||||
}
|
||||
func getTranslatorForLang(lang string) ut.Translator {
|
||||
switch lang {
|
||||
case "zh_CN":
|
||||
fallthrough
|
||||
case "zh-CN":
|
||||
fallthrough
|
||||
case "zh":
|
||||
trans, _ := Validator.UT.GetTranslator("zh_Hans_CN")
|
||||
return trans
|
||||
case "ko":
|
||||
trans, _ := Validator.UT.GetTranslator("ko")
|
||||
return trans
|
||||
case "ru":
|
||||
trans, _ := Validator.UT.GetTranslator("ru")
|
||||
return trans
|
||||
case "en":
|
||||
fallthrough
|
||||
default:
|
||||
trans, _ := Validator.UT.GetTranslator("en")
|
||||
return trans
|
||||
}
|
||||
}
|
||||
@@ -6,9 +6,11 @@ import (
|
||||
"Gwen/lib/jwt"
|
||||
"Gwen/lib/lock"
|
||||
"Gwen/lib/upload"
|
||||
"github.com/gin-gonic/gin"
|
||||
ut "github.com/go-playground/universal-translator"
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/go-redis/redis/v8"
|
||||
"github.com/nicksnyder/go-i18n/v2/i18n"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/viper"
|
||||
"gorm.io/gorm"
|
||||
@@ -23,11 +25,13 @@ var (
|
||||
Cache cache.Handler
|
||||
Validator struct {
|
||||
Validate *validator.Validate
|
||||
UT *ut.UniversalTranslator
|
||||
VTrans ut.Translator
|
||||
ValidStruct func(interface{}) []string
|
||||
ValidVar func(field interface{}, tag string) []string
|
||||
ValidStruct func(*gin.Context, interface{}) []string
|
||||
ValidVar func(ctx *gin.Context, field interface{}, tag string) []string
|
||||
}
|
||||
Oss *upload.Oss
|
||||
Jwt *jwt.Jwt
|
||||
Lock lock.Locker
|
||||
Oss *upload.Oss
|
||||
Jwt *jwt.Jwt
|
||||
Lock lock.Locker
|
||||
Localizer func(lang string) *i18n.Localizer
|
||||
)
|
||||
|
||||
53
global/i18n.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package global
|
||||
|
||||
import (
|
||||
"github.com/BurntSushi/toml"
|
||||
"github.com/nicksnyder/go-i18n/v2/i18n"
|
||||
"golang.org/x/text/language"
|
||||
"os"
|
||||
)
|
||||
|
||||
func InitI18n() {
|
||||
bundle := i18n.NewBundle(language.English)
|
||||
bundle.RegisterUnmarshalFunc("toml", toml.Unmarshal)
|
||||
//读取global.Config.Gin.ResourcesPath下的所有语言文件
|
||||
dir := Config.Gin.ResourcesPath + "/i18n"
|
||||
fileInfos, err := os.ReadDir(dir)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
return
|
||||
}
|
||||
for _, fileInfo := range fileInfos {
|
||||
//如果文件名不是.toml结尾
|
||||
if fileInfo.IsDir() || fileInfo.Name()[len(fileInfo.Name())-5:] != ".toml" {
|
||||
continue
|
||||
}
|
||||
bundle.LoadMessageFile(Config.Gin.ResourcesPath + "/i18n/" + fileInfo.Name())
|
||||
}
|
||||
Localizer = func(lang string) *i18n.Localizer {
|
||||
if lang == "" {
|
||||
lang = Config.Lang
|
||||
}
|
||||
if lang == "en" {
|
||||
return i18n.NewLocalizer(bundle, "en")
|
||||
} else {
|
||||
return i18n.NewLocalizer(bundle, lang, "en")
|
||||
}
|
||||
}
|
||||
|
||||
//personUnreadEmails := localizer.MustLocalize(&i18n.LocalizeConfig{
|
||||
// DefaultMessage: &i18n.Message{
|
||||
// ID: "PersonUnreadEmails",
|
||||
// },
|
||||
// PluralCount: 6,
|
||||
// TemplateData: map[string]interface{}{
|
||||
// "Name": "LE",
|
||||
// "PluralCount": 6,
|
||||
// },
|
||||
//})
|
||||
//personUnreadEmails, err := global.Localizer.LocalizeMessage(&i18n.Message{
|
||||
// ID: "ParamsError",
|
||||
//})
|
||||
//fmt.Println(err, personUnreadEmails)
|
||||
|
||||
}
|
||||
17
go.mod
@@ -3,6 +3,7 @@ module Gwen
|
||||
go 1.22
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v1.3.2
|
||||
github.com/antonfisher/nested-logrus-formatter v1.3.1
|
||||
github.com/fsnotify/fsnotify v1.5.1
|
||||
github.com/fvbock/endless v0.0.0-20170109170031-447134032cb6
|
||||
@@ -12,17 +13,22 @@ require (
|
||||
github.com/go-playground/validator/v10 v10.11.2
|
||||
github.com/go-redis/redis/v8 v8.11.4
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1
|
||||
github.com/google/uuid v1.1.2
|
||||
github.com/nicksnyder/go-i18n/v2 v2.4.0
|
||||
github.com/sirupsen/logrus v1.8.1
|
||||
github.com/spf13/viper v1.9.0
|
||||
github.com/swaggo/files v1.0.1
|
||||
github.com/swaggo/gin-swagger v1.6.0
|
||||
github.com/swaggo/swag v1.16.3
|
||||
golang.org/x/oauth2 v0.23.0
|
||||
golang.org/x/text v0.18.0
|
||||
gorm.io/driver/mysql v1.5.7
|
||||
gorm.io/driver/sqlite v1.5.6
|
||||
gorm.io/gorm v1.25.7
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go/compute/metadata v0.5.1 // indirect
|
||||
github.com/KyleBanks/depth v1.2.1 // indirect
|
||||
github.com/PuerkitoBio/purell v1.1.1 // indirect
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
|
||||
@@ -61,12 +67,11 @@ require (
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.9 // indirect
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
|
||||
golang.org/x/crypto v0.14.0 // indirect
|
||||
golang.org/x/net v0.17.0 // indirect
|
||||
golang.org/x/sys v0.13.0 // indirect
|
||||
golang.org/x/text v0.13.0 // indirect
|
||||
golang.org/x/tools v0.7.0 // indirect
|
||||
google.golang.org/protobuf v1.28.1 // indirect
|
||||
golang.org/x/crypto v0.23.0 // indirect
|
||||
golang.org/x/net v0.25.0 // indirect
|
||||
golang.org/x/sys v0.25.0 // indirect
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
|
||||
google.golang.org/protobuf v1.33.0 // indirect
|
||||
gopkg.in/ini.v1 v1.63.2 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"Gwen/global"
|
||||
"Gwen/http/request/admin"
|
||||
"Gwen/http/response"
|
||||
"Gwen/model"
|
||||
"Gwen/service"
|
||||
_ "encoding/json"
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -31,14 +32,14 @@ func (ct *AddressBook) Detail(c *gin.Context) {
|
||||
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.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
|
||||
return
|
||||
}
|
||||
if t.RowId > 0 {
|
||||
response.Success(c, t)
|
||||
return
|
||||
}
|
||||
response.Fail(c, 101, "信息不存在")
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -56,10 +57,10 @@ func (ct *AddressBook) Detail(c *gin.Context) {
|
||||
func (ct *AddressBook) Create(c *gin.Context) {
|
||||
f := &admin.AddressBookForm{}
|
||||
if err := c.ShouldBindJSON(f); err != nil {
|
||||
response.Fail(c, 101, "参数错误")
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||
return
|
||||
}
|
||||
errList := global.Validator.ValidStruct(f)
|
||||
errList := global.Validator.ValidStruct(c, f)
|
||||
if len(errList) > 0 {
|
||||
response.Fail(c, 101, errList[0])
|
||||
return
|
||||
@@ -69,12 +70,75 @@ func (ct *AddressBook) Create(c *gin.Context) {
|
||||
if !service.AllService.UserService.IsAdmin(u) || t.UserId == 0 {
|
||||
t.UserId = u.Id
|
||||
}
|
||||
err := service.AllService.AddressBookService.Create(t)
|
||||
if err != nil {
|
||||
response.Fail(c, 101, "创建失败")
|
||||
if t.CollectionId > 0 && !service.AllService.AddressBookService.CheckCollectionOwner(t.UserId, t.CollectionId) {
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||
return
|
||||
}
|
||||
response.Success(c, u)
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
// BatchCreate 批量创建地址簿
|
||||
// @Tags 地址簿
|
||||
// @Summary 批量创建地址簿
|
||||
// @Description 批量创建地址簿
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param body body admin.AddressBookForm true "地址簿信息"
|
||||
// @Success 200 {object} response.Response{data=model.AddressBook}
|
||||
// @Failure 500 {object} response.Response
|
||||
// @Router /admin/address_book/create [post]
|
||||
// @Security token
|
||||
func (ct *AddressBook) BatchCreate(c *gin.Context) {
|
||||
f := &admin.AddressBookForm{}
|
||||
if err := c.ShouldBindJSON(f); err != nil {
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||
return
|
||||
}
|
||||
errList := global.Validator.ValidStruct(c, f)
|
||||
if len(errList) > 0 {
|
||||
response.Fail(c, 101, errList[0])
|
||||
return
|
||||
}
|
||||
|
||||
//创建标签
|
||||
for _, fu := range f.UserIds {
|
||||
if fu == 0 {
|
||||
continue
|
||||
}
|
||||
for _, ft := range f.Tags {
|
||||
exTag := service.AllService.TagService.InfoByUserIdAndNameAndCollectionId(fu, ft, 0)
|
||||
if exTag.Id == 0 {
|
||||
service.AllService.TagService.Create(&model.Tag{
|
||||
UserId: fu,
|
||||
Name: ft,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
ts := f.ToAddressBooks()
|
||||
for _, t := range ts {
|
||||
if t.UserId == 0 {
|
||||
continue
|
||||
}
|
||||
ex := service.AllService.AddressBookService.InfoByUserIdAndId(t.UserId, t.Id)
|
||||
if ex.RowId == 0 {
|
||||
service.AllService.AddressBookService.Create(t)
|
||||
}
|
||||
}
|
||||
|
||||
response.Success(c, nil)
|
||||
}
|
||||
|
||||
// List 列表
|
||||
@@ -94,7 +158,7 @@ func (ct *AddressBook) Create(c *gin.Context) {
|
||||
func (ct *AddressBook) List(c *gin.Context) {
|
||||
query := &admin.AddressBookQuery{}
|
||||
if err := c.ShouldBindQuery(query); err != nil {
|
||||
response.Fail(c, 101, "参数错误")
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||
return
|
||||
}
|
||||
u := service.AllService.UserService.CurUser(c)
|
||||
@@ -102,10 +166,35 @@ func (ct *AddressBook) List(c *gin.Context) {
|
||||
query.UserId = int(u.Id)
|
||||
}
|
||||
res := service.AllService.AddressBookService.List(query.Page, query.PageSize, func(tx *gorm.DB) {
|
||||
tx.Preload("Collection", func(txc *gorm.DB) *gorm.DB {
|
||||
return txc.Select("id,name")
|
||||
})
|
||||
if query.Id != "" {
|
||||
tx.Where("id like ?", "%"+query.Id+"%")
|
||||
}
|
||||
if query.UserId > 0 {
|
||||
tx.Where("user_id = ?", query.UserId)
|
||||
}
|
||||
if query.Username != "" {
|
||||
tx.Where("username like ?", "%"+query.Username+"%")
|
||||
}
|
||||
if query.Hostname != "" {
|
||||
tx.Where("hostname like ?", "%"+query.Hostname+"%")
|
||||
}
|
||||
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)
|
||||
}
|
||||
//获取地址簿名称
|
||||
//cRes := service.AllService.AddressBookService.ListCollection(1, 999, func(tx *gorm.DB) {
|
||||
// tx.Where("id in ?", abCIds)
|
||||
//})
|
||||
//
|
||||
response.Success(c, res)
|
||||
}
|
||||
|
||||
@@ -123,27 +212,31 @@ func (ct *AddressBook) List(c *gin.Context) {
|
||||
func (ct *AddressBook) Update(c *gin.Context) {
|
||||
f := &admin.AddressBookForm{}
|
||||
if err := c.ShouldBindJSON(f); err != nil {
|
||||
response.Fail(c, 101, "参数错误")
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||
return
|
||||
}
|
||||
errList := global.Validator.ValidStruct(f)
|
||||
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.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||
return
|
||||
}
|
||||
t := f.ToAddressBook()
|
||||
u := service.AllService.UserService.CurUser(c)
|
||||
if !service.AllService.UserService.IsAdmin(u) && t.UserId != u.Id {
|
||||
response.Fail(c, 101, "无权限")
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
|
||||
return
|
||||
}
|
||||
err := service.AllService.AddressBookService.Update(t)
|
||||
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.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||
return
|
||||
}
|
||||
response.Success(c, nil)
|
||||
@@ -163,19 +256,23 @@ func (ct *AddressBook) Update(c *gin.Context) {
|
||||
func (ct *AddressBook) Delete(c *gin.Context) {
|
||||
f := &admin.AddressBookForm{}
|
||||
if err := c.ShouldBindJSON(f); err != nil {
|
||||
response.Fail(c, 101, "系统错误")
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||
return
|
||||
}
|
||||
id := f.RowId
|
||||
errList := global.Validator.ValidVar(id, "required,gt=0")
|
||||
errList := global.Validator.ValidVar(c, id, "required,gt=0")
|
||||
if len(errList) > 0 {
|
||||
response.Fail(c, 101, errList[0])
|
||||
return
|
||||
}
|
||||
t := service.AllService.AddressBookService.InfoByRowId(f.RowId)
|
||||
if t.RowId == 0 {
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||
return
|
||||
}
|
||||
u := service.AllService.UserService.CurUser(c)
|
||||
if !service.AllService.UserService.IsAdmin(u) && t.UserId != u.Id {
|
||||
response.Fail(c, 101, "无权限")
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
|
||||
return
|
||||
}
|
||||
if u.Id > 0 {
|
||||
@@ -184,8 +281,49 @@ func (ct *AddressBook) Delete(c *gin.Context) {
|
||||
response.Success(c, nil)
|
||||
return
|
||||
}
|
||||
response.Fail(c, 101, err.Error())
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||
return
|
||||
}
|
||||
response.Fail(c, 101, "信息不存在")
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||
}
|
||||
|
||||
// ShareByWebClient
|
||||
// @Tags 地址簿
|
||||
// @Summary 地址簿分享
|
||||
// @Description 地址簿分享
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param body body admin.ShareByWebClientForm true "地址簿信息"
|
||||
// @Success 200 {object} response.Response
|
||||
// @Failure 500 {object} response.Response
|
||||
// @Router /admin/address_book/share [post]
|
||||
// @Security token
|
||||
func (ct *AddressBook) ShareByWebClient(c *gin.Context) {
|
||||
f := &admin.ShareByWebClientForm{}
|
||||
if err := c.ShouldBindJSON(f); err != nil {
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||
return
|
||||
}
|
||||
errList := global.Validator.ValidStruct(c, f)
|
||||
if len(errList) > 0 {
|
||||
response.Fail(c, 101, errList[0])
|
||||
return
|
||||
}
|
||||
|
||||
u := service.AllService.UserService.CurUser(c)
|
||||
ab := service.AllService.AddressBookService.InfoByUserIdAndId(u.Id, f.Id)
|
||||
if ab.RowId == 0 {
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||
return
|
||||
}
|
||||
m := f.ToShareRecord()
|
||||
m.UserId = u.Id
|
||||
err := service.AllService.AddressBookService.ShareByWebClient(m)
|
||||
if err != nil {
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||
return
|
||||
}
|
||||
response.Success(c, &gin.H{
|
||||
"share_token": m.ShareToken,
|
||||
})
|
||||
}
|
||||
|
||||
192
http/controller/admin/addressBookCollection.go
Normal file
@@ -0,0 +1,192 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"Gwen/global"
|
||||
"Gwen/http/request/admin"
|
||||
"Gwen/http/response"
|
||||
"Gwen/model"
|
||||
"Gwen/service"
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type AddressBookCollection struct {
|
||||
}
|
||||
|
||||
// Detail 地址簿集合
|
||||
// @AddressBookCollections 地址簿集合
|
||||
// @Summary 地址簿集合详情
|
||||
// @Description 地址簿集合详情
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path int true "ID"
|
||||
// @Success 200 {object} response.Response{data=model.AddressBookCollection}
|
||||
// @Failure 500 {object} response.Response
|
||||
// @Router /admin/address_book_collection/detail/{id} [get]
|
||||
// @Security token
|
||||
func (abc *AddressBookCollection) Detail(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
iid, _ := strconv.Atoi(id)
|
||||
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 {
|
||||
response.Success(c, t)
|
||||
return
|
||||
}
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||
return
|
||||
}
|
||||
|
||||
// Create 创建地址簿集合
|
||||
// @AddressBookCollections 地址簿集合
|
||||
// @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/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
|
||||
}
|
||||
//t := f.ToAddressBookCollection()
|
||||
t := f
|
||||
u := service.AllService.UserService.CurUser(c)
|
||||
if !service.AllService.UserService.IsAdmin(u) || t.UserId == 0 {
|
||||
t.UserId = u.Id
|
||||
}
|
||||
err := service.AllService.AddressBookService.CreateCollection(t)
|
||||
if err != nil {
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||
return
|
||||
}
|
||||
response.Success(c, nil)
|
||||
}
|
||||
|
||||
// List 列表
|
||||
// @AddressBookCollections 地址簿集合
|
||||
// @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.AddressBookCollectionList}
|
||||
// @Failure 500 {object} response.Response
|
||||
// @Router /admin/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)
|
||||
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) {
|
||||
if query.UserId > 0 {
|
||||
tx.Where("user_id = ?", query.UserId)
|
||||
}
|
||||
})
|
||||
response.Success(c, res)
|
||||
}
|
||||
|
||||
// Update 编辑
|
||||
// @AddressBookCollections 地址簿集合
|
||||
// @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/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
|
||||
}
|
||||
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)
|
||||
if err != nil {
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||
return
|
||||
}
|
||||
response.Success(c, nil)
|
||||
}
|
||||
|
||||
// Delete 删除
|
||||
// @AddressBookCollections 地址簿集合
|
||||
// @Summary 地址簿集合删除
|
||||
// @Description 地址簿集合删除
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param body body model.AddressBookCollection true "地址簿集合信息"
|
||||
// @Success 200 {object} response.Response
|
||||
// @Failure 500 {object} response.Response
|
||||
// @Router /admin/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
|
||||
}
|
||||
t := service.AllService.AddressBookService.CollectionInfoById(f.Id)
|
||||
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 u.Id > 0 {
|
||||
err := service.AllService.AddressBookService.DeleteCollection(t)
|
||||
if err == nil {
|
||||
response.Success(c, nil)
|
||||
return
|
||||
}
|
||||
response.Fail(c, 101, err.Error())
|
||||
return
|
||||
}
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||
}
|
||||
251
http/controller/admin/addressBookCollectionRule.go
Normal file
@@ -0,0 +1,251 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"Gwen/global"
|
||||
"Gwen/http/request/admin"
|
||||
"Gwen/http/response"
|
||||
"Gwen/model"
|
||||
"Gwen/service"
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type AddressBookCollectionRule struct {
|
||||
}
|
||||
|
||||
// List 列表
|
||||
// @AddressBookCollectionRule 地址簿集合规则
|
||||
// @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/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)
|
||||
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) {
|
||||
if query.UserId > 0 {
|
||||
tx.Where("user_id = ?", query.UserId)
|
||||
}
|
||||
if query.CollectionId > 0 {
|
||||
tx.Where("collection_id = ?", query.CollectionId)
|
||||
}
|
||||
})
|
||||
response.Success(c, res)
|
||||
}
|
||||
|
||||
// Detail 地址簿集合规则
|
||||
// @AddressBookCollectionRule 地址簿集合规则
|
||||
// @Summary 地址簿集合规则详情
|
||||
// @Description 地址簿集合规则详情
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path int true "ID"
|
||||
// @Success 200 {object} response.Response{data=model.AddressBookCollectionRule}
|
||||
// @Failure 500 {object} response.Response
|
||||
// @Router /admin/address_book_collection_rule/detail/{id} [get]
|
||||
// @Security token
|
||||
func (abcr *AddressBookCollectionRule) Detail(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
iid, _ := strconv.Atoi(id)
|
||||
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 {
|
||||
response.Success(c, t)
|
||||
return
|
||||
}
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||
return
|
||||
}
|
||||
|
||||
// Create 创建地址簿集合规则
|
||||
// @AddressBookCollectionRule 地址簿集合规则
|
||||
// @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/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)
|
||||
if t.UserId == 0 {
|
||||
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 !service.AllService.UserService.IsAdmin(u) && 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 && !service.AllService.UserService.IsAdmin(u) {
|
||||
return "NoAccess", false
|
||||
}
|
||||
} 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)
|
||||
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 编辑
|
||||
// @AddressBookCollectionRule 地址簿集合规则
|
||||
// @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/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
|
||||
}
|
||||
t := f //f.ToAddressBookCollection()
|
||||
u := service.AllService.UserService.CurUser(c)
|
||||
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 删除
|
||||
// @AddressBookCollectionRule 地址簿集合规则
|
||||
// @Summary 地址簿集合规则删除
|
||||
// @Description 地址簿集合规则删除
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param body body model.AddressBookCollectionRule true "地址簿集合规则信息"
|
||||
// @Success 200 {object} response.Response
|
||||
// @Failure 500 {object} response.Response
|
||||
// @Router /admin/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
|
||||
}
|
||||
t := service.AllService.AddressBookService.RuleInfoById(f.Id)
|
||||
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 {
|
||||
err := service.AllService.AddressBookService.DeleteRule(t)
|
||||
if err == nil {
|
||||
response.Success(c, nil)
|
||||
return
|
||||
}
|
||||
response.Fail(c, 101, err.Error())
|
||||
return
|
||||
}
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||
}
|
||||
150
http/controller/admin/audit.go
Normal file
@@ -0,0 +1,150 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"Gwen/global"
|
||||
"Gwen/http/request/admin"
|
||||
"Gwen/http/response"
|
||||
"Gwen/model"
|
||||
"Gwen/service"
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type Audit struct {
|
||||
}
|
||||
|
||||
// ConnList 列表
|
||||
// @Tags 链接日志
|
||||
// @Summary 链接日志列表
|
||||
// @Description 链接日志列表
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param page query int false "页码"
|
||||
// @Param page_size query int false "页大小"
|
||||
// @Param peer_id query int false "目标设备"
|
||||
// @Param from_peer query int false "来源设备"
|
||||
// @Success 200 {object} response.Response{data=model.AuditConnList}
|
||||
// @Failure 500 {object} response.Response
|
||||
// @Router /admin/audit_conn/list [get]
|
||||
// @Security token
|
||||
func (a *Audit) ConnList(c *gin.Context) {
|
||||
query := &admin.AuditQuery{}
|
||||
if err := c.ShouldBindQuery(query); err != nil {
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||
return
|
||||
}
|
||||
res := service.AllService.AuditService.AuditConnList(query.Page, query.PageSize, func(tx *gorm.DB) {
|
||||
if query.PeerId != "" {
|
||||
tx.Where("peer_id like ?", "%"+query.PeerId+"%")
|
||||
}
|
||||
if query.FromPeer != "" {
|
||||
tx.Where("from_peer like ?", "%"+query.FromPeer+"%")
|
||||
}
|
||||
tx.Order("id desc")
|
||||
})
|
||||
response.Success(c, res)
|
||||
}
|
||||
|
||||
// ConnDelete 删除
|
||||
// @Tags 链接日志
|
||||
// @Summary 链接日志删除
|
||||
// @Description 链接日志删除
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param body body model.AuditConn true "链接日志信息"
|
||||
// @Success 200 {object} response.Response
|
||||
// @Failure 500 {object} response.Response
|
||||
// @Router /admin/audit_conn/delete [post]
|
||||
// @Security token
|
||||
func (a *Audit) ConnDelete(c *gin.Context) {
|
||||
f := &model.AuditConn{}
|
||||
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.AuditService.ConnInfoById(f.Id)
|
||||
if l.Id > 0 {
|
||||
err := service.AllService.AuditService.DeleteAuditConn(l)
|
||||
if err == nil {
|
||||
response.Success(c, nil)
|
||||
return
|
||||
}
|
||||
response.Fail(c, 101, err.Error())
|
||||
return
|
||||
}
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||
}
|
||||
|
||||
// FileList 列表
|
||||
// @Tags 文件日志
|
||||
// @Summary 文件日志列表
|
||||
// @Description 文件日志列表
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param page query int false "页码"
|
||||
// @Param page_size query int false "页大小"
|
||||
// @Param peer_id query int false "目标设备"
|
||||
// @Param from_peer query int false "来源设备"
|
||||
// @Success 200 {object} response.Response{data=model.AuditFileList}
|
||||
// @Failure 500 {object} response.Response
|
||||
// @Router /admin/audit_conn/list [get]
|
||||
// @Security token
|
||||
func (a *Audit) FileList(c *gin.Context) {
|
||||
query := &admin.AuditQuery{}
|
||||
if err := c.ShouldBindQuery(query); err != nil {
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||
return
|
||||
}
|
||||
res := service.AllService.AuditService.AuditFileList(query.Page, query.PageSize, func(tx *gorm.DB) {
|
||||
if query.PeerId != "" {
|
||||
tx.Where("peer_id like ?", "%"+query.PeerId+"%")
|
||||
}
|
||||
if query.FromPeer != "" {
|
||||
tx.Where("from_peer like ?", "%"+query.FromPeer+"%")
|
||||
}
|
||||
tx.Order("id desc")
|
||||
})
|
||||
response.Success(c, res)
|
||||
}
|
||||
|
||||
// FileDelete 删除
|
||||
// @Tags 文件日志
|
||||
// @Summary 文件日志删除
|
||||
// @Description 文件日志删除
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param body body model.AuditFile true "文件日志信息"
|
||||
// @Success 200 {object} response.Response
|
||||
// @Failure 500 {object} response.Response
|
||||
// @Router /admin/audit_conn/delete [post]
|
||||
// @Security token
|
||||
func (a *Audit) FileDelete(c *gin.Context) {
|
||||
f := &model.AuditFile{}
|
||||
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.AuditService.FileInfoById(f.Id)
|
||||
if l.Id > 0 {
|
||||
err := service.AllService.AuditService.DeleteAuditFile(l)
|
||||
if err == nil {
|
||||
response.Success(c, nil)
|
||||
return
|
||||
}
|
||||
response.Fail(c, 101, err.Error())
|
||||
return
|
||||
}
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||
}
|
||||
@@ -31,7 +31,7 @@ func (ct *Group) Detail(c *gin.Context) {
|
||||
response.Success(c, u)
|
||||
return
|
||||
}
|
||||
response.Fail(c, 101, "信息不存在")
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -49,10 +49,10 @@ func (ct *Group) Detail(c *gin.Context) {
|
||||
func (ct *Group) Create(c *gin.Context) {
|
||||
f := &admin.GroupForm{}
|
||||
if err := c.ShouldBindJSON(f); err != nil {
|
||||
response.Fail(c, 101, "参数错误")
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||
return
|
||||
}
|
||||
errList := global.Validator.ValidStruct(f)
|
||||
errList := global.Validator.ValidStruct(c, f)
|
||||
if len(errList) > 0 {
|
||||
response.Fail(c, 101, errList[0])
|
||||
return
|
||||
@@ -60,10 +60,10 @@ func (ct *Group) Create(c *gin.Context) {
|
||||
u := f.ToGroup()
|
||||
err := service.AllService.GroupService.Create(u)
|
||||
if err != nil {
|
||||
response.Fail(c, 101, "创建失败")
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||
return
|
||||
}
|
||||
response.Success(c, u)
|
||||
response.Success(c, nil)
|
||||
}
|
||||
|
||||
// List 列表
|
||||
@@ -81,7 +81,7 @@ func (ct *Group) Create(c *gin.Context) {
|
||||
func (ct *Group) List(c *gin.Context) {
|
||||
query := &admin.PageQuery{}
|
||||
if err := c.ShouldBindQuery(query); err != nil {
|
||||
response.Fail(c, 101, "参数错误")
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||
return
|
||||
}
|
||||
res := service.AllService.GroupService.List(query.Page, query.PageSize, nil)
|
||||
@@ -102,14 +102,14 @@ func (ct *Group) List(c *gin.Context) {
|
||||
func (ct *Group) Update(c *gin.Context) {
|
||||
f := &admin.GroupForm{}
|
||||
if err := c.ShouldBindJSON(f); err != nil {
|
||||
response.Fail(c, 101, "参数错误")
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||
return
|
||||
}
|
||||
if f.Id == 0 {
|
||||
response.Fail(c, 101, "参数错误")
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||
return
|
||||
}
|
||||
errList := global.Validator.ValidStruct(f)
|
||||
errList := global.Validator.ValidStruct(c, f)
|
||||
if len(errList) > 0 {
|
||||
response.Fail(c, 101, errList[0])
|
||||
return
|
||||
@@ -117,7 +117,7 @@ func (ct *Group) Update(c *gin.Context) {
|
||||
u := f.ToGroup()
|
||||
err := service.AllService.GroupService.Update(u)
|
||||
if err != nil {
|
||||
response.Fail(c, 101, "更新失败")
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||
return
|
||||
}
|
||||
response.Success(c, nil)
|
||||
@@ -137,11 +137,11 @@ func (ct *Group) Update(c *gin.Context) {
|
||||
func (ct *Group) Delete(c *gin.Context) {
|
||||
f := &admin.GroupForm{}
|
||||
if err := c.ShouldBindJSON(f); err != nil {
|
||||
response.Fail(c, 101, "系统错误")
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||
return
|
||||
}
|
||||
id := f.Id
|
||||
errList := global.Validator.ValidVar(id, "required,gt=0")
|
||||
errList := global.Validator.ValidVar(c, id, "required,gt=0")
|
||||
if len(errList) > 0 {
|
||||
response.Fail(c, 101, errList[0])
|
||||
return
|
||||
@@ -153,8 +153,8 @@ func (ct *Group) Delete(c *gin.Context) {
|
||||
response.Success(c, nil)
|
||||
return
|
||||
}
|
||||
response.Fail(c, 101, err.Error())
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||
return
|
||||
}
|
||||
response.Fail(c, 101, "信息不存在")
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||
}
|
||||
|
||||
@@ -2,12 +2,16 @@ package admin
|
||||
|
||||
import (
|
||||
"Gwen/global"
|
||||
"Gwen/http/controller/api"
|
||||
"Gwen/http/request/admin"
|
||||
apiReq "Gwen/http/request/api"
|
||||
"Gwen/http/response"
|
||||
adResp "Gwen/http/response/admin"
|
||||
"Gwen/model"
|
||||
"Gwen/service"
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type Login struct {
|
||||
@@ -25,27 +29,36 @@ type Login struct {
|
||||
// @Router /admin/login [post]
|
||||
// @Security token
|
||||
func (ct *Login) Login(c *gin.Context) {
|
||||
fmt.Println("login")
|
||||
f := &admin.Login{}
|
||||
err := c.ShouldBindJSON(f)
|
||||
if err != nil {
|
||||
response.Fail(c, 101, "参数错误")
|
||||
global.Logger.Warn(fmt.Sprintf("Login Fail: %s %s %s", "ParamsError", c.RemoteIP(), c.ClientIP()))
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
errList := global.Validator.ValidStruct(f)
|
||||
errList := global.Validator.ValidStruct(c, f)
|
||||
if len(errList) > 0 {
|
||||
global.Logger.Warn(fmt.Sprintf("Login Fail: %s %s %s", "ParamsError", c.RemoteIP(), c.ClientIP()))
|
||||
response.Fail(c, 101, errList[0])
|
||||
return
|
||||
}
|
||||
u := service.AllService.UserService.InfoByUsernamePassword(f.Username, f.Password)
|
||||
|
||||
if u.Id == 0 {
|
||||
response.Fail(c, 101, "用户名或密码错误")
|
||||
global.Logger.Warn(fmt.Sprintf("Login Fail: %s %s %s", "UsernameOrPasswordError", c.RemoteIP(), c.ClientIP()))
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "UsernameOrPasswordError"))
|
||||
return
|
||||
}
|
||||
|
||||
ut := service.AllService.UserService.Login(u)
|
||||
ut := service.AllService.UserService.Login(u, &model.LoginLog{
|
||||
UserId: u.Id,
|
||||
Client: "webadmin",
|
||||
Uuid: "", //must be empty
|
||||
Ip: c.ClientIP(),
|
||||
Type: "account",
|
||||
Platform: f.Platform,
|
||||
})
|
||||
|
||||
response.Success(c, &adResp.LoginPayload{
|
||||
Token: ut.Token,
|
||||
@@ -72,3 +85,87 @@ func (ct *Login) Logout(c *gin.Context) {
|
||||
}
|
||||
response.Success(c, nil)
|
||||
}
|
||||
|
||||
// LoginOptions
|
||||
// @Tags 登录
|
||||
// @Summary 登录选项
|
||||
// @Description 登录选项
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} []string
|
||||
// @Failure 500 {object} response.ErrorResponse
|
||||
// @Router /admin/login-options [post]
|
||||
func (ct *Login) LoginOptions(c *gin.Context) {
|
||||
res := service.AllService.OauthService.List(1, 100, func(tx *gorm.DB) {
|
||||
tx.Select("op").Order("id")
|
||||
})
|
||||
var ops []string
|
||||
for _, v := range res.Oauths {
|
||||
ops = append(ops, v.Op)
|
||||
}
|
||||
response.Success(c, ops)
|
||||
}
|
||||
|
||||
// OidcAuth
|
||||
// @Tags Oauth
|
||||
// @Summary OidcAuth
|
||||
// @Description OidcAuth
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Router /admin/oidc/auth [post]
|
||||
func (ct *Login) OidcAuth(c *gin.Context) {
|
||||
// o := &api.Oauth{}
|
||||
// o.OidcAuth(c)
|
||||
f := &apiReq.OidcAuthRequest{}
|
||||
err := c.ShouldBindJSON(f)
|
||||
if err != nil {
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
err, code, url := service.AllService.OauthService.BeginAuth(f.Op)
|
||||
if err != nil {
|
||||
response.Error(c, response.TranslateMsg(c, err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
service.AllService.OauthService.SetOauthCache(code, &service.OauthCacheItem{
|
||||
Action: service.OauthActionTypeLogin,
|
||||
Op: f.Op,
|
||||
Id: f.Id,
|
||||
DeviceType: "webadmin",
|
||||
// DeviceOs: ct.Platform(c),
|
||||
DeviceOs: f.DeviceInfo.Os,
|
||||
Uuid: f.Uuid,
|
||||
}, 5*60)
|
||||
|
||||
response.Success(c, gin.H{
|
||||
"code": code,
|
||||
"url": url,
|
||||
})
|
||||
}
|
||||
|
||||
// OidcAuthQuery
|
||||
// @Tags Oauth
|
||||
// @Summary OidcAuthQuery
|
||||
// @Description OidcAuthQuery
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} response.Response{data=adResp.LoginPayload}
|
||||
// @Failure 500 {object} response.Response
|
||||
// @Router /admin/oidc/auth-query [get]
|
||||
func (ct *Login) OidcAuthQuery(c *gin.Context) {
|
||||
o := &api.Oauth{}
|
||||
u, ut := o.OidcAuthQueryPre(c)
|
||||
if ut == nil {
|
||||
return
|
||||
}
|
||||
//fmt.Println("u:", u)
|
||||
//fmt.Println("ut:", ut)
|
||||
response.Success(c, &adResp.LoginPayload{
|
||||
Token: ut.Token,
|
||||
Username: u.Username,
|
||||
RouteNames: service.AllService.UserService.RouteNames(u),
|
||||
Nickname: u.Nickname,
|
||||
})
|
||||
}
|
||||
|
||||
111
http/controller/admin/loginLog.go
Normal file
@@ -0,0 +1,111 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"Gwen/global"
|
||||
"Gwen/http/request/admin"
|
||||
"Gwen/http/response"
|
||||
"Gwen/model"
|
||||
"Gwen/service"
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type LoginLog struct {
|
||||
}
|
||||
|
||||
// Detail 登录日志
|
||||
// @Tags 登录日志
|
||||
// @Summary 登录日志详情
|
||||
// @Description 登录日志详情
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path int true "ID"
|
||||
// @Success 200 {object} response.Response{data=model.LoginLog}
|
||||
// @Failure 500 {object} response.Response
|
||||
// @Router /admin/loginLog/detail/{id} [get]
|
||||
// @Security token
|
||||
func (ct *LoginLog) Detail(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
iid, _ := strconv.Atoi(id)
|
||||
u := service.AllService.LoginLogService.InfoById(uint(iid))
|
||||
if u.Id > 0 {
|
||||
response.Success(c, u)
|
||||
return
|
||||
}
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||
return
|
||||
}
|
||||
|
||||
// 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/loginLog/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)
|
||||
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) {
|
||||
if query.UserId > 0 {
|
||||
tx.Where("user_id = ?", query.UserId)
|
||||
}
|
||||
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/loginLog/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)
|
||||
u := service.AllService.UserService.CurUser(c)
|
||||
if !service.AllService.UserService.IsAdmin(u) && l.UserId != u.Id {
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
|
||||
return
|
||||
}
|
||||
if l.Id > 0 {
|
||||
err := service.AllService.LoginLogService.Delete(l)
|
||||
if err == nil {
|
||||
response.Success(c, nil)
|
||||
return
|
||||
}
|
||||
response.Fail(c, 101, err.Error())
|
||||
return
|
||||
}
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||
}
|
||||
306
http/controller/admin/oauth.go
Normal file
@@ -0,0 +1,306 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"Gwen/global"
|
||||
"Gwen/http/request/admin"
|
||||
adminReq "Gwen/http/request/admin"
|
||||
"Gwen/http/response"
|
||||
"Gwen/model"
|
||||
"Gwen/service"
|
||||
"github.com/gin-gonic/gin"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type Oauth struct {
|
||||
}
|
||||
|
||||
// Info
|
||||
func (o *Oauth) Info(c *gin.Context) {
|
||||
code := c.Query("code")
|
||||
if code == "" {
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||
return
|
||||
}
|
||||
v := service.AllService.OauthService.GetOauthCache(code)
|
||||
if v == nil {
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||
return
|
||||
}
|
||||
response.Success(c, v)
|
||||
}
|
||||
|
||||
func (o *Oauth) ToBind(c *gin.Context) {
|
||||
f := &adminReq.BindOauthForm{}
|
||||
err := c.ShouldBindJSON(f)
|
||||
if err != nil {
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||
return
|
||||
}
|
||||
u := service.AllService.UserService.CurUser(c)
|
||||
|
||||
utr := service.AllService.UserService.UserThirdInfo(u.Id, f.Op)
|
||||
if utr.Id > 0 {
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "OauthHasBindOtherUser"))
|
||||
return
|
||||
}
|
||||
|
||||
err, code, url := service.AllService.OauthService.BeginAuth(f.Op)
|
||||
if err != nil {
|
||||
response.Error(c, response.TranslateMsg(c, err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
service.AllService.OauthService.SetOauthCache(code, &service.OauthCacheItem{
|
||||
Action: service.OauthActionTypeBind,
|
||||
Op: f.Op,
|
||||
UserId: u.Id,
|
||||
}, 5*60)
|
||||
|
||||
response.Success(c, gin.H{
|
||||
"code": code,
|
||||
"url": url,
|
||||
})
|
||||
}
|
||||
|
||||
// Confirm 确认授权登录
|
||||
func (o *Oauth) Confirm(c *gin.Context) {
|
||||
j := &adminReq.OauthConfirmForm{}
|
||||
err := c.ShouldBindJSON(j)
|
||||
if err != nil {
|
||||
response.Fail(c, 101, "参数错误"+err.Error())
|
||||
return
|
||||
}
|
||||
if j.Code == "" {
|
||||
response.Fail(c, 101, "参数错误: code 不存在")
|
||||
return
|
||||
}
|
||||
v := service.AllService.OauthService.GetOauthCache(j.Code)
|
||||
if v == nil {
|
||||
response.Fail(c, 101, "授权已过期")
|
||||
return
|
||||
}
|
||||
u := service.AllService.UserService.CurUser(c)
|
||||
v.UserId = u.Id
|
||||
service.AllService.OauthService.SetOauthCache(j.Code, v, 0)
|
||||
response.Success(c, v)
|
||||
}
|
||||
|
||||
func (o *Oauth) BindConfirm(c *gin.Context) {
|
||||
j := &adminReq.OauthConfirmForm{}
|
||||
err := c.ShouldBindJSON(j)
|
||||
if err != nil {
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||
return
|
||||
}
|
||||
if j.Code == "" {
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||
return
|
||||
}
|
||||
v := service.AllService.OauthService.GetOauthCache(j.Code)
|
||||
if v == nil {
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "OauthExpired"))
|
||||
return
|
||||
}
|
||||
u := service.AllService.UserService.CurUser(c)
|
||||
err = service.AllService.OauthService.BindOauthUser(v.Op, v.ThirdOpenId, v.ThirdName, u.Id)
|
||||
if err != nil {
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "BindFail"))
|
||||
return
|
||||
}
|
||||
|
||||
v.UserId = u.Id
|
||||
service.AllService.OauthService.SetOauthCache(j.Code, v, 0)
|
||||
response.Success(c, v)
|
||||
}
|
||||
|
||||
func (o *Oauth) Unbind(c *gin.Context) {
|
||||
f := &adminReq.UnBindOauthForm{}
|
||||
err := c.ShouldBindJSON(f)
|
||||
if err != nil {
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||
return
|
||||
}
|
||||
u := service.AllService.UserService.CurUser(c)
|
||||
utr := service.AllService.UserService.UserThirdInfo(u.Id, f.Op)
|
||||
if utr.Id == 0 {
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||
return
|
||||
}
|
||||
if f.Op == model.OauthTypeGithub {
|
||||
err = service.AllService.OauthService.UnBindGithubUser(u.Id)
|
||||
if err != nil {
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
if f.Op == model.OauthTypeGoogle {
|
||||
err = service.AllService.OauthService.UnBindGoogleUser(u.Id)
|
||||
if err != nil {
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
if f.Op == model.OauthTypeOidc {
|
||||
err = service.AllService.OauthService.UnBindOidcUser(u.Id)
|
||||
if err != nil {
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
response.Success(c, nil)
|
||||
}
|
||||
|
||||
// Detail Oauth
|
||||
// @Tags Oauth
|
||||
// @Summary Oauth详情
|
||||
// @Description Oauth详情
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path int true "ID"
|
||||
// @Success 200 {object} response.Response{data=model.Oauth}
|
||||
// @Failure 500 {object} response.Response
|
||||
// @Router /admin/oauth/detail/{id} [get]
|
||||
// @Security token
|
||||
func (o *Oauth) Detail(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
iid, _ := strconv.Atoi(id)
|
||||
u := service.AllService.OauthService.InfoById(uint(iid))
|
||||
if u.Id > 0 {
|
||||
response.Success(c, u)
|
||||
return
|
||||
}
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||
return
|
||||
}
|
||||
|
||||
// Create 创建Oauth
|
||||
// @Tags Oauth
|
||||
// @Summary 创建Oauth
|
||||
// @Description 创建Oauth
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param body body admin.OauthForm true "Oauth信息"
|
||||
// @Success 200 {object} response.Response{data=model.Oauth}
|
||||
// @Failure 500 {object} response.Response
|
||||
// @Router /admin/oauth/create [post]
|
||||
// @Security token
|
||||
func (o *Oauth) Create(c *gin.Context) {
|
||||
f := &admin.OauthForm{}
|
||||
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.OauthService.InfoByOp(f.Op)
|
||||
if ex.Id > 0 {
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ItemExists"))
|
||||
return
|
||||
}
|
||||
|
||||
u := f.ToOauth()
|
||||
err := service.AllService.OauthService.Create(u)
|
||||
if err != nil {
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||
return
|
||||
}
|
||||
response.Success(c, nil)
|
||||
}
|
||||
|
||||
// List 列表
|
||||
// @Tags Oauth
|
||||
// @Summary Oauth列表
|
||||
// @Description Oauth列表
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param page query int false "页码"
|
||||
// @Param page_size query int false "页大小"
|
||||
// @Success 200 {object} response.Response{data=model.OauthList}
|
||||
// @Failure 500 {object} response.Response
|
||||
// @Router /admin/oauth/list [get]
|
||||
// @Security token
|
||||
func (o *Oauth) List(c *gin.Context) {
|
||||
query := &admin.PageQuery{}
|
||||
if err := c.ShouldBindQuery(query); err != nil {
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||
return
|
||||
}
|
||||
res := service.AllService.OauthService.List(query.Page, query.PageSize, nil)
|
||||
response.Success(c, res)
|
||||
}
|
||||
|
||||
// Update 编辑
|
||||
// @Tags Oauth
|
||||
// @Summary Oauth编辑
|
||||
// @Description Oauth编辑
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param body body admin.OauthForm true "Oauth信息"
|
||||
// @Success 200 {object} response.Response{data=model.OauthList}
|
||||
// @Failure 500 {object} response.Response
|
||||
// @Router /admin/oauth/update [post]
|
||||
// @Security token
|
||||
func (o *Oauth) Update(c *gin.Context) {
|
||||
f := &admin.OauthForm{}
|
||||
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
|
||||
}
|
||||
errList := global.Validator.ValidStruct(c, f)
|
||||
if len(errList) > 0 {
|
||||
response.Fail(c, 101, errList[0])
|
||||
return
|
||||
}
|
||||
u := f.ToOauth()
|
||||
err := service.AllService.OauthService.Update(u)
|
||||
if err != nil {
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||
return
|
||||
}
|
||||
response.Success(c, nil)
|
||||
}
|
||||
|
||||
// Delete 删除
|
||||
// @Tags Oauth
|
||||
// @Summary Oauth删除
|
||||
// @Description Oauth删除
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param body body admin.OauthForm true "Oauth信息"
|
||||
// @Success 200 {object} response.Response
|
||||
// @Failure 500 {object} response.Response
|
||||
// @Router /admin/oauth/delete [post]
|
||||
// @Security token
|
||||
func (o *Oauth) Delete(c *gin.Context) {
|
||||
f := &admin.OauthForm{}
|
||||
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
|
||||
}
|
||||
u := service.AllService.OauthService.InfoById(f.Id)
|
||||
if u.Id > 0 {
|
||||
err := service.AllService.OauthService.Delete(u)
|
||||
if err == nil {
|
||||
response.Success(c, nil)
|
||||
return
|
||||
}
|
||||
response.Fail(c, 101, err.Error())
|
||||
return
|
||||
}
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||
}
|
||||
@@ -6,16 +6,18 @@ import (
|
||||
"Gwen/http/response"
|
||||
"Gwen/service"
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Peer struct {
|
||||
}
|
||||
|
||||
// Detail 机器
|
||||
// @Tags 机器
|
||||
// @Summary 机器详情
|
||||
// @Description 机器详情
|
||||
// Detail 设备
|
||||
// @Tags 设备
|
||||
// @Summary 设备详情
|
||||
// @Description 设备详情
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path int true "ID"
|
||||
@@ -31,17 +33,17 @@ func (ct *Peer) Detail(c *gin.Context) {
|
||||
response.Success(c, u)
|
||||
return
|
||||
}
|
||||
response.Fail(c, 101, "信息不存在")
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||
return
|
||||
}
|
||||
|
||||
// Create 创建机器
|
||||
// @Tags 机器
|
||||
// @Summary 创建机器
|
||||
// @Description 创建机器
|
||||
// Create 创建设备
|
||||
// @Tags 设备
|
||||
// @Summary 创建设备
|
||||
// @Description 创建设备
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param body body admin.PeerForm true "机器信息"
|
||||
// @Param body body admin.PeerForm true "设备信息"
|
||||
// @Success 200 {object} response.Response{data=model.Peer}
|
||||
// @Failure 500 {object} response.Response
|
||||
// @Router /admin/peer/create [post]
|
||||
@@ -49,52 +51,70 @@ func (ct *Peer) Detail(c *gin.Context) {
|
||||
func (ct *Peer) Create(c *gin.Context) {
|
||||
f := &admin.PeerForm{}
|
||||
if err := c.ShouldBindJSON(f); err != nil {
|
||||
response.Fail(c, 101, "参数错误")
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||
return
|
||||
}
|
||||
errList := global.Validator.ValidStruct(f)
|
||||
errList := global.Validator.ValidStruct(c, f)
|
||||
if len(errList) > 0 {
|
||||
response.Fail(c, 101, errList[0])
|
||||
return
|
||||
}
|
||||
u := f.ToPeer()
|
||||
err := service.AllService.PeerService.Create(u)
|
||||
p := f.ToPeer()
|
||||
err := service.AllService.PeerService.Create(p)
|
||||
if err != nil {
|
||||
response.Fail(c, 101, "创建失败")
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||
return
|
||||
}
|
||||
response.Success(c, u)
|
||||
response.Success(c, nil)
|
||||
}
|
||||
|
||||
// List 列表
|
||||
// @Tags 机器
|
||||
// @Summary 机器列表
|
||||
// @Description 机器列表
|
||||
// @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 "主机名"
|
||||
// @Success 200 {object} response.Response{data=model.PeerList}
|
||||
// @Failure 500 {object} response.Response
|
||||
// @Router /admin/peer/list [get]
|
||||
// @Security token
|
||||
func (ct *Peer) List(c *gin.Context) {
|
||||
query := &admin.PageQuery{}
|
||||
query := &admin.PeerQuery{}
|
||||
if err := c.ShouldBindQuery(query); err != nil {
|
||||
response.Fail(c, 101, "参数错误")
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||
return
|
||||
}
|
||||
res := service.AllService.PeerService.List(query.Page, query.PageSize, nil)
|
||||
res := service.AllService.PeerService.List(query.Page, query.PageSize, func(tx *gorm.DB) {
|
||||
if query.TimeAgo > 0 {
|
||||
lt := time.Now().Unix() - int64(query.TimeAgo)
|
||||
tx.Where("last_online_time < ?", lt)
|
||||
}
|
||||
if query.TimeAgo < 0 {
|
||||
lt := time.Now().Unix() + int64(query.TimeAgo)
|
||||
tx.Where("last_online_time > ?", lt)
|
||||
}
|
||||
if query.Id != "" {
|
||||
tx.Where("id like ?", "%"+query.Id+"%")
|
||||
}
|
||||
if query.Hostname != "" {
|
||||
tx.Where("hostname like ?", "%"+query.Hostname+"%")
|
||||
}
|
||||
})
|
||||
response.Success(c, res)
|
||||
}
|
||||
|
||||
// Update 编辑
|
||||
// @Tags 机器
|
||||
// @Summary 机器编辑
|
||||
// @Description 机器编辑
|
||||
// @Tags 设备
|
||||
// @Summary 设备编辑
|
||||
// @Description 设备编辑
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param body body admin.PeerForm true "机器信息"
|
||||
// @Param body body admin.PeerForm true "设备信息"
|
||||
// @Success 200 {object} response.Response{data=model.Peer}
|
||||
// @Failure 500 {object} response.Response
|
||||
// @Router /admin/peer/update [post]
|
||||
@@ -102,14 +122,14 @@ func (ct *Peer) List(c *gin.Context) {
|
||||
func (ct *Peer) Update(c *gin.Context) {
|
||||
f := &admin.PeerForm{}
|
||||
if err := c.ShouldBindJSON(f); err != nil {
|
||||
response.Fail(c, 101, "参数错误")
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||
return
|
||||
}
|
||||
if f.RowId == 0 {
|
||||
response.Fail(c, 101, "参数错误")
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||
return
|
||||
}
|
||||
errList := global.Validator.ValidStruct(f)
|
||||
errList := global.Validator.ValidStruct(c, f)
|
||||
if len(errList) > 0 {
|
||||
response.Fail(c, 101, errList[0])
|
||||
return
|
||||
@@ -117,19 +137,19 @@ func (ct *Peer) Update(c *gin.Context) {
|
||||
u := f.ToPeer()
|
||||
err := service.AllService.PeerService.Update(u)
|
||||
if err != nil {
|
||||
response.Fail(c, 101, "更新失败")
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||
return
|
||||
}
|
||||
response.Success(c, nil)
|
||||
}
|
||||
|
||||
// Delete 删除
|
||||
// @Tags 机器
|
||||
// @Summary 机器删除
|
||||
// @Description 机器删除
|
||||
// @Tags 设备
|
||||
// @Summary 设备删除
|
||||
// @Description 设备删除
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param body body admin.PeerForm true "机器信息"
|
||||
// @Param body body admin.PeerForm true "设备信息"
|
||||
// @Success 200 {object} response.Response
|
||||
// @Failure 500 {object} response.Response
|
||||
// @Router /admin/peer/delete [post]
|
||||
@@ -137,11 +157,11 @@ func (ct *Peer) Update(c *gin.Context) {
|
||||
func (ct *Peer) Delete(c *gin.Context) {
|
||||
f := &admin.PeerForm{}
|
||||
if err := c.ShouldBindJSON(f); err != nil {
|
||||
response.Fail(c, 101, "系统错误")
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||
return
|
||||
}
|
||||
id := f.RowId
|
||||
errList := global.Validator.ValidVar(id, "required,gt=0")
|
||||
errList := global.Validator.ValidVar(c, id, "required,gt=0")
|
||||
if len(errList) > 0 {
|
||||
response.Fail(c, 101, errList[0])
|
||||
return
|
||||
@@ -153,8 +173,55 @@ func (ct *Peer) Delete(c *gin.Context) {
|
||||
response.Success(c, nil)
|
||||
return
|
||||
}
|
||||
response.Fail(c, 101, err.Error())
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||
return
|
||||
}
|
||||
response.Fail(c, 101, "信息不存在")
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||
}
|
||||
|
||||
// BatchDelete 批量删除
|
||||
// @Tags 设备
|
||||
// @Summary 批量设备删除
|
||||
// @Description 批量设备删除
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param body body admin.PeerBatchDeleteForm true "设备id"
|
||||
// @Success 200 {object} response.Response
|
||||
// @Failure 500 {object} response.Response
|
||||
// @Router /admin/peer/delete [post]
|
||||
// @Security token
|
||||
func (ct *Peer) BatchDelete(c *gin.Context) {
|
||||
f := &admin.PeerBatchDeleteForm{}
|
||||
if err := c.ShouldBindJSON(f); err != nil {
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||
return
|
||||
}
|
||||
if len(f.RowIds) == 0 {
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||
return
|
||||
}
|
||||
err := service.AllService.PeerService.BatchDelete(f.RowIds)
|
||||
if err != nil {
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||
return
|
||||
}
|
||||
response.Success(c, nil)
|
||||
}
|
||||
|
||||
func (ct *Peer) SimpleData(c *gin.Context) {
|
||||
f := &admin.SimpleDataQuery{}
|
||||
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
|
||||
}
|
||||
res := service.AllService.PeerService.List(1, 99999, func(tx *gorm.DB) {
|
||||
//可以公开的情报
|
||||
tx.Select("id,version")
|
||||
tx.Where("id in (?)", f.Ids)
|
||||
})
|
||||
response.Success(c, res)
|
||||
}
|
||||
|
||||
@@ -9,9 +9,9 @@ import (
|
||||
type Rustdesk struct {
|
||||
}
|
||||
|
||||
// ServerConfig 服务配置
|
||||
// ServerConfig RUSTDESK服务配置
|
||||
// @Tags ADMIN
|
||||
// @Summary 服务配置
|
||||
// @Summary RUSTDESK服务配置
|
||||
// @Description 服务配置,给webclient提供api-server
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
@@ -28,3 +28,19 @@ func (r *Rustdesk) ServerConfig(c *gin.Context) {
|
||||
}
|
||||
response.Success(c, cf)
|
||||
}
|
||||
|
||||
// AppConfig APP服务配置
|
||||
// @Tags ADMIN
|
||||
// @Summary APP服务配置
|
||||
// @Description APP服务配置
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} response.Response
|
||||
// @Failure 500 {object} response.Response
|
||||
// @Router /admin/app-config [get]
|
||||
// @Security token
|
||||
func (r *Rustdesk) AppConfig(c *gin.Context) {
|
||||
response.Success(c, &gin.H{
|
||||
"web_client": global.Config.App.WebClient,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -30,14 +30,14 @@ func (ct *Tag) Detail(c *gin.Context) {
|
||||
t := service.AllService.TagService.InfoById(uint(iid))
|
||||
u := service.AllService.UserService.CurUser(c)
|
||||
if !service.AllService.UserService.IsAdmin(u) && t.UserId != u.Id {
|
||||
response.Fail(c, 101, "无权限")
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
|
||||
return
|
||||
}
|
||||
if t.Id > 0 {
|
||||
response.Success(c, t)
|
||||
return
|
||||
}
|
||||
response.Fail(c, 101, "信息不存在")
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -55,25 +55,25 @@ func (ct *Tag) Detail(c *gin.Context) {
|
||||
func (ct *Tag) Create(c *gin.Context) {
|
||||
f := &admin.TagForm{}
|
||||
if err := c.ShouldBindJSON(f); err != nil {
|
||||
response.Fail(c, 101, "参数错误")
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||
return
|
||||
}
|
||||
errList := global.Validator.ValidStruct(f)
|
||||
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)
|
||||
if !service.AllService.UserService.IsAdmin(u) {
|
||||
if !service.AllService.UserService.IsAdmin(u) || t.UserId == 0 {
|
||||
t.UserId = u.Id
|
||||
}
|
||||
err := service.AllService.TagService.Create(t)
|
||||
if err != nil {
|
||||
response.Fail(c, 101, "创建失败")
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||
return
|
||||
}
|
||||
response.Success(c, u)
|
||||
response.Success(c, nil)
|
||||
}
|
||||
|
||||
// List 列表
|
||||
@@ -93,7 +93,7 @@ func (ct *Tag) Create(c *gin.Context) {
|
||||
func (ct *Tag) List(c *gin.Context) {
|
||||
query := &admin.TagQuery{}
|
||||
if err := c.ShouldBindQuery(query); err != nil {
|
||||
response.Fail(c, 101, "参数错误")
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||
return
|
||||
}
|
||||
u := service.AllService.UserService.CurUser(c)
|
||||
@@ -101,9 +101,15 @@ func (ct *Tag) List(c *gin.Context) {
|
||||
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")
|
||||
})
|
||||
if query.UserId > 0 {
|
||||
tx.Where("user_id = ?", query.UserId)
|
||||
}
|
||||
if query.CollectionId != nil && *query.CollectionId >= 0 {
|
||||
tx.Where("collection_id = ?", query.CollectionId)
|
||||
}
|
||||
})
|
||||
response.Success(c, res)
|
||||
}
|
||||
@@ -122,27 +128,27 @@ func (ct *Tag) List(c *gin.Context) {
|
||||
func (ct *Tag) Update(c *gin.Context) {
|
||||
f := &admin.TagForm{}
|
||||
if err := c.ShouldBindJSON(f); err != nil {
|
||||
response.Fail(c, 101, "参数错误")
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||
return
|
||||
}
|
||||
errList := global.Validator.ValidStruct(f)
|
||||
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.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||
return
|
||||
}
|
||||
t := f.ToTag()
|
||||
u := service.AllService.UserService.CurUser(c)
|
||||
if !service.AllService.UserService.IsAdmin(u) && t.UserId != u.Id {
|
||||
response.Fail(c, 101, "无权限")
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
|
||||
return
|
||||
}
|
||||
err := service.AllService.TagService.Update(t)
|
||||
if err != nil {
|
||||
response.Fail(c, 101, "更新失败")
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||
return
|
||||
}
|
||||
response.Success(c, nil)
|
||||
@@ -162,11 +168,11 @@ func (ct *Tag) Update(c *gin.Context) {
|
||||
func (ct *Tag) Delete(c *gin.Context) {
|
||||
f := &admin.TagForm{}
|
||||
if err := c.ShouldBindJSON(f); err != nil {
|
||||
response.Fail(c, 101, "系统错误")
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||
return
|
||||
}
|
||||
id := f.Id
|
||||
errList := global.Validator.ValidVar(id, "required,gt=0")
|
||||
errList := global.Validator.ValidVar(c, id, "required,gt=0")
|
||||
if len(errList) > 0 {
|
||||
response.Fail(c, 101, errList[0])
|
||||
return
|
||||
@@ -174,7 +180,7 @@ func (ct *Tag) Delete(c *gin.Context) {
|
||||
t := service.AllService.TagService.InfoById(f.Id)
|
||||
u := service.AllService.UserService.CurUser(c)
|
||||
if !service.AllService.UserService.IsAdmin(u) && t.UserId != u.Id {
|
||||
response.Fail(c, 101, "无权限")
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
|
||||
return
|
||||
}
|
||||
if u.Id > 0 {
|
||||
@@ -186,5 +192,5 @@ func (ct *Tag) Delete(c *gin.Context) {
|
||||
response.Fail(c, 101, err.Error())
|
||||
return
|
||||
}
|
||||
response.Fail(c, 101, "信息不存在")
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ func (ct *User) Detail(c *gin.Context) {
|
||||
response.Success(c, u)
|
||||
return
|
||||
}
|
||||
response.Fail(c, 101, "信息不存在")
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -51,10 +51,10 @@ func (ct *User) Detail(c *gin.Context) {
|
||||
func (ct *User) Create(c *gin.Context) {
|
||||
f := &admin.UserForm{}
|
||||
if err := c.ShouldBindJSON(f); err != nil {
|
||||
response.Fail(c, 101, "参数错误")
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||
return
|
||||
}
|
||||
errList := global.Validator.ValidStruct(f)
|
||||
errList := global.Validator.ValidStruct(c, f)
|
||||
if len(errList) > 0 {
|
||||
response.Fail(c, 101, errList[0])
|
||||
return
|
||||
@@ -62,10 +62,10 @@ func (ct *User) Create(c *gin.Context) {
|
||||
u := f.ToUser()
|
||||
err := service.AllService.UserService.Create(u)
|
||||
if err != nil {
|
||||
response.Fail(c, 101, "创建失败")
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||
return
|
||||
}
|
||||
response.Success(c, u)
|
||||
response.Success(c, nil)
|
||||
}
|
||||
|
||||
// List 列表
|
||||
@@ -84,7 +84,7 @@ func (ct *User) Create(c *gin.Context) {
|
||||
func (ct *User) List(c *gin.Context) {
|
||||
query := &admin.UserQuery{}
|
||||
if err := c.ShouldBindQuery(query); err != nil {
|
||||
response.Fail(c, 101, "参数错误")
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||
return
|
||||
}
|
||||
res := service.AllService.UserService.List(query.Page, query.PageSize, func(tx *gorm.DB) {
|
||||
@@ -109,14 +109,14 @@ func (ct *User) List(c *gin.Context) {
|
||||
func (ct *User) Update(c *gin.Context) {
|
||||
f := &admin.UserForm{}
|
||||
if err := c.ShouldBindJSON(f); err != nil {
|
||||
response.Fail(c, 101, "参数错误:"+err.Error())
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||
return
|
||||
}
|
||||
if f.Id == 0 {
|
||||
response.Fail(c, 101, "参数错误")
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||
return
|
||||
}
|
||||
errList := global.Validator.ValidStruct(f)
|
||||
errList := global.Validator.ValidStruct(c, f)
|
||||
if len(errList) > 0 {
|
||||
response.Fail(c, 101, errList[0])
|
||||
return
|
||||
@@ -124,7 +124,7 @@ func (ct *User) Update(c *gin.Context) {
|
||||
u := f.ToUser()
|
||||
err := service.AllService.UserService.Update(u)
|
||||
if err != nil {
|
||||
response.Fail(c, 101, "更新失败")
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||
return
|
||||
}
|
||||
response.Success(c, nil)
|
||||
@@ -144,11 +144,11 @@ func (ct *User) Update(c *gin.Context) {
|
||||
func (ct *User) Delete(c *gin.Context) {
|
||||
f := &admin.UserForm{}
|
||||
if err := c.ShouldBindJSON(f); err != nil {
|
||||
response.Fail(c, 101, "系统错误")
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||
return
|
||||
}
|
||||
id := f.Id
|
||||
errList := global.Validator.ValidVar(id, "required,gt=0")
|
||||
errList := global.Validator.ValidVar(c, id, "required,gt=0")
|
||||
if len(errList) > 0 {
|
||||
response.Fail(c, 101, errList[0])
|
||||
return
|
||||
@@ -163,7 +163,7 @@ func (ct *User) Delete(c *gin.Context) {
|
||||
response.Fail(c, 101, err.Error())
|
||||
return
|
||||
}
|
||||
response.Fail(c, 101, "信息不存在")
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||
}
|
||||
|
||||
// UpdatePassword 修改密码
|
||||
@@ -180,22 +180,22 @@ func (ct *User) Delete(c *gin.Context) {
|
||||
func (ct *User) UpdatePassword(c *gin.Context) {
|
||||
f := &admin.UserPasswordForm{}
|
||||
if err := c.ShouldBindJSON(f); err != nil {
|
||||
response.Fail(c, 101, "参数错误")
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||
return
|
||||
}
|
||||
errList := global.Validator.ValidStruct(f)
|
||||
errList := global.Validator.ValidStruct(c, f)
|
||||
if len(errList) > 0 {
|
||||
response.Fail(c, 101, errList[0])
|
||||
return
|
||||
}
|
||||
u := service.AllService.UserService.InfoById(f.Id)
|
||||
if u.Id == 0 {
|
||||
response.Fail(c, 101, "信息不存在")
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||
return
|
||||
}
|
||||
err := service.AllService.UserService.UpdatePassword(u, f.Password)
|
||||
if err != nil {
|
||||
response.Fail(c, 101, "更新失败")
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||
return
|
||||
}
|
||||
response.Success(c, nil)
|
||||
@@ -237,11 +237,11 @@ func (ct *User) Current(c *gin.Context) {
|
||||
func (ct *User) ChangeCurPwd(c *gin.Context) {
|
||||
f := &admin.ChangeCurPasswordForm{}
|
||||
if err := c.ShouldBindJSON(f); err != nil {
|
||||
response.Fail(c, 101, "参数错误")
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
errList := global.Validator.ValidStruct(f)
|
||||
errList := global.Validator.ValidStruct(c, f)
|
||||
if len(errList) > 0 {
|
||||
response.Fail(c, 101, errList[0])
|
||||
return
|
||||
@@ -249,13 +249,77 @@ func (ct *User) ChangeCurPwd(c *gin.Context) {
|
||||
u := service.AllService.UserService.CurUser(c)
|
||||
oldPwd := service.AllService.UserService.EncryptPassword(f.OldPassword)
|
||||
if u.Password != oldPwd {
|
||||
response.Fail(c, 101, "旧密码错误")
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "OldPasswordError"))
|
||||
return
|
||||
}
|
||||
err := service.AllService.UserService.UpdatePassword(u, f.NewPassword)
|
||||
if err != nil {
|
||||
response.Fail(c, 101, "更新失败")
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||
return
|
||||
}
|
||||
response.Success(c, nil)
|
||||
}
|
||||
|
||||
// MyOauth
|
||||
// @Tags 用户
|
||||
// @Summary 我的授权
|
||||
// @Description 我的授权
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} response.Response{data=[]adResp.UserOauthItem}
|
||||
// @Failure 500 {object} response.Response
|
||||
// @Router /admin/user/myOauth [get]
|
||||
// @Security token
|
||||
func (ct *User) MyOauth(c *gin.Context) {
|
||||
u := service.AllService.UserService.CurUser(c)
|
||||
oal := service.AllService.OauthService.List(1, 100, nil)
|
||||
ops := make([]string, 0)
|
||||
for _, oa := range oal.Oauths {
|
||||
ops = append(ops, oa.Op)
|
||||
}
|
||||
uts := service.AllService.UserService.UserThirdsByUserId(u.Id)
|
||||
var res []*adResp.UserOauthItem
|
||||
for _, oa := range oal.Oauths {
|
||||
item := &adResp.UserOauthItem{
|
||||
ThirdType: oa.Op,
|
||||
}
|
||||
for _, ut := range uts {
|
||||
if ut.ThirdType == oa.Op {
|
||||
item.Status = 1
|
||||
break
|
||||
}
|
||||
}
|
||||
res = append(res, item)
|
||||
}
|
||||
response.Success(c, res)
|
||||
}
|
||||
|
||||
// groupUsers
|
||||
func (ct *User) GroupUsers(c *gin.Context) {
|
||||
q := &admin.GroupUsersQuery{}
|
||||
if err := c.ShouldBindJSON(q); err != nil {
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||
return
|
||||
}
|
||||
u := service.AllService.UserService.CurUser(c)
|
||||
gid := u.GroupId
|
||||
uid := u.Id
|
||||
if service.AllService.UserService.IsAdmin(u) && q.UserId > 0 {
|
||||
nu := service.AllService.UserService.InfoById(q.UserId)
|
||||
gid = nu.GroupId
|
||||
uid = q.UserId
|
||||
}
|
||||
res := service.AllService.UserService.List(1, 999, func(tx *gorm.DB) {
|
||||
tx.Where("group_id = ?", gid)
|
||||
})
|
||||
var data []*adResp.GroupUsersPayload
|
||||
for _, _u := range res.Users {
|
||||
gup := &adResp.GroupUsersPayload{}
|
||||
gup.FromUser(_u)
|
||||
if _u.Id == uid {
|
||||
gup.Status = 0
|
||||
}
|
||||
data = append(data, gup)
|
||||
}
|
||||
response.Success(c, data)
|
||||
}
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"Gwen/global"
|
||||
requstform "Gwen/http/request/api"
|
||||
"Gwen/http/response"
|
||||
"Gwen/http/response/api"
|
||||
"Gwen/model"
|
||||
"Gwen/service"
|
||||
"Gwen/utils"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"errors"
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Ab struct {
|
||||
@@ -66,39 +70,30 @@ func (a *Ab) UpAb(c *gin.Context) {
|
||||
abf := &requstform.AddressBookForm{}
|
||||
err := c.ShouldBindJSON(&abf)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
response.Error(c, "参数错误")
|
||||
response.Error(c, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||
return
|
||||
}
|
||||
abd := &requstform.AddressBookFormData{}
|
||||
err = json.Unmarshal([]byte(abf.Data), abd)
|
||||
if err != nil {
|
||||
response.Error(c, "系统错误")
|
||||
response.Error(c, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||
return
|
||||
}
|
||||
tc := map[string]uint{}
|
||||
err = json.Unmarshal([]byte(abd.TagColors), &tc)
|
||||
if err != nil {
|
||||
response.Error(c, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
//fmt.Println(abd)
|
||||
//for _, peer := range abd.Peers {
|
||||
// fmt.Println(peer)
|
||||
//}
|
||||
|
||||
user := service.AllService.UserService.CurUser(c)
|
||||
|
||||
err = service.AllService.AddressBookService.UpdateAddressBook(abd.Peers, user.Id)
|
||||
if err != nil {
|
||||
c.Abort()
|
||||
response.Error(c, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
tc := map[string]uint{}
|
||||
err = json.Unmarshal([]byte(abd.TagColors), &tc)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
response.Error(c, "系统错误")
|
||||
return
|
||||
} else {
|
||||
service.AllService.TagService.UpdateTags(user.Id, tc)
|
||||
}
|
||||
service.AllService.TagService.UpdateTags(user.Id, tc)
|
||||
|
||||
c.JSON(http.StatusOK, nil)
|
||||
}
|
||||
@@ -120,31 +115,630 @@ func (a *Ab) Tags(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, tags.Tags)
|
||||
}
|
||||
|
||||
// PTags
|
||||
// @Tags 地址[Personal]
|
||||
// @Summary 标签
|
||||
// @Description 标签
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param guid path string true "guid"
|
||||
// @Success 200 {object} model.TagList
|
||||
// @Failure 500 {object} response.ErrorResponse
|
||||
// @Router /ab/tags/{guid} [post]
|
||||
// @Security BearerAuth
|
||||
func (a *Ab) PTags(c *gin.Context) {
|
||||
u := service.AllService.UserService.CurUser(c)
|
||||
guid := c.Param("guid")
|
||||
_, uid, cid, err := a.CheckGuid(u, guid)
|
||||
if err != nil {
|
||||
response.Error(c, response.TranslateMsg(c, err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
//check privileges
|
||||
if !service.AllService.AddressBookService.CheckUserReadPrivilege(u, uid, cid) {
|
||||
response.Error(c, response.TranslateMsg(c, "NoAccess"))
|
||||
return
|
||||
}
|
||||
tags := service.AllService.TagService.ListByUserIdAndCollectionId(uid, cid)
|
||||
c.JSON(http.StatusOK, tags.Tags)
|
||||
}
|
||||
|
||||
// TagAdd
|
||||
// @Tags 地址
|
||||
// @Tags 地址[Personal]
|
||||
// @Summary 标签添加
|
||||
// @Description 标签
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param guid path string true "guid"
|
||||
// @Success 200 {string} string
|
||||
// @Failure 500 {object} response.ErrorResponse
|
||||
// @Router /ab/add [post]
|
||||
// @Router /ab/tag/add/{guid} [post]
|
||||
// @Security BearerAuth
|
||||
func (a *Ab) TagAdd(c *gin.Context) {
|
||||
|
||||
t := &model.Tag{}
|
||||
err := c.ShouldBindJSON(t)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
response.Error(c, "参数错误")
|
||||
response.Error(c, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
u := service.AllService.UserService.CurUser(c)
|
||||
guid := c.Param("guid")
|
||||
_, uid, cid, err := a.CheckGuid(u, guid)
|
||||
if err != nil {
|
||||
response.Error(c, response.TranslateMsg(c, err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
//check privileges
|
||||
if !service.AllService.AddressBookService.CheckUserWritePrivilege(u, uid, cid) {
|
||||
response.Error(c, response.TranslateMsg(c, "NoAccess"))
|
||||
return
|
||||
}
|
||||
|
||||
tag := service.AllService.TagService.InfoByUserIdAndNameAndCollectionId(uid, t.Name, cid)
|
||||
if tag != nil && tag.Id != 0 {
|
||||
response.Error(c, response.TranslateMsg(c, "ItemExists"))
|
||||
return
|
||||
}
|
||||
t.UserId = uid
|
||||
t.CollectionId = cid
|
||||
err = service.AllService.TagService.Create(t)
|
||||
if err != nil {
|
||||
response.Error(c, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||
return
|
||||
}
|
||||
c.String(http.StatusOK, "")
|
||||
}
|
||||
|
||||
// TagRename
|
||||
// @Tags 地址[Personal]
|
||||
// @Summary 标签重命名
|
||||
// @Description 标签
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param guid path string true "guid"
|
||||
// @Success 200 {string} string
|
||||
// @Failure 500 {object} response.ErrorResponse
|
||||
// @Router /ab/tag/rename/{guid} [put]
|
||||
// @Security BearerAuth
|
||||
func (a *Ab) TagRename(c *gin.Context) {
|
||||
|
||||
t := &requstform.TagRenameForm{}
|
||||
err := c.ShouldBindJSON(t)
|
||||
if err != nil {
|
||||
response.Error(c, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||
return
|
||||
}
|
||||
u := service.AllService.UserService.CurUser(c)
|
||||
guid := c.Param("guid")
|
||||
_, uid, cid, err := a.CheckGuid(u, guid)
|
||||
if err != nil {
|
||||
response.Error(c, response.TranslateMsg(c, err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
//check privileges
|
||||
if !service.AllService.AddressBookService.CheckUserWritePrivilege(u, uid, cid) {
|
||||
response.Error(c, response.TranslateMsg(c, "NoAccess"))
|
||||
return
|
||||
}
|
||||
|
||||
tag := service.AllService.TagService.InfoByUserIdAndNameAndCollectionId(uid, t.Old, cid)
|
||||
if tag == nil || tag.Id == 0 {
|
||||
response.Error(c, response.TranslateMsg(c, "ItemNotFound"))
|
||||
return
|
||||
}
|
||||
ntag := service.AllService.TagService.InfoByUserIdAndNameAndCollectionId(uid, t.New, cid)
|
||||
if ntag != nil && ntag.Id != 0 {
|
||||
response.Error(c, response.TranslateMsg(c, "ItemExists"))
|
||||
return
|
||||
}
|
||||
tag.Name = t.New
|
||||
err = service.AllService.TagService.Update(tag)
|
||||
if err != nil {
|
||||
response.Error(c, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||
return
|
||||
}
|
||||
c.String(http.StatusOK, "")
|
||||
}
|
||||
|
||||
// TagUpdate
|
||||
// @Tags 地址[Personal]
|
||||
// @Summary 标签修改颜色
|
||||
// @Description 标签
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param guid path string true "guid"
|
||||
// @Success 200 {string} string
|
||||
// @Failure 500 {object} response.ErrorResponse
|
||||
// @Router /ab/tag/update/{guid} [put]
|
||||
// @Security BearerAuth
|
||||
func (a *Ab) TagUpdate(c *gin.Context) {
|
||||
t := &requstform.TagColorForm{}
|
||||
err := c.ShouldBindJSON(t)
|
||||
if err != nil {
|
||||
response.Error(c, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||
return
|
||||
}
|
||||
u := service.AllService.UserService.CurUser(c)
|
||||
guid := c.Param("guid")
|
||||
_, uid, cid, err := a.CheckGuid(u, guid)
|
||||
if err != nil {
|
||||
response.Error(c, response.TranslateMsg(c, err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
//check privileges
|
||||
if !service.AllService.AddressBookService.CheckUserWritePrivilege(u, uid, cid) {
|
||||
response.Error(c, response.TranslateMsg(c, "NoAccess"))
|
||||
return
|
||||
}
|
||||
|
||||
tag := service.AllService.TagService.InfoByUserIdAndNameAndCollectionId(uid, t.Name, cid)
|
||||
if tag == nil || tag.Id == 0 {
|
||||
response.Error(c, response.TranslateMsg(c, "ItemNotFound"))
|
||||
return
|
||||
}
|
||||
tag.Color = t.Color
|
||||
err = service.AllService.TagService.Update(tag)
|
||||
if err != nil {
|
||||
response.Error(c, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||
return
|
||||
}
|
||||
c.String(http.StatusOK, "")
|
||||
}
|
||||
|
||||
// TagDel
|
||||
// @Tags 地址[Personal]
|
||||
// @Summary 标签删除
|
||||
// @Description 标签
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param guid path string true "guid"
|
||||
// @Success 200 {string} string
|
||||
// @Failure 500 {object} response.ErrorResponse
|
||||
// @Router /ab/tag/{guid} [delete]
|
||||
// @Security BearerAuth
|
||||
func (a *Ab) TagDel(c *gin.Context) {
|
||||
|
||||
t := &[]string{}
|
||||
err := c.ShouldBind(t)
|
||||
if err != nil {
|
||||
response.Error(c, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||
return
|
||||
}
|
||||
//fmt.Println(t)
|
||||
u := service.AllService.UserService.CurUser(c)
|
||||
guid := c.Param("guid")
|
||||
_, uid, cid, err := a.CheckGuid(u, guid)
|
||||
if err != nil {
|
||||
response.Error(c, response.TranslateMsg(c, err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
//check privileges
|
||||
if !service.AllService.AddressBookService.CheckUserFullControlPrivilege(u, uid, cid) {
|
||||
response.Error(c, response.TranslateMsg(c, "NoAccess"))
|
||||
return
|
||||
}
|
||||
|
||||
for _, name := range *t {
|
||||
tag := service.AllService.TagService.InfoByUserIdAndNameAndCollectionId(uid, name, cid)
|
||||
if tag == nil || tag.Id == 0 {
|
||||
response.Error(c, response.TranslateMsg(c, "ItemNotFound"))
|
||||
return
|
||||
}
|
||||
err = service.AllService.TagService.Delete(tag)
|
||||
if err != nil {
|
||||
response.Error(c, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
c.String(http.StatusOK, "")
|
||||
}
|
||||
|
||||
// Personal
|
||||
// @Tags 地址[Personal]
|
||||
// @Summary 个人地址
|
||||
// @Description 个人地址
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param string body string false "string valid"
|
||||
// @Success 200 {object} response.Response
|
||||
// @Failure 500 {object} response.Response
|
||||
// @Router /ab/personal [post]
|
||||
// @Security BearerAuth
|
||||
func (a *Ab) Personal(c *gin.Context) {
|
||||
user := service.AllService.UserService.CurUser(c)
|
||||
/**
|
||||
guid = json['guid'] ?? '',
|
||||
name = json['name'] ?? '',
|
||||
owner = json['owner'] ?? '',
|
||||
note = json['note'] ?? '',
|
||||
rule = json['rule'] ?? 0;
|
||||
*/
|
||||
if global.Config.Rustdesk.Personal == 1 {
|
||||
guid := a.ComposeGuid(user.GroupId, user.Id, 0)
|
||||
//如果返回了guid,后面的请求会有变化
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"guid": guid,
|
||||
"name": user.Username,
|
||||
"rule": 3,
|
||||
})
|
||||
} else {
|
||||
c.JSON(http.StatusOK, nil)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Settings
|
||||
// @Tags 地址[Personal]
|
||||
// @Summary 设置
|
||||
// @Description 设置
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param string body string false "string valid"
|
||||
// @Success 200 {object} response.Response
|
||||
// @Failure 500 {object} response.Response
|
||||
// @Router /ab/settings [post]
|
||||
// @Security BearerAuth
|
||||
func (a *Ab) Settings(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"max_peer_one_ab": 0, //最大peer数,0表示不限制
|
||||
})
|
||||
}
|
||||
|
||||
// SharedProfiles
|
||||
// @Tags 地址[Personal]
|
||||
// @Summary 共享地址簿
|
||||
// @Description 共享
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param current query int false "页码"
|
||||
// @Param pageSize query int false "每页数量"
|
||||
// @Success 200 {object} response.Response
|
||||
// @Failure 500 {object} response.Response
|
||||
// @Router /ab/shared/profiles [post]
|
||||
// @Security BearerAuth
|
||||
func (a *Ab) SharedProfiles(c *gin.Context) {
|
||||
|
||||
var res []*api.SharedProfilesPayload
|
||||
|
||||
user := service.AllService.UserService.CurUser(c)
|
||||
myAbCollectionList := service.AllService.AddressBookService.ListCollectionByUserId(user.Id)
|
||||
for _, ab := range myAbCollectionList.AddressBookCollection {
|
||||
res = append(res, &api.SharedProfilesPayload{
|
||||
Guid: a.ComposeGuid(user.GroupId, user.Id, ab.Id),
|
||||
Name: ab.Name,
|
||||
Owner: user.Username,
|
||||
Rule: model.ShareAddressBookRuleRuleFullControl,
|
||||
})
|
||||
}
|
||||
|
||||
allAbIds := make(map[uint]int) //用map去重,并保留最大Rule
|
||||
allUserIds := make(map[uint]*model.User)
|
||||
rules := service.AllService.AddressBookService.CollectionReadRules(user)
|
||||
for _, rule := range rules {
|
||||
//先判断是否存在
|
||||
r, ok := allAbIds[rule.CollectionId]
|
||||
if ok {
|
||||
//再判断权限大小
|
||||
if r < rule.Rule {
|
||||
allAbIds[rule.CollectionId] = rule.Rule
|
||||
}
|
||||
} else {
|
||||
allAbIds[rule.CollectionId] = rule.Rule
|
||||
allUserIds[rule.UserId] = nil
|
||||
}
|
||||
|
||||
}
|
||||
//u := service.AllService.UserService.CurUser(c)
|
||||
abids := utils.Keys(allAbIds)
|
||||
collections := service.AllService.AddressBookService.ListCollectionByIds(abids)
|
||||
|
||||
//err = service.AllService.TagService.UpdateTags(t.Name, t.Color, user.Id)
|
||||
//if err != nil {
|
||||
// response.Error(c, "操作失败")
|
||||
// return
|
||||
//}
|
||||
c.JSON(http.StatusOK, "")
|
||||
ids := utils.Keys(allUserIds)
|
||||
allUsers := service.AllService.UserService.ListByIds(ids)
|
||||
for _, u := range allUsers {
|
||||
allUserIds[u.Id] = u
|
||||
}
|
||||
|
||||
for _, collection := range collections {
|
||||
_u, ok := allUserIds[collection.UserId]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
res = append(res, &api.SharedProfilesPayload{
|
||||
Guid: a.ComposeGuid(_u.GroupId, _u.Id, collection.Id),
|
||||
Name: collection.Name,
|
||||
Owner: _u.Username,
|
||||
Rule: allAbIds[collection.Id],
|
||||
})
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"total": 0, //len(res),
|
||||
"data": res,
|
||||
})
|
||||
}
|
||||
|
||||
// ParseGuid
|
||||
func (a *Ab) ParseGuid(guid string) (gid, uid, cid uint) {
|
||||
//用-切割 guid
|
||||
guids := strings.Split(guid, "-")
|
||||
if len(guids) < 2 {
|
||||
return 0, 0, 0
|
||||
}
|
||||
if len(guids) != 3 {
|
||||
cid = 0
|
||||
} else {
|
||||
s, err := strconv.Atoi(guids[2])
|
||||
if err != nil {
|
||||
return 0, 0, 0
|
||||
}
|
||||
cid = uint(s)
|
||||
}
|
||||
g, err := strconv.Atoi(guids[0])
|
||||
if err != nil {
|
||||
return 0, 0, 0
|
||||
}
|
||||
gid = uint(g)
|
||||
u, err := strconv.Atoi(guids[1])
|
||||
if err != nil {
|
||||
return 0, 0, 0
|
||||
}
|
||||
uid = uint(u)
|
||||
return
|
||||
}
|
||||
|
||||
// ComposeGuid
|
||||
func (a *Ab) ComposeGuid(gid, uid, cid uint) string {
|
||||
return strconv.Itoa(int(gid)) + "-" + strconv.Itoa(int(uid)) + "-" + strconv.Itoa(int(cid))
|
||||
}
|
||||
|
||||
// CheckGuid
|
||||
func (a *Ab) CheckGuid(cu *model.User, guid string) (gid, uid, cid uint, err error) {
|
||||
gid, uid, cid = a.ParseGuid(guid)
|
||||
err = nil
|
||||
if gid == 0 || uid == 0 {
|
||||
err = errors.New("ParamsError")
|
||||
return
|
||||
}
|
||||
u := &model.User{}
|
||||
if cu.Id == uid {
|
||||
u = cu
|
||||
} else {
|
||||
u = service.AllService.UserService.InfoById(uid)
|
||||
}
|
||||
if u == nil || u.Id == 0 {
|
||||
err = errors.New("ParamsError")
|
||||
return
|
||||
}
|
||||
if u.GroupId != gid {
|
||||
err = errors.New("ParamsError")
|
||||
return
|
||||
}
|
||||
if cid == 0 && cu.Id != uid {
|
||||
err = errors.New("ParamsError")
|
||||
return
|
||||
}
|
||||
if cid > 0 {
|
||||
c := service.AllService.AddressBookService.CollectionInfoById(cid)
|
||||
if c == nil || c.Id == 0 {
|
||||
err = errors.New("ParamsError")
|
||||
return
|
||||
}
|
||||
if c.UserId != uid {
|
||||
err = errors.New("ParamsError")
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Peers
|
||||
// @Tags 地址[Personal]
|
||||
// @Summary 地址列表
|
||||
// @Description 地址
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param current query int false "页码"
|
||||
// @Param pageSize query int false "每页数量"
|
||||
// @Param ab query string false "guid"
|
||||
// @Success 200 {object} response.Response
|
||||
// @Failure 500 {object} response.Response
|
||||
// @Router /ab/peers [post]
|
||||
// @Security BearerAuth
|
||||
func (a *Ab) Peers(c *gin.Context) {
|
||||
u := service.AllService.UserService.CurUser(c)
|
||||
guid := c.Query("ab")
|
||||
_, uid, cid, err := a.CheckGuid(u, guid)
|
||||
if err != nil {
|
||||
response.Error(c, response.TranslateMsg(c, err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
//check privileges
|
||||
if !service.AllService.AddressBookService.CheckUserReadPrivilege(u, uid, cid) {
|
||||
response.Error(c, response.TranslateMsg(c, "NoAccess"))
|
||||
return
|
||||
}
|
||||
|
||||
al := service.AllService.AddressBookService.ListByUserIdAndCollectionId(uid, cid, 1, 1000)
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"total": al.Total,
|
||||
"data": al.AddressBooks,
|
||||
"licensed_devices": 99999,
|
||||
})
|
||||
}
|
||||
|
||||
// PeerAdd
|
||||
// @Tags 地址[Personal]
|
||||
// @Summary 添加地址
|
||||
// @Description 添加地址
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param guid path string true "guid"
|
||||
// @Success 200 {string} string
|
||||
// @Failure 500 {object} response.ErrorResponse
|
||||
// @Router /ab/peer/add/{guid} [post]
|
||||
// @Security BearerAuth
|
||||
func (a *Ab) PeerAdd(c *gin.Context) {
|
||||
// forceAlwaysRelay永远是字符串"false"
|
||||
//f := &gin.H{}
|
||||
f := &requstform.PersonalAddressBookForm{}
|
||||
err := c.ShouldBindJSON(f)
|
||||
if err != nil {
|
||||
response.Error(c, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
u := service.AllService.UserService.CurUser(c)
|
||||
guid := c.Param("guid")
|
||||
_, uid, cid, err := a.CheckGuid(u, guid)
|
||||
if err != nil {
|
||||
response.Error(c, response.TranslateMsg(c, err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
//check privileges
|
||||
if !service.AllService.AddressBookService.CheckUserWritePrivilege(u, uid, cid) {
|
||||
response.Error(c, response.TranslateMsg(c, "NoAccess"))
|
||||
return
|
||||
}
|
||||
|
||||
//fmt.Println(f)
|
||||
f.UserId = uid
|
||||
ab := f.ToAddressBook()
|
||||
ab.CollectionId = cid
|
||||
if ab.Platform == "" || ab.Username == "" || ab.Hostname == "" {
|
||||
peer := service.AllService.PeerService.FindById(ab.Id)
|
||||
if peer.RowId != 0 {
|
||||
ab.Platform = service.AllService.AddressBookService.PlatformFromOs(peer.Os)
|
||||
ab.Username = peer.Username
|
||||
ab.Hostname = peer.Hostname
|
||||
}
|
||||
}
|
||||
|
||||
err = service.AllService.AddressBookService.AddAddressBook(ab)
|
||||
if err != nil {
|
||||
response.Error(c, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||
return
|
||||
}
|
||||
c.String(http.StatusOK, "")
|
||||
}
|
||||
|
||||
// PeerDel
|
||||
// @Tags 地址[Personal]
|
||||
// @Summary 删除地址
|
||||
// @Description 删除地址
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param guid path string true "guid"
|
||||
// @Success 200 {string} string
|
||||
// @Failure 500 {object} response.ErrorResponse
|
||||
// @Router /ab/peer/add/{guid} [delete]
|
||||
// @Security BearerAuth
|
||||
func (a *Ab) PeerDel(c *gin.Context) {
|
||||
f := &[]string{}
|
||||
err := c.ShouldBind(f)
|
||||
if err != nil {
|
||||
response.Error(c, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||
return
|
||||
}
|
||||
u := service.AllService.UserService.CurUser(c)
|
||||
guid := c.Param("guid")
|
||||
_, uid, cid, err := a.CheckGuid(u, guid)
|
||||
if err != nil {
|
||||
response.Error(c, response.TranslateMsg(c, err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
//check privileges
|
||||
if !service.AllService.AddressBookService.CheckUserFullControlPrivilege(u, uid, cid) {
|
||||
response.Error(c, response.TranslateMsg(c, "NoAccess"))
|
||||
return
|
||||
}
|
||||
|
||||
for _, id := range *f {
|
||||
ab := service.AllService.AddressBookService.InfoByUserIdAndIdAndCid(uid, id, cid)
|
||||
if ab == nil || ab.RowId == 0 {
|
||||
response.Error(c, response.TranslateMsg(c, "ItemNotFound"))
|
||||
return
|
||||
}
|
||||
err = service.AllService.AddressBookService.Delete(ab)
|
||||
if err != nil {
|
||||
response.Error(c, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
c.String(http.StatusOK, "")
|
||||
}
|
||||
|
||||
// PeerUpdate
|
||||
// @Tags 地址[Personal]
|
||||
// @Summary 更新地址
|
||||
// @Description 更新地址
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param guid path string true "guid"
|
||||
// @Success 200 {string} string
|
||||
// @Failure 500 {object} response.ErrorResponse
|
||||
// @Router /ab/peer/update/{guid} [put]
|
||||
// @Security BearerAuth
|
||||
func (a *Ab) PeerUpdate(c *gin.Context) {
|
||||
f := gin.H{}
|
||||
//f := &requstform.PersonalAddressBookForm{}
|
||||
err := c.ShouldBindJSON(&f)
|
||||
if err != nil {
|
||||
response.Error(c, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||
return
|
||||
}
|
||||
u := service.AllService.UserService.CurUser(c)
|
||||
guid := c.Param("guid")
|
||||
_, uid, cid, err := a.CheckGuid(u, guid)
|
||||
if err != nil {
|
||||
response.Error(c, response.TranslateMsg(c, err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
//check privileges
|
||||
if !service.AllService.AddressBookService.CheckUserWritePrivilege(u, uid, cid) {
|
||||
response.Error(c, response.TranslateMsg(c, "NoAccess"))
|
||||
return
|
||||
}
|
||||
//fmt.Println(f)
|
||||
//判断f["Id"]是否存在
|
||||
fid, ok := f["id"]
|
||||
if !ok {
|
||||
response.Error(c, response.TranslateMsg(c, "ParamsError"))
|
||||
return
|
||||
}
|
||||
fidstr := fid.(string)
|
||||
|
||||
ab := service.AllService.AddressBookService.InfoByUserIdAndIdAndCid(uid, fidstr, cid)
|
||||
if ab == nil || ab.RowId == 0 {
|
||||
response.Error(c, response.TranslateMsg(c, "ItemNotFound"))
|
||||
return
|
||||
}
|
||||
//允许的字段
|
||||
allowUp := []string{"password", "hash", "tags", "alias"}
|
||||
//f中的字段如果不在allowUp中,就删除
|
||||
for k := range f {
|
||||
if !utils.InArray(k, allowUp) {
|
||||
delete(f, k)
|
||||
}
|
||||
}
|
||||
//fmt.Println(f)
|
||||
if tags, _ok := f["tags"]; _ok {
|
||||
f["tags"], _ = json.Marshal(tags)
|
||||
}
|
||||
err = service.AllService.AddressBookService.UpdateByMap(ab, f)
|
||||
if err != nil {
|
||||
response.Error(c, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||
return
|
||||
}
|
||||
c.String(http.StatusOK, "")
|
||||
}
|
||||
|
||||
84
http/controller/api/audit.go
Normal file
@@ -0,0 +1,84 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
request "Gwen/http/request/api"
|
||||
"Gwen/http/response"
|
||||
"Gwen/model"
|
||||
"Gwen/service"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gin-gonic/gin/binding"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Audit struct {
|
||||
}
|
||||
|
||||
// AuditConn
|
||||
// @Tags 审计
|
||||
// @Summary 审计连接
|
||||
// @Description 审计连接
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param body body request.AuditConnForm true "审计连接"
|
||||
// @Success 200 {string} string ""
|
||||
// @Failure 500 {object} response.Response
|
||||
// @Router /audit/conn [post]
|
||||
func (a *Audit) AuditConn(c *gin.Context) {
|
||||
af := &request.AuditConnForm{}
|
||||
err := c.ShouldBindBodyWith(af, binding.JSON)
|
||||
if err != nil {
|
||||
response.Error(c, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||
return
|
||||
}
|
||||
/*ttt := &gin.H{}
|
||||
c.ShouldBindBodyWith(ttt, binding.JSON)
|
||||
fmt.Println(ttt)*/
|
||||
ac := af.ToAuditConn()
|
||||
if af.Action == model.AuditActionNew {
|
||||
service.AllService.AuditService.CreateAuditConn(ac)
|
||||
} else if af.Action == model.AuditActionClose {
|
||||
ex := service.AllService.AuditService.InfoByPeerIdAndConnId(af.Id, af.ConnId)
|
||||
if ex.Id != 0 {
|
||||
ex.CloseTime = time.Now().Unix()
|
||||
service.AllService.AuditService.UpdateAuditConn(ex)
|
||||
}
|
||||
} else if af.Action == "" {
|
||||
ex := service.AllService.AuditService.InfoByPeerIdAndConnId(af.Id, af.ConnId)
|
||||
if ex.Id != 0 {
|
||||
up := &model.AuditConn{
|
||||
IdModel: model.IdModel{Id: ex.Id},
|
||||
FromPeer: ac.FromPeer,
|
||||
FromName: ac.FromName,
|
||||
SessionId: ac.SessionId,
|
||||
Type: ac.Type,
|
||||
}
|
||||
service.AllService.AuditService.UpdateAuditConn(up)
|
||||
}
|
||||
}
|
||||
response.Success(c, "")
|
||||
}
|
||||
|
||||
// AuditFile
|
||||
// @Tags 审计
|
||||
// @Summary 审计文件
|
||||
// @Description 审计文件
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param body body request.AuditFileForm true "审计文件"
|
||||
// @Success 200 {string} string ""
|
||||
// @Failure 500 {object} response.Response
|
||||
// @Router /audit/file [post]
|
||||
func (a *Audit) AuditFile(c *gin.Context) {
|
||||
aff := &request.AuditFileForm{}
|
||||
err := c.ShouldBindBodyWith(aff, binding.JSON)
|
||||
if err != nil {
|
||||
response.Error(c, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||
return
|
||||
}
|
||||
//ttt := &gin.H{}
|
||||
//c.ShouldBindBodyWith(ttt, binding.JSON)
|
||||
//fmt.Println(ttt)
|
||||
af := aff.ToAuditFile()
|
||||
service.AllService.AuditService.CreateAuditFile(af)
|
||||
response.Success(c, "")
|
||||
}
|
||||
@@ -28,23 +28,23 @@ type Group struct {
|
||||
// @Router /users [get]
|
||||
// @Security BearerAuth
|
||||
func (g *Group) Users(c *gin.Context) {
|
||||
u := service.AllService.UserService.CurUser(c)
|
||||
|
||||
if !*u.IsAdmin {
|
||||
gr := service.AllService.GroupService.InfoById(u.GroupId)
|
||||
if gr.Type != model.GroupTypeShare {
|
||||
response.Error(c, "不是管理员也不在分享组")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
q := &apiReq.UserListQuery{}
|
||||
err := c.ShouldBindQuery(&q)
|
||||
if err != nil {
|
||||
response.Error(c, err.Error())
|
||||
return
|
||||
}
|
||||
userList := service.AllService.UserService.ListByGroupId(u.GroupId, q.Page, q.PageSize)
|
||||
u := service.AllService.UserService.CurUser(c)
|
||||
gr := service.AllService.GroupService.InfoById(u.GroupId)
|
||||
userList := &model.UserList{}
|
||||
if !*u.IsAdmin && gr.Type != model.GroupTypeShare {
|
||||
//仅能获取到自己
|
||||
userList.Users = append(userList.Users, u)
|
||||
userList.Total = 1
|
||||
} else {
|
||||
userList = service.AllService.UserService.ListByGroupId(u.GroupId, q.Page, q.PageSize)
|
||||
}
|
||||
|
||||
var data []*apiResp.UserPayload
|
||||
for _, user := range userList.Users {
|
||||
up := &apiResp.UserPayload{}
|
||||
@@ -73,38 +73,36 @@ func (g *Group) Users(c *gin.Context) {
|
||||
// @Security BearerAuth
|
||||
func (g *Group) Peers(c *gin.Context) {
|
||||
u := service.AllService.UserService.CurUser(c)
|
||||
|
||||
if !*u.IsAdmin {
|
||||
gr := service.AllService.GroupService.InfoById(u.GroupId)
|
||||
if gr.Type != model.GroupTypeShare {
|
||||
response.Error(c, "不是管理员也不在分享组")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
q := &apiReq.PeerListQuery{}
|
||||
err := c.ShouldBindQuery(&q)
|
||||
if err != nil {
|
||||
response.Error(c, err.Error())
|
||||
return
|
||||
}
|
||||
gr := service.AllService.GroupService.InfoById(u.GroupId)
|
||||
users := make([]*model.User, 0, 1)
|
||||
if !*u.IsAdmin && gr.Type != model.GroupTypeShare {
|
||||
//仅能获取到自己
|
||||
users = append(users, u)
|
||||
} else {
|
||||
users = service.AllService.UserService.ListIdAndNameByGroupId(u.GroupId)
|
||||
}
|
||||
|
||||
users := service.AllService.UserService.ListIdAndNameByGroupId(u.GroupId)
|
||||
namesById := make(map[uint]string)
|
||||
userIds := make([]uint, 0)
|
||||
for _, user := range users {
|
||||
namesById[user.Id] = user.Username
|
||||
userIds = append(userIds, user.Id)
|
||||
}
|
||||
peerList := service.AllService.AddressBookService.ListByUserIds(userIds, q.Page, q.PageSize)
|
||||
peerList := service.AllService.PeerService.ListByUserIds(userIds, q.Page, q.PageSize)
|
||||
var data []*apiResp.GroupPeerPayload
|
||||
for _, ab := range peerList.AddressBooks {
|
||||
uname, ok := namesById[ab.UserId]
|
||||
for _, peer := range peerList.Peers {
|
||||
uname, ok := namesById[peer.UserId]
|
||||
if !ok {
|
||||
uname = ""
|
||||
}
|
||||
pp := &apiResp.GroupPeerPayload{}
|
||||
pp.FromAddressBook(ab, uname)
|
||||
pp.FromPeer(peer, uname)
|
||||
data = append(data, pp)
|
||||
|
||||
}
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
requstform "Gwen/http/request/api"
|
||||
"Gwen/http/response"
|
||||
"Gwen/model"
|
||||
"Gwen/service"
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Index struct {
|
||||
@@ -35,5 +39,25 @@ func (i *Index) Index(c *gin.Context) {
|
||||
// @Failure 500 {object} response.Response
|
||||
// @Router /heartbeat [post]
|
||||
func (i *Index) Heartbeat(c *gin.Context) {
|
||||
info := &requstform.PeerInfoInHeartbeat{}
|
||||
err := c.ShouldBindJSON(info)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{})
|
||||
return
|
||||
}
|
||||
if info.Uuid == "" {
|
||||
c.JSON(http.StatusOK, gin.H{})
|
||||
return
|
||||
}
|
||||
peer := service.AllService.PeerService.FindByUuid(info.Uuid)
|
||||
if peer == nil || peer.RowId == 0 {
|
||||
c.JSON(http.StatusOK, gin.H{})
|
||||
return
|
||||
}
|
||||
//如果在40s以内则不更新
|
||||
if time.Now().Unix()-peer.LastOnlineTime > 40 {
|
||||
upp := &model.Peer{RowId: peer.RowId, LastOnlineTime: time.Now().Unix(), LastOnlineIp: c.ClientIP()}
|
||||
service.AllService.PeerService.Update(upp)
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{})
|
||||
}
|
||||
|
||||
@@ -5,7 +5,10 @@ import (
|
||||
"Gwen/http/request/api"
|
||||
"Gwen/http/response"
|
||||
apiResp "Gwen/http/response/api"
|
||||
"Gwen/model"
|
||||
"Gwen/service"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
)
|
||||
@@ -26,13 +29,16 @@ type Login struct {
|
||||
func (l *Login) Login(c *gin.Context) {
|
||||
f := &api.LoginForm{}
|
||||
err := c.ShouldBindJSON(f)
|
||||
//fmt.Println(f)
|
||||
if err != nil {
|
||||
response.Error(c, "系统错误")
|
||||
global.Logger.Warn(fmt.Sprintf("Login Fail: %s %s %s", "ParamsError", c.RemoteIP(), c.ClientIP()))
|
||||
response.Error(c, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
errList := global.Validator.ValidStruct(f)
|
||||
errList := global.Validator.ValidStruct(c, f)
|
||||
if len(errList) > 0 {
|
||||
global.Logger.Warn(fmt.Sprintf("Login Fail: %s %s %s", "ParamsError", c.RemoteIP(), c.ClientIP()))
|
||||
response.Error(c, errList[0])
|
||||
return
|
||||
}
|
||||
@@ -40,11 +46,25 @@ func (l *Login) Login(c *gin.Context) {
|
||||
u := service.AllService.UserService.InfoByUsernamePassword(f.Username, f.Password)
|
||||
|
||||
if u.Id == 0 {
|
||||
response.Error(c, "用户名或密码错误")
|
||||
global.Logger.Warn(fmt.Sprintf("Login Fail: %s %s %s", "UsernameOrPasswordError", c.RemoteIP(), c.ClientIP()))
|
||||
response.Error(c, response.TranslateMsg(c, "UsernameOrPasswordError"))
|
||||
return
|
||||
}
|
||||
|
||||
ut := service.AllService.UserService.Login(u)
|
||||
//根据refer判断是webclient还是app
|
||||
ref := c.GetHeader("referer")
|
||||
if ref != "" {
|
||||
f.DeviceInfo.Type = "webclient"
|
||||
}
|
||||
|
||||
ut := service.AllService.UserService.Login(u, &model.LoginLog{
|
||||
UserId: u.Id,
|
||||
Client: f.DeviceInfo.Type,
|
||||
Uuid: f.Uuid,
|
||||
Ip: c.ClientIP(),
|
||||
Type: model.LoginLogTypeAccount,
|
||||
Platform: f.DeviceInfo.Os,
|
||||
})
|
||||
|
||||
c.JSON(http.StatusOK, apiResp.LoginRes{
|
||||
AccessToken: ut.Token,
|
||||
@@ -61,13 +81,37 @@ func (l *Login) Login(c *gin.Context) {
|
||||
// @Produce json
|
||||
// @Success 200 {object} []string
|
||||
// @Failure 500 {object} response.ErrorResponse
|
||||
// @Router /login-options [post]
|
||||
// @Router /login-options [get]
|
||||
func (l *Login) LoginOptions(c *gin.Context) {
|
||||
test := []string{
|
||||
//"common-oidc/[{\"name\":\"google\"},{\"name\":\"github\"},{\"name\":\"facebook\"},{\"name\":\"网页授权登录\",\"icon\":\"\"}]",
|
||||
//"oidc/myapp",
|
||||
oauthOks := []string{}
|
||||
err, _ := service.AllService.OauthService.GetOauthConfig(model.OauthTypeGithub)
|
||||
if err == nil {
|
||||
oauthOks = append(oauthOks, model.OauthTypeGithub)
|
||||
}
|
||||
c.JSON(http.StatusOK, test)
|
||||
err, _ = service.AllService.OauthService.GetOauthConfig(model.OauthTypeGoogle)
|
||||
if err == nil {
|
||||
oauthOks = append(oauthOks, model.OauthTypeGoogle)
|
||||
}
|
||||
err, _ = service.AllService.OauthService.GetOauthConfig(model.OauthTypeOidc)
|
||||
if err == nil {
|
||||
oauthOks = append(oauthOks, model.OauthTypeOidc)
|
||||
}
|
||||
oauthOks = append(oauthOks, model.OauthTypeWebauth)
|
||||
var oidcItems []map[string]string
|
||||
for _, v := range oauthOks {
|
||||
oidcItems = append(oidcItems, map[string]string{"name": v})
|
||||
}
|
||||
common, err := json.Marshal(oidcItems)
|
||||
if err != nil {
|
||||
response.Error(c, response.TranslateMsg(c, "SystemError")+err.Error())
|
||||
return
|
||||
}
|
||||
var res []string
|
||||
res = append(res, "common-oidc/"+string(common))
|
||||
for _, v := range oauthOks {
|
||||
res = append(res, "oidc/"+v)
|
||||
}
|
||||
c.JSON(http.StatusOK, res)
|
||||
}
|
||||
|
||||
// Logout
|
||||
|
||||
271
http/controller/api/ouath.go
Normal file
@@ -0,0 +1,271 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"Gwen/global"
|
||||
"Gwen/http/request/api"
|
||||
"Gwen/http/response"
|
||||
apiResp "Gwen/http/response/api"
|
||||
"Gwen/model"
|
||||
"Gwen/service"
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Oauth struct {
|
||||
}
|
||||
|
||||
// OidcAuth
|
||||
// @Tags Oauth
|
||||
// @Summary OidcAuth
|
||||
// @Description OidcAuth
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} apiResp.LoginRes
|
||||
// @Failure 500 {object} response.ErrorResponse
|
||||
// @Router /oidc/auth [post]
|
||||
func (o *Oauth) OidcAuth(c *gin.Context) {
|
||||
f := &api.OidcAuthRequest{}
|
||||
err := c.ShouldBindJSON(&f)
|
||||
if err != nil {
|
||||
response.Error(c, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||
return
|
||||
}
|
||||
//fmt.Println(f)
|
||||
if f.Op != model.OauthTypeWebauth && f.Op != model.OauthTypeGoogle && f.Op != model.OauthTypeGithub && f.Op != model.OauthTypeOidc {
|
||||
response.Error(c, response.TranslateMsg(c, "ParamsError"))
|
||||
return
|
||||
}
|
||||
|
||||
err, code, url := service.AllService.OauthService.BeginAuth(f.Op)
|
||||
if err != nil {
|
||||
response.Error(c, response.TranslateMsg(c, err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
service.AllService.OauthService.SetOauthCache(code, &service.OauthCacheItem{
|
||||
Action: service.OauthActionTypeLogin,
|
||||
Id: f.Id,
|
||||
Op: f.Op,
|
||||
Uuid: f.Uuid,
|
||||
DeviceName: f.DeviceInfo.Name,
|
||||
DeviceOs: f.DeviceInfo.Os,
|
||||
DeviceType: f.DeviceInfo.Type,
|
||||
}, 5*60)
|
||||
//fmt.Println("code url", code, url)
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": code,
|
||||
"url": url,
|
||||
})
|
||||
}
|
||||
|
||||
func (o *Oauth) OidcAuthQueryPre(c *gin.Context) (*model.User, *model.UserToken) {
|
||||
var u *model.User
|
||||
var ut *model.UserToken
|
||||
q := &api.OidcAuthQuery{}
|
||||
|
||||
// 解析查询参数并处理错误
|
||||
if err := c.ShouldBindQuery(q); err != nil {
|
||||
response.Error(c, response.TranslateMsg(c, "ParamsError")+": "+err.Error())
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// 获取 OAuth 缓存
|
||||
v := service.AllService.OauthService.GetOauthCache(q.Code)
|
||||
if v == nil {
|
||||
response.Error(c, response.TranslateMsg(c, "OauthExpired"))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// 如果 UserId 为 0,说明还在授权中
|
||||
if v.UserId == 0 {
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Authorization in progress, please login and bind"})
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// 获取用户信息
|
||||
u = service.AllService.UserService.InfoById(v.UserId)
|
||||
if u == nil {
|
||||
response.Error(c, response.TranslateMsg(c, "UserNotFound"))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// 删除 OAuth 缓存
|
||||
service.AllService.OauthService.DeleteOauthCache(q.Code)
|
||||
|
||||
// 创建登录日志并生成用户令牌
|
||||
ut = service.AllService.UserService.Login(u, &model.LoginLog{
|
||||
UserId: u.Id,
|
||||
Client: v.DeviceType,
|
||||
Uuid: v.Uuid,
|
||||
Ip: c.ClientIP(),
|
||||
Type: model.LoginLogTypeOauth,
|
||||
Platform: v.DeviceOs,
|
||||
})
|
||||
|
||||
if ut == nil {
|
||||
response.Error(c, response.TranslateMsg(c, "LoginFailed"))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// 返回用户令牌
|
||||
return u, ut
|
||||
}
|
||||
|
||||
// OidcAuthQuery
|
||||
// @Tags Oauth
|
||||
// @Summary OidcAuthQuery
|
||||
// @Description OidcAuthQuery
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} apiResp.LoginRes
|
||||
// @Failure 500 {object} response.ErrorResponse
|
||||
// @Router /oidc/auth-query [get]
|
||||
func (o *Oauth) OidcAuthQuery(c *gin.Context) {
|
||||
u, ut := o.OidcAuthQueryPre(c)
|
||||
if u == nil || ut == nil {
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, apiResp.LoginRes{
|
||||
AccessToken: ut.Token,
|
||||
Type: "access_token",
|
||||
User: *(&apiResp.UserPayload{}).FromUser(u),
|
||||
})
|
||||
}
|
||||
|
||||
// OauthCallback 回调
|
||||
// @Tags Oauth
|
||||
// @Summary OauthCallback
|
||||
// @Description OauthCallback
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} apiResp.LoginRes
|
||||
// @Failure 500 {object} response.ErrorResponse
|
||||
// @Router /oauth/callback [get]
|
||||
func (o *Oauth) OauthCallback(c *gin.Context) {
|
||||
state := c.Query("state")
|
||||
if state == "" {
|
||||
c.String(http.StatusInternalServerError, response.TranslateParamMsg(c, "ParamIsEmpty", "state"))
|
||||
return
|
||||
}
|
||||
|
||||
cacheKey := state
|
||||
//从缓存中获取
|
||||
v := service.AllService.OauthService.GetOauthCache(cacheKey)
|
||||
if v == nil {
|
||||
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "OauthExpired"))
|
||||
return
|
||||
}
|
||||
|
||||
ty := v.Op
|
||||
ac := v.Action
|
||||
var u *model.User
|
||||
openid := ""
|
||||
thirdName := ""
|
||||
//fmt.Println("ty ac ", ty, ac)
|
||||
|
||||
if ty == model.OauthTypeGithub {
|
||||
code := c.Query("code")
|
||||
err, userData := service.AllService.OauthService.GithubCallback(code)
|
||||
if err != nil {
|
||||
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "OauthFailed")+response.TranslateMsg(c, err.Error()))
|
||||
return
|
||||
}
|
||||
openid = strconv.Itoa(userData.Id)
|
||||
thirdName = userData.Login
|
||||
} else if ty == model.OauthTypeGoogle {
|
||||
code := c.Query("code")
|
||||
err, userData := service.AllService.OauthService.GoogleCallback(code)
|
||||
if err != nil {
|
||||
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "OauthFailed")+response.TranslateMsg(c, err.Error()))
|
||||
return
|
||||
}
|
||||
openid = userData.Email
|
||||
//将空格替换成_
|
||||
thirdName = strings.Replace(userData.Name, " ", "_", -1)
|
||||
} else if ty == model.OauthTypeOidc {
|
||||
code := c.Query("code")
|
||||
err, userData := service.AllService.OauthService.OidcCallback(code)
|
||||
if err != nil {
|
||||
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "OauthFailed")+response.TranslateMsg(c, err.Error()))
|
||||
return
|
||||
}
|
||||
openid = userData.Sub
|
||||
thirdName = userData.PreferredUsername
|
||||
} else {
|
||||
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "ParamsError"))
|
||||
return
|
||||
}
|
||||
if ac == service.OauthActionTypeBind {
|
||||
|
||||
//fmt.Println("bind", ty, userData)
|
||||
utr := service.AllService.OauthService.UserThirdInfo(ty, openid)
|
||||
if utr.UserId > 0 {
|
||||
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "OauthHasBindOtherUser"))
|
||||
return
|
||||
}
|
||||
//绑定
|
||||
u = service.AllService.UserService.InfoById(v.UserId)
|
||||
if u == nil {
|
||||
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "ItemNotFound"))
|
||||
return
|
||||
}
|
||||
//绑定
|
||||
err := service.AllService.OauthService.BindOauthUser(ty, openid, thirdName, v.UserId)
|
||||
if err != nil {
|
||||
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "BindFail"))
|
||||
return
|
||||
}
|
||||
c.String(http.StatusOK, response.TranslateMsg(c, "BindSuccess"))
|
||||
return
|
||||
|
||||
} else if ac == service.OauthActionTypeLogin {
|
||||
//登录
|
||||
if v.UserId != 0 {
|
||||
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "OauthHasBeenSuccess"))
|
||||
return
|
||||
}
|
||||
u = service.AllService.UserService.InfoByGithubId(openid)
|
||||
if u == nil {
|
||||
oa := service.AllService.OauthService.InfoByOp(ty)
|
||||
if !*oa.AutoRegister {
|
||||
//c.String(http.StatusInternalServerError, "还未绑定用户,请先绑定")
|
||||
v.ThirdName = thirdName
|
||||
v.ThirdOpenId = openid
|
||||
url := global.Config.Rustdesk.ApiServer + "/_admin/#/oauth/bind/" + cacheKey
|
||||
c.Redirect(http.StatusFound, url)
|
||||
return
|
||||
}
|
||||
|
||||
//自动注册
|
||||
u = service.AllService.UserService.RegisterByOauth(ty, thirdName, openid)
|
||||
if u.Id == 0 {
|
||||
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "OauthRegisterFailed"))
|
||||
return
|
||||
}
|
||||
}
|
||||
v.UserId = u.Id
|
||||
service.AllService.OauthService.SetOauthCache(cacheKey, v, 0)
|
||||
// 如果是webadmin,登录成功后跳转到webadmin
|
||||
if v.DeviceType == "webadmin" {
|
||||
/*service.AllService.UserService.Login(u, &model.LoginLog{
|
||||
UserId: u.Id,
|
||||
Client: "webadmin",
|
||||
Uuid: "", //must be empty
|
||||
Ip: c.ClientIP(),
|
||||
Type: model.LoginLogTypeOauth,
|
||||
Platform: v.DeviceOs,
|
||||
})*/
|
||||
url := global.Config.Rustdesk.ApiServer + "/_admin/#/"
|
||||
c.Redirect(http.StatusFound, url)
|
||||
return
|
||||
}
|
||||
c.String(http.StatusOK, response.TranslateMsg(c, "OauthSuccess"))
|
||||
return
|
||||
} else {
|
||||
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "ParamsError"))
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
@@ -22,27 +22,37 @@ type Peer struct {
|
||||
// @Success 200 {string} string "SYSINFO_UPDATED,ID_NOT_FOUND"
|
||||
// @Failure 500 {object} response.ErrorResponse
|
||||
// @Router /sysinfo [post]
|
||||
// @Security BearerAuth
|
||||
func (p *Peer) SysInfo(c *gin.Context) {
|
||||
f := &requstform.PeerForm{}
|
||||
err := c.ShouldBindBodyWith(f, binding.JSON)
|
||||
if err != nil {
|
||||
response.Error(c, err.Error())
|
||||
response.Error(c, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
fpe := f.ToPeer()
|
||||
pe := service.AllService.PeerService.FindById(f.Id)
|
||||
if pe == nil || pe.RowId == 0 {
|
||||
if pe.RowId == 0 {
|
||||
pe = f.ToPeer()
|
||||
pe.UserId = service.AllService.UserService.FindLatestUserIdFromLoginLogByUuid(pe.Uuid)
|
||||
err = service.AllService.PeerService.Create(pe)
|
||||
if err != nil {
|
||||
response.Error(c, err.Error())
|
||||
response.Error(c, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if pe.UserId == 0 {
|
||||
pe.UserId = service.AllService.UserService.FindLatestUserIdFromLoginLogByUuid(pe.Uuid)
|
||||
}
|
||||
fpe.RowId = pe.RowId
|
||||
fpe.UserId = pe.UserId
|
||||
err = service.AllService.PeerService.Update(fpe)
|
||||
if err != nil {
|
||||
response.Error(c, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
//SYSINFO_UPDATED 上传成功
|
||||
//ID_NOT_FOUND 下次心跳会上传
|
||||
//直接响应文本
|
||||
c.String(http.StatusOK, "")
|
||||
c.String(http.StatusOK, "SYSINFO_UPDATED")
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package api
|
||||
import (
|
||||
apiResp "Gwen/http/response/api"
|
||||
"Gwen/service"
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
)
|
||||
@@ -21,11 +20,11 @@ type User struct {
|
||||
// @Failure 500 {object} response.Response
|
||||
// @Router /currentUser [get]
|
||||
// @Security token
|
||||
func (u *User) currentUser(c *gin.Context) {
|
||||
user := service.AllService.UserService.CurUser(c)
|
||||
up := (&apiResp.UserPayload{}).FromUser(user)
|
||||
c.JSON(http.StatusOK, up)
|
||||
}
|
||||
//func (u *User) currentUser(c *gin.Context) {
|
||||
// user := service.AllService.UserService.CurUser(c)
|
||||
// up := (&apiResp.UserPayload{}).FromName(user)
|
||||
// c.JSON(http.StatusOK, up)
|
||||
//}
|
||||
|
||||
// Info 用户信息
|
||||
// @Tags 用户
|
||||
@@ -42,33 +41,3 @@ func (u *User) Info(c *gin.Context) {
|
||||
up := (&apiResp.UserPayload{}).FromUser(user)
|
||||
c.JSON(http.StatusOK, up)
|
||||
}
|
||||
|
||||
// Personal
|
||||
// @Tags 用户
|
||||
// @Summary 个人信息
|
||||
// @Description 个人信息
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param string body string false "string valid"
|
||||
// @Success 200 {object} response.Response
|
||||
// @Failure 500 {object} response.Response
|
||||
// @Router /ab/personal [post]
|
||||
// @Security BearerAuth
|
||||
func (u *User) Personal(c *gin.Context) {
|
||||
//打印全部body
|
||||
fmt.Println(c.Request.Body)
|
||||
|
||||
/**
|
||||
guid = json['guid'] ?? '',
|
||||
name = json['name'] ?? '',
|
||||
owner = json['owner'] ?? '',
|
||||
note = json['note'] ?? '',
|
||||
rule = json['rule'] ?? 0;
|
||||
*/
|
||||
//如果返回了guid,后面的请求会有变化
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
//"guid": "123456",
|
||||
//"name": "admindddd",
|
||||
//"rule": 1,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"Gwen/http/response/api"
|
||||
"Gwen/service"
|
||||
"github.com/gin-gonic/gin"
|
||||
"time"
|
||||
)
|
||||
|
||||
type WebClient struct {
|
||||
@@ -36,7 +37,51 @@ func (i *WebClient) ServerConfig(c *gin.Context) {
|
||||
gin.H{
|
||||
"id_server": global.Config.Rustdesk.IdServer,
|
||||
"key": global.Config.Rustdesk.Key,
|
||||
//"peers": peers,
|
||||
"peers": peers,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// SharedPeer 分享的peer
|
||||
// @Tags WEBCLIENT
|
||||
// @Summary 分享的peer
|
||||
// @Description 分享的peer
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} response.Response
|
||||
// @Failure 500 {object} response.Response
|
||||
// @Router /shared-peer [post]
|
||||
func (i *WebClient) SharedPeer(c *gin.Context) {
|
||||
j := &gin.H{}
|
||||
c.ShouldBindJSON(j)
|
||||
t := (*j)["share_token"].(string)
|
||||
if t == "" {
|
||||
response.Fail(c, 101, "share_token is required")
|
||||
return
|
||||
}
|
||||
sr := service.AllService.AddressBookService.SharedPeer(t)
|
||||
if sr == nil || sr.Id == 0 {
|
||||
response.Fail(c, 101, "share not found")
|
||||
return
|
||||
}
|
||||
//判断是否过期,created_at + expire > now
|
||||
ca := time.Time(sr.CreatedAt)
|
||||
if ca.Add(time.Second * time.Duration(sr.Expire)).Before(time.Now()) {
|
||||
response.Fail(c, 101, "share expired")
|
||||
return
|
||||
}
|
||||
ab := service.AllService.AddressBookService.InfoByUserIdAndId(sr.UserId, sr.PeerId)
|
||||
if ab.RowId == 0 {
|
||||
response.Fail(c, 101, "peer not found")
|
||||
return
|
||||
}
|
||||
pp := &api.WebClientPeerPayload{}
|
||||
pp.FromShareRecord(sr)
|
||||
pp.Info.Username = ab.Username
|
||||
pp.Info.Hostname = ab.Hostname
|
||||
response.Success(c, gin.H{
|
||||
"id_server": global.Config.Rustdesk.IdServer,
|
||||
"key": global.Config.Rustdesk.Key,
|
||||
"peer": pp,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -8,61 +8,15 @@ import (
|
||||
type Index struct {
|
||||
}
|
||||
|
||||
func (i *Index) Index(c *gin.Context) {
|
||||
c.Redirect(302, "/_admin/")
|
||||
}
|
||||
|
||||
func (i *Index) ConfigJs(c *gin.Context) {
|
||||
apiServer := global.Config.Rustdesk.ApiServer
|
||||
|
||||
tmp := `
|
||||
window._gwen = {}
|
||||
window._gwen.kv = {}
|
||||
function getQueryVariable() {
|
||||
const query = window.location.hash.substring(3);
|
||||
const vars = query.split("&");
|
||||
for (var i = 0; i < vars.length; i++) {
|
||||
var pair = vars[i].split("=");
|
||||
window._gwen.kv[pair[0]] = pair[1]
|
||||
}
|
||||
}
|
||||
getQueryVariable()
|
||||
const id = window._gwen.kv.id || ''
|
||||
if (id) {
|
||||
localStorage.setItem('remote-id', id)
|
||||
}
|
||||
window._gwen.hosts = [
|
||||
"rs-sg.rustdesk.com",
|
||||
"rs-cn.rustdesk.com",
|
||||
"rs-us.rustdesk.com",
|
||||
]
|
||||
localStorage.setItem('api-server', "` + apiServer + `")
|
||||
const autoWriteServer = () => {
|
||||
return setTimeout(() => {
|
||||
const token = localStorage.getItem('access_token')
|
||||
const apiserver = localStorage.getItem('api-server')
|
||||
if (token && apiserver) {
|
||||
fetch(apiserver + "/api/server-config", {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer ' + token
|
||||
}
|
||||
}
|
||||
).then(res => res.json()).then(res => {
|
||||
if (res.code === 0) {
|
||||
if(!localStorage.getItem('custom-rendezvous-server') || !localStorage.getItem('key') ) {
|
||||
localStorage.setItem('custom-rendezvous-server', res.data.id_server)
|
||||
localStorage.setItem('key', res.data.key)
|
||||
}
|
||||
|
||||
if (res.data.peers) {
|
||||
localStorage.setItem('peers', JSON.stringify(res.data.peers))
|
||||
}
|
||||
}
|
||||
})
|
||||
} else {
|
||||
autoWriteServer()
|
||||
}
|
||||
}, 1000)
|
||||
}
|
||||
autoWriteServer()
|
||||
`
|
||||
c.String(200, tmp)
|
||||
}
|
||||
|
||||
11
http/http.go
@@ -7,12 +7,23 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func ApiInit() {
|
||||
gin.SetMode(global.Config.Gin.Mode)
|
||||
g := gin.New()
|
||||
|
||||
//[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
|
||||
//Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.
|
||||
if global.Config.Gin.TrustProxy != "" {
|
||||
pro := strings.Split(global.Config.Gin.TrustProxy, ",")
|
||||
err := g.SetTrustedProxies(pro)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
if global.Config.Gin.Mode == gin.ReleaseMode {
|
||||
//修改gin Recovery日志 输出为logger的输出点
|
||||
if global.Logger != nil {
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
|
||||
func RustAuth() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
|
||||
//fmt.Println(c.Request.URL, c.Request.Header)
|
||||
//获取HTTP_AUTHORIZATION
|
||||
token := c.GetHeader("Authorization")
|
||||
if token == "" {
|
||||
@@ -17,6 +17,13 @@ func RustAuth() gin.HandlerFunc {
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
if len(token) <= 7 {
|
||||
c.JSON(401, gin.H{
|
||||
"error": "Unauthorized",
|
||||
})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
//提取token,格式是Bearer {token}
|
||||
//这里只是简单的提取
|
||||
token = token[7:]
|
||||
|
||||
@@ -16,12 +16,14 @@ type AddressBookForm struct {
|
||||
Tags []string `json:"tags"`
|
||||
Hash string `json:"hash"`
|
||||
UserId uint `json:"user_id"`
|
||||
ForceAlwaysRelay bool `json:"force_always_relay"`
|
||||
RdpPort string `json:"rdp_port"`
|
||||
RdpUsername string `json:"rdp_username"`
|
||||
UserIds []uint `json:"user_ids"`
|
||||
ForceAlwaysRelay bool `json:"forceAlwaysRelay"`
|
||||
RdpPort string `json:"rdpPort"`
|
||||
RdpUsername string `json:"rdpUsername"`
|
||||
Online bool `json:"online"`
|
||||
LoginName string `json:"login_name" `
|
||||
SameServer bool `json:"same_server"`
|
||||
LoginName string `json:"loginName" `
|
||||
SameServer bool `json:"sameServer"`
|
||||
CollectionId uint `json:"collection_id"`
|
||||
}
|
||||
|
||||
func (a AddressBookForm) ToAddressBook() *model.AddressBook {
|
||||
@@ -45,12 +47,78 @@ func (a AddressBookForm) ToAddressBook() *model.AddressBook {
|
||||
Online: a.Online,
|
||||
LoginName: a.LoginName,
|
||||
SameServer: a.SameServer,
|
||||
CollectionId: a.CollectionId,
|
||||
}
|
||||
|
||||
}
|
||||
func (a AddressBookForm) ToAddressBooks() []*model.AddressBook {
|
||||
//tags转换
|
||||
tags, _ := json.Marshal(a.Tags)
|
||||
|
||||
abs := make([]*model.AddressBook, 0, len(a.UserIds))
|
||||
for _, userId := range a.UserIds {
|
||||
abs = append(abs, &model.AddressBook{
|
||||
RowId: a.RowId,
|
||||
Id: a.Id,
|
||||
Username: a.Username,
|
||||
Password: a.Password,
|
||||
Hostname: a.Hostname,
|
||||
Alias: a.Alias,
|
||||
Platform: a.Platform,
|
||||
Tags: tags,
|
||||
Hash: a.Hash,
|
||||
UserId: userId,
|
||||
ForceAlwaysRelay: a.ForceAlwaysRelay,
|
||||
RdpPort: a.RdpPort,
|
||||
RdpUsername: a.RdpUsername,
|
||||
Online: a.Online,
|
||||
LoginName: a.LoginName,
|
||||
SameServer: a.SameServer,
|
||||
CollectionId: a.CollectionId,
|
||||
})
|
||||
}
|
||||
return abs
|
||||
}
|
||||
|
||||
type AddressBookQuery struct {
|
||||
UserId int `form:"user_id"`
|
||||
CollectionId *int `form:"collection_id"`
|
||||
IsMy int `form:"is_my"`
|
||||
Username string `form:"username"`
|
||||
Hostname string `form:"hostname"`
|
||||
Id string `form:"id"`
|
||||
PageQuery
|
||||
}
|
||||
|
||||
type ShareByWebClientForm struct {
|
||||
Id string `json:"id" validate:"required"`
|
||||
PasswordType string `json:"password_type" validate:"required,oneof=once fixed"` //只能是once,fixed
|
||||
Password string `json:"password" validate:"required"`
|
||||
Expire int64 `json:"expire"`
|
||||
}
|
||||
|
||||
func (sbwcf ShareByWebClientForm) ToShareRecord() *model.ShareRecord {
|
||||
return &model.ShareRecord{
|
||||
UserId: 0,
|
||||
PeerId: sbwcf.Id,
|
||||
PasswordType: sbwcf.PasswordType,
|
||||
Password: sbwcf.Password,
|
||||
Expire: sbwcf.Expire,
|
||||
}
|
||||
}
|
||||
|
||||
type AddressBookCollectionQuery struct {
|
||||
UserId int `form:"user_id"`
|
||||
IsMy int `form:"is_my"`
|
||||
PageQuery
|
||||
}
|
||||
|
||||
type AddressBookCollectionSimpleListQuery struct {
|
||||
UserIds []uint `form:"user_ids"`
|
||||
}
|
||||
type AddressBookCollectionRuleQuery struct {
|
||||
UserId int `form:"user_id"`
|
||||
CollectionId int `form:"collection_id"`
|
||||
IsMy int `form:"is_my"`
|
||||
PageQuery
|
||||
}
|
||||
|
||||
7
http/request/admin/audit.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package admin
|
||||
|
||||
type AuditQuery struct {
|
||||
PeerId string `form:"peer_id"`
|
||||
FromPeer string `form:"from_peer"`
|
||||
PageQuery
|
||||
}
|
||||
@@ -5,11 +5,13 @@ import "Gwen/model"
|
||||
type GroupForm struct {
|
||||
Id uint `json:"id"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
Type int `json:"type"`
|
||||
}
|
||||
|
||||
func (gf *GroupForm) FromGroup(group *model.Group) *GroupForm {
|
||||
gf.Id = group.Id
|
||||
gf.Name = group.Name
|
||||
gf.Type = group.Type
|
||||
return gf
|
||||
}
|
||||
|
||||
@@ -17,5 +19,6 @@ func (gf *GroupForm) ToGroup() *model.Group {
|
||||
group := &model.Group{}
|
||||
group.Id = gf.Id
|
||||
group.Name = gf.Name
|
||||
group.Type = gf.Type
|
||||
return group
|
||||
}
|
||||
|
||||
@@ -3,4 +3,11 @@ package admin
|
||||
type Login struct {
|
||||
Username string `json:"username" validate:"required" label:"用户名"`
|
||||
Password string `json:"password,omitempty" validate:"required" label:"密码"`
|
||||
Platform string `json:"platform" label:"平台"`
|
||||
}
|
||||
|
||||
type LoginLogQuery struct {
|
||||
UserId int `form:"user_id"`
|
||||
IsMy int `form:"is_my"`
|
||||
PageQuery
|
||||
}
|
||||
|
||||
38
http/request/admin/oauth.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package admin
|
||||
|
||||
import "Gwen/model"
|
||||
|
||||
type BindOauthForm struct {
|
||||
Op string `json:"op" binding:"required"`
|
||||
}
|
||||
|
||||
type OauthConfirmForm struct {
|
||||
Code string `json:"code" binding:"required"`
|
||||
}
|
||||
type UnBindOauthForm struct {
|
||||
Op string `json:"op" binding:"required"`
|
||||
}
|
||||
type OauthForm struct {
|
||||
Id uint `json:"id"`
|
||||
Op string `json:"op" validate:"required"`
|
||||
Issuer string `json:"issuer" validate:"omitempty,url"`
|
||||
Scopes string `json:"scopes" validate:"omitempty"`
|
||||
ClientId string `json:"client_id" validate:"required"`
|
||||
ClientSecret string `json:"client_secret" validate:"required"`
|
||||
RedirectUrl string `json:"redirect_url" validate:"required"`
|
||||
AutoRegister *bool `json:"auto_register"`
|
||||
}
|
||||
|
||||
func (of *OauthForm) ToOauth() *model.Oauth {
|
||||
oa := &model.Oauth{
|
||||
Op: of.Op,
|
||||
ClientId: of.ClientId,
|
||||
ClientSecret: of.ClientSecret,
|
||||
RedirectUrl: of.RedirectUrl,
|
||||
AutoRegister: of.AutoRegister,
|
||||
Issuer: of.Issuer,
|
||||
Scopes: of.Scopes,
|
||||
}
|
||||
oa.Id = of.Id
|
||||
return oa
|
||||
}
|
||||
@@ -14,6 +14,10 @@ type PeerForm struct {
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
type PeerBatchDeleteForm struct {
|
||||
RowIds []uint `json:"row_ids" validate:"required"`
|
||||
}
|
||||
|
||||
// ToPeer
|
||||
func (f *PeerForm) ToPeer() *model.Peer {
|
||||
return &model.Peer{
|
||||
@@ -28,3 +32,14 @@ func (f *PeerForm) ToPeer() *model.Peer {
|
||||
Version: f.Version,
|
||||
}
|
||||
}
|
||||
|
||||
type PeerQuery struct {
|
||||
PageQuery
|
||||
TimeAgo int `json:"time_ago" form:"time_ago"`
|
||||
Id string `json:"id" form:"id"`
|
||||
Hostname string `json:"hostname" form:"hostname"`
|
||||
}
|
||||
|
||||
type SimpleDataQuery struct {
|
||||
Ids []string `json:"ids" form:"ids"`
|
||||
}
|
||||
|
||||
@@ -3,10 +3,11 @@ package admin
|
||||
import "Gwen/model"
|
||||
|
||||
type TagForm struct {
|
||||
Id uint `json:"id"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
Color uint `json:"color" validate:"required"`
|
||||
UserId uint `json:"user_id" validate:"required"`
|
||||
Id uint `json:"id"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
Color uint `json:"color" validate:"required"`
|
||||
UserId uint `json:"user_id"`
|
||||
CollectionId uint `json:"collection_id"`
|
||||
}
|
||||
|
||||
func (f *TagForm) FromTag(group *model.Tag) *TagForm {
|
||||
@@ -14,6 +15,7 @@ func (f *TagForm) FromTag(group *model.Tag) *TagForm {
|
||||
f.Name = group.Name
|
||||
f.Color = group.Color
|
||||
f.UserId = group.UserId
|
||||
f.CollectionId = group.CollectionId
|
||||
return f
|
||||
}
|
||||
|
||||
@@ -23,11 +25,13 @@ func (f *TagForm) ToTag() *model.Tag {
|
||||
i.Name = f.Name
|
||||
i.Color = f.Color
|
||||
i.UserId = f.UserId
|
||||
i.CollectionId = f.CollectionId
|
||||
return i
|
||||
}
|
||||
|
||||
type TagQuery struct {
|
||||
UserId int `form:"user_id"`
|
||||
IsMy int `form:"is_my"`
|
||||
UserId int `form:"user_id"`
|
||||
IsMy int `form:"is_my"`
|
||||
CollectionId *int `form:"collection_id"`
|
||||
PageQuery
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ type UserForm struct {
|
||||
Id uint `json:"id"`
|
||||
Username string `json:"username" validate:"required,gte=4,lte=10"`
|
||||
//Password string `json:"password" validate:"required,gte=4,lte=20"`
|
||||
Nickname string `json:"nickname" validate:"required"`
|
||||
Nickname string `json:"nickname"`
|
||||
Avatar string `json:"avatar"`
|
||||
GroupId uint `json:"group_id" validate:"required"`
|
||||
IsAdmin *bool `json:"is_admin" `
|
||||
@@ -55,3 +55,7 @@ type ChangeCurPasswordForm struct {
|
||||
OldPassword string `json:"old_password" validate:"required,gte=4,lte=20"`
|
||||
NewPassword string `json:"new_password" validate:"required,gte=4,lte=20"`
|
||||
}
|
||||
type GroupUsersQuery struct {
|
||||
IsMy int `json:"is_my"`
|
||||
UserId uint `json:"user_id"`
|
||||
}
|
||||
|
||||
78
http/request/api/audit.go
Normal file
@@ -0,0 +1,78 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"Gwen/global"
|
||||
"Gwen/model"
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type AuditConnForm struct {
|
||||
Action string `json:"action"`
|
||||
ConnId int64 `json:"conn_id"`
|
||||
Id string `json:"id"`
|
||||
Peer []string `json:"peer"`
|
||||
Ip string `json:"ip"`
|
||||
SessionId float64 `json:"session_id"`
|
||||
Type int `json:"type"`
|
||||
Uuid string `json:"uuid"`
|
||||
}
|
||||
|
||||
func (a *AuditConnForm) ToAuditConn() *model.AuditConn {
|
||||
fp := ""
|
||||
fn := ""
|
||||
if len(a.Peer) >= 1 {
|
||||
fp = a.Peer[0]
|
||||
if len(a.Peer) == 2 {
|
||||
fn = a.Peer[1]
|
||||
}
|
||||
}
|
||||
ssid := strconv.FormatFloat(a.SessionId, 'f', -1, 64)
|
||||
return &model.AuditConn{
|
||||
Action: a.Action,
|
||||
ConnId: a.ConnId,
|
||||
PeerId: a.Id,
|
||||
FromPeer: fp,
|
||||
FromName: fn,
|
||||
Ip: a.Ip,
|
||||
SessionId: ssid,
|
||||
Type: a.Type,
|
||||
Uuid: a.Uuid,
|
||||
}
|
||||
}
|
||||
|
||||
type AuditFileForm struct {
|
||||
Id string `json:"id"`
|
||||
Info string `json:"info"`
|
||||
IsFile bool `json:"is_file"`
|
||||
Path string `json:"path"`
|
||||
PeerId string `json:"peer_id"`
|
||||
Type int `json:"type"`
|
||||
Uuid string `json:"uuid"`
|
||||
}
|
||||
type AuditFileInfo struct {
|
||||
Ip string `json:"ip"`
|
||||
Name string `json:"name"`
|
||||
Num int `json:"num"`
|
||||
}
|
||||
|
||||
func (a *AuditFileForm) ToAuditFile() *model.AuditFile {
|
||||
fi := &AuditFileInfo{}
|
||||
err := json.Unmarshal([]byte(a.Info), fi)
|
||||
if err != nil {
|
||||
global.Logger.Warn("ToAuditFile", err)
|
||||
}
|
||||
|
||||
return &model.AuditFile{
|
||||
PeerId: a.Id,
|
||||
Info: a.Info,
|
||||
IsFile: a.IsFile,
|
||||
FromPeer: a.PeerId,
|
||||
Path: a.Path,
|
||||
Type: a.Type,
|
||||
Uuid: a.Uuid,
|
||||
FromName: fi.Name,
|
||||
Ip: fi.Ip,
|
||||
Num: fi.Num,
|
||||
}
|
||||
}
|
||||
14
http/request/api/oauth.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package api
|
||||
|
||||
type OidcAuthRequest struct {
|
||||
DeviceInfo DeviceInfoInLogin `json:"deviceInfo" label:"设备信息"`
|
||||
Id string `json:"id" label:"id"`
|
||||
Op string `json:"op" label:"op"`
|
||||
Uuid string `json:"uuid" label:"uuid"`
|
||||
}
|
||||
|
||||
type OidcAuthQuery struct {
|
||||
Code string `json:"code" form:"code" label:"code"`
|
||||
Id string `json:"id" form:"id" label:"id"`
|
||||
Uuid string `json:"uuid" form:"uuid" label:"uuid"`
|
||||
}
|
||||
@@ -35,3 +35,45 @@ func (pf *PeerForm) ToPeer() *model.Peer {
|
||||
Version: pf.Version,
|
||||
}
|
||||
}
|
||||
|
||||
// PersonalAddressBookForm 个人地址簿表单
|
||||
type PersonalAddressBookForm struct {
|
||||
model.AddressBook
|
||||
ForceAlwaysRelay string `json:"forceAlwaysRelay"`
|
||||
}
|
||||
|
||||
func (pabf *PersonalAddressBookForm) ToAddressBook() *model.AddressBook {
|
||||
return &model.AddressBook{
|
||||
RowId: pabf.RowId,
|
||||
Id: pabf.Id,
|
||||
Username: pabf.Username,
|
||||
Password: pabf.Password,
|
||||
Hostname: pabf.Hostname,
|
||||
Alias: pabf.Alias,
|
||||
Platform: pabf.Platform,
|
||||
Tags: pabf.Tags,
|
||||
Hash: pabf.Hash,
|
||||
UserId: pabf.UserId,
|
||||
ForceAlwaysRelay: pabf.ForceAlwaysRelay == "true",
|
||||
RdpPort: pabf.RdpPort,
|
||||
RdpUsername: pabf.RdpUsername,
|
||||
Online: pabf.Online,
|
||||
LoginName: pabf.LoginName,
|
||||
SameServer: pabf.SameServer,
|
||||
}
|
||||
}
|
||||
|
||||
type TagRenameForm struct {
|
||||
Old string `json:"old"`
|
||||
New string `json:"new"`
|
||||
}
|
||||
type TagColorForm struct {
|
||||
Name string `json:"name"`
|
||||
Color uint `json:"color"`
|
||||
}
|
||||
|
||||
type PeerInfoInHeartbeat struct {
|
||||
Id string `json:"id"`
|
||||
Uuid string `json:"uuid"`
|
||||
Ver int `json:"ver"`
|
||||
}
|
||||
|
||||
@@ -21,9 +21,21 @@ package api
|
||||
bytes hwid = 14;
|
||||
}
|
||||
*/
|
||||
|
||||
type DeviceInfoInLogin struct {
|
||||
Name string `json:"name" label:"name"`
|
||||
Os string `json:"os" label:"os"`
|
||||
Type string `json:"type" label:"type"`
|
||||
}
|
||||
|
||||
type LoginForm struct {
|
||||
Username string `json:"username" validate:"required,gte=4,lte=10" label:"用户名"`
|
||||
Password string `json:"password,omitempty" validate:"gte=4,lte=20" label:"密码"`
|
||||
AutoLogin bool `json:"autoLogin" label:"自动登录"`
|
||||
DeviceInfo DeviceInfoInLogin `json:"deviceInfo" label:"设备信息"`
|
||||
Id string `json:"id" label:"id"`
|
||||
Type string `json:"type" label:"type"`
|
||||
Uuid string `json:"uuid" label:"uuid"`
|
||||
Username string `json:"username" validate:"required,gte=4,lte=10" label:"用户名"`
|
||||
Password string `json:"password,omitempty" validate:"gte=4,lte=20" label:"密码"`
|
||||
}
|
||||
|
||||
type UserListQuery struct {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package admin
|
||||
|
||||
import "Gwen/model"
|
||||
|
||||
type LoginPayload struct {
|
||||
Username string `json:"username"`
|
||||
Token string `json:"token"`
|
||||
@@ -8,6 +10,23 @@ type LoginPayload struct {
|
||||
}
|
||||
|
||||
var UserRouteNames = []string{
|
||||
"MyTagList", "MyAddressBookList",
|
||||
"MyTagList", "MyAddressBookList", "MyInfo", "MyAddressBookCollection",
|
||||
}
|
||||
var AdminRouteNames = []string{"*"}
|
||||
|
||||
type UserOauthItem struct {
|
||||
ThirdType string `json:"third_type"`
|
||||
Status int `json:"status"`
|
||||
}
|
||||
|
||||
type GroupUsersPayload struct {
|
||||
Id uint `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Status int `json:"status"`
|
||||
}
|
||||
|
||||
func (g *GroupUsersPayload) FromUser(user *model.User) {
|
||||
g.Id = user.Id
|
||||
g.Username = user.Username
|
||||
g.Status = 1
|
||||
}
|
||||
|
||||
@@ -7,3 +7,11 @@ type AbList struct {
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
TagColors string `json:"tag_colors,omitempty"`
|
||||
}
|
||||
|
||||
type SharedProfilesPayload struct {
|
||||
Guid string `json:"guid"`
|
||||
Name string `json:"name"`
|
||||
Owner string `json:"owner"`
|
||||
Note string `json:"note"`
|
||||
Rule int `json:"rule"`
|
||||
}
|
||||
|
||||
@@ -59,16 +59,13 @@ func (gpp *GroupPeerPayload) FromAddressBook(a *model.AddressBook, username stri
|
||||
gpp.UserName = username
|
||||
}
|
||||
|
||||
//func (gpp *GroupPeerPayload) FromPeer(p *model.Peer) {
|
||||
// gpp.Id = p.Id
|
||||
// gpp.Info = &PeerPayloadInfo{
|
||||
// DeviceName: p.Hostname,
|
||||
// Os: p.Os,
|
||||
// Username: p.Username,
|
||||
// }
|
||||
// gpp.Note = ""
|
||||
// if p.User.Id != 0 {
|
||||
// //gpp.User = p.User.Username
|
||||
// gpp.UserName = p.User.Username
|
||||
// }
|
||||
//}
|
||||
func (gpp *GroupPeerPayload) FromPeer(p *model.Peer, username string) {
|
||||
gpp.Id = p.Id
|
||||
gpp.Info = &PeerPayloadInfo{
|
||||
DeviceName: p.Hostname,
|
||||
Os: p.Os,
|
||||
Username: p.Username,
|
||||
}
|
||||
gpp.Note = ""
|
||||
gpp.UserName = username
|
||||
}
|
||||
|
||||
@@ -19,17 +19,19 @@ UserStatus status;
|
||||
bool isAdmin = false;
|
||||
*/
|
||||
type UserPayload struct {
|
||||
Name string `json:"name"`
|
||||
Email string `json:"email"`
|
||||
Note string `json:"note"`
|
||||
IsAdmin *bool `json:"is_admin"`
|
||||
Status int `json:"status"`
|
||||
Name string `json:"name"`
|
||||
Email string `json:"email"`
|
||||
Note string `json:"note"`
|
||||
IsAdmin *bool `json:"is_admin"`
|
||||
Status int `json:"status"`
|
||||
Info map[string]interface{} `json:"info"`
|
||||
}
|
||||
|
||||
func (up *UserPayload) FromUser(user *model.User) *UserPayload {
|
||||
up.Name = user.Username
|
||||
up.IsAdmin = user.IsAdmin
|
||||
up.Status = int(user.Status)
|
||||
up.Info = map[string]interface{}{}
|
||||
return up
|
||||
}
|
||||
|
||||
@@ -50,6 +52,6 @@ type LoginRes struct {
|
||||
Type string `json:"type"`
|
||||
AccessToken string `json:"access_token"`
|
||||
User UserPayload `json:"user"`
|
||||
Secret string `json:"secret"`
|
||||
TfaType string `json:"tfa_type"`
|
||||
Secret string `json:"secret,omitempty"`
|
||||
TfaType string `json:"tfa_type,omitempty"`
|
||||
}
|
||||
|
||||
@@ -5,51 +5,42 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// type T struct {
|
||||
// Field1 struct {
|
||||
// ViewStyle string `json:"view-style"`
|
||||
// Tm int64 `json:"tm"`
|
||||
// Info struct {
|
||||
// Username string `json:"username"`
|
||||
// Hostname string `json:"hostname"`
|
||||
// Platform string `json:"platform"`
|
||||
// Displays []struct {
|
||||
// X int `json:"x"`
|
||||
// Y int `json:"y"`
|
||||
// Width int `json:"width"`
|
||||
// Height int `json:"height"`
|
||||
// Name string `json:"name"`
|
||||
// Online bool `json:"online"`
|
||||
// } `json:"displays"`
|
||||
// CurrentDisplay int `json:"current_display"`
|
||||
// SasEnabled bool `json:"sas_enabled"`
|
||||
// Version string `json:"version"`
|
||||
// ConnId int `json:"conn_id"`
|
||||
// Features struct {
|
||||
// PrivacyMode bool `json:"privacy_mode"`
|
||||
// } `json:"features"`
|
||||
// } `json:"info"`
|
||||
// } `json:"1799928825"`
|
||||
// }
|
||||
|
||||
type WebClientPeerPayload struct {
|
||||
ViewStyle string `json:"view-style"`
|
||||
Tm int64 `json:"tm"`
|
||||
Info WebClientPeerInfoPayload `json:"info"`
|
||||
Tmppwd string `json:"tmppwd"`
|
||||
}
|
||||
|
||||
type WebClientPeerInfoPayload struct {
|
||||
Username string `json:"username"`
|
||||
Hostname string `json:"hostname"`
|
||||
Platform string `json:"platform"`
|
||||
Hash string `json:"hash"`
|
||||
Id string `json:"id"`
|
||||
}
|
||||
|
||||
func (wcpp *WebClientPeerPayload) FromAddressBook(a *model.AddressBook) {
|
||||
wcpp.ViewStyle = "shrink"
|
||||
wcpp.Tm = time.Now().UnixNano()
|
||||
//24小时前
|
||||
wcpp.Tm = time.Now().Add(-time.Hour * 24).UnixNano()
|
||||
wcpp.Info = WebClientPeerInfoPayload{
|
||||
Username: a.Username,
|
||||
Hostname: a.Hostname,
|
||||
Platform: a.Platform,
|
||||
Hash: a.Hash,
|
||||
}
|
||||
}
|
||||
|
||||
func (wcpp *WebClientPeerPayload) FromShareRecord(sr *model.ShareRecord) {
|
||||
wcpp.ViewStyle = "shrink"
|
||||
//24小时前
|
||||
wcpp.Tm = time.Now().UnixNano()
|
||||
wcpp.Tmppwd = sr.Password
|
||||
wcpp.Info = WebClientPeerInfoPayload{
|
||||
Username: "",
|
||||
Hostname: "",
|
||||
Platform: "",
|
||||
Id: sr.PeerId,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
package response
|
||||
|
||||
import (
|
||||
"Gwen/global"
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/nicksnyder/go-i18n/v2/i18n"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
@@ -51,3 +54,48 @@ type ServerConfigResponse struct {
|
||||
RelayServer string `json:"relay_server"`
|
||||
ApiServer string `json:"api_server"`
|
||||
}
|
||||
|
||||
func TranslateMsg(c *gin.Context, messageId string) string {
|
||||
localizer := global.Localizer(c.GetHeader("Accept-Language"))
|
||||
errMsg, err := localizer.LocalizeMessage(&i18n.Message{
|
||||
ID: messageId,
|
||||
})
|
||||
if err != nil {
|
||||
global.Logger.Warn("LocalizeMessage Error: " + err.Error())
|
||||
errMsg = messageId
|
||||
}
|
||||
return errMsg
|
||||
}
|
||||
func TranslateTempMsg(c *gin.Context, messageId string, templateData map[string]interface{}) string {
|
||||
localizer := global.Localizer(c.GetHeader("Accept-Language"))
|
||||
errMsg, err := localizer.Localize(&i18n.LocalizeConfig{
|
||||
DefaultMessage: &i18n.Message{
|
||||
ID: messageId,
|
||||
},
|
||||
TemplateData: templateData,
|
||||
})
|
||||
if err != nil {
|
||||
global.Logger.Warn("LocalizeMessage Error: " + err.Error())
|
||||
errMsg = messageId
|
||||
}
|
||||
return errMsg
|
||||
}
|
||||
func TranslateParamMsg(c *gin.Context, messageId string, params ...string) string {
|
||||
localizer := global.Localizer(c.GetHeader("Accept-Language"))
|
||||
templateData := make(map[string]interface{})
|
||||
for i, v := range params {
|
||||
k := fmt.Sprintf("P%d", i)
|
||||
templateData[k] = v
|
||||
}
|
||||
errMsg, err := localizer.Localize(&i18n.LocalizeConfig{
|
||||
DefaultMessage: &i18n.Message{
|
||||
ID: messageId,
|
||||
},
|
||||
TemplateData: templateData,
|
||||
})
|
||||
if err != nil {
|
||||
global.Logger.Warn("LocalizeMessage Error: " + err.Error())
|
||||
errMsg = messageId
|
||||
}
|
||||
return errMsg
|
||||
}
|
||||
|
||||
@@ -25,10 +25,14 @@ func Init(g *gin.Engine) {
|
||||
TagBind(adg)
|
||||
AddressBookBind(adg)
|
||||
PeerBind(adg)
|
||||
|
||||
OauthBind(adg)
|
||||
LoginLogBind(adg)
|
||||
AuditBind(adg)
|
||||
AddressBookCollectionBind(adg)
|
||||
AddressBookCollectionRuleBind(adg)
|
||||
rs := &admin.Rustdesk{}
|
||||
adg.GET("/server-config", rs.ServerConfig)
|
||||
|
||||
adg.GET("/app-config", rs.AppConfig)
|
||||
//访问静态文件
|
||||
//g.StaticFS("/upload", http.Dir(global.Config.Gin.ResourcesPath+"/upload"))
|
||||
}
|
||||
@@ -36,6 +40,9 @@ func LoginBind(rg *gin.RouterGroup) {
|
||||
cont := &admin.Login{}
|
||||
rg.POST("/login", cont.Login)
|
||||
rg.POST("/logout", cont.Logout)
|
||||
rg.GET("/login-options", cont.LoginOptions)
|
||||
rg.POST("/oidc/auth", cont.OidcAuth)
|
||||
rg.GET("/oidc/auth-query", cont.OidcAuthQuery)
|
||||
}
|
||||
|
||||
func UserBind(rg *gin.RouterGroup) {
|
||||
@@ -44,6 +51,8 @@ func UserBind(rg *gin.RouterGroup) {
|
||||
cont := &admin.User{}
|
||||
aR.GET("/current", cont.Current)
|
||||
aR.POST("/changeCurPwd", cont.ChangeCurPwd)
|
||||
aR.POST("/myOauth", cont.MyOauth)
|
||||
aR.POST("/groupUsers", cont.GroupUsers)
|
||||
}
|
||||
aRP := rg.Group("/user").Use(middleware.AdminPrivilege())
|
||||
{
|
||||
@@ -90,6 +99,10 @@ func AddressBookBind(rg *gin.RouterGroup) {
|
||||
aR.POST("/create", cont.Create)
|
||||
aR.POST("/update", cont.Update)
|
||||
aR.POST("/delete", cont.Delete)
|
||||
aR.POST("/shareByWebClient", cont.ShareByWebClient)
|
||||
|
||||
arp := aR.Use(middleware.AdminPrivilege())
|
||||
arp.POST("/batchCreate", cont.BatchCreate)
|
||||
}
|
||||
}
|
||||
func PeerBind(rg *gin.RouterGroup) {
|
||||
@@ -101,6 +114,71 @@ func PeerBind(rg *gin.RouterGroup) {
|
||||
aR.POST("/create", cont.Create)
|
||||
aR.POST("/update", cont.Update)
|
||||
aR.POST("/delete", cont.Delete)
|
||||
aR.POST("/simpleData", cont.SimpleData)
|
||||
|
||||
arp := aR.Use(middleware.AdminPrivilege())
|
||||
arp.POST("/batchDelete", cont.BatchDelete)
|
||||
}
|
||||
}
|
||||
|
||||
func OauthBind(rg *gin.RouterGroup) {
|
||||
aR := rg.Group("/oauth")
|
||||
{
|
||||
cont := &admin.Oauth{}
|
||||
aR.POST("/confirm", cont.Confirm)
|
||||
aR.POST("/bind", cont.ToBind)
|
||||
aR.POST("/bindConfirm", cont.BindConfirm)
|
||||
aR.POST("/unbind", cont.Unbind)
|
||||
aR.GET("/info", cont.Info)
|
||||
}
|
||||
arp := aR.Use(middleware.AdminPrivilege())
|
||||
{
|
||||
cont := &admin.Oauth{}
|
||||
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)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
func LoginLogBind(rg *gin.RouterGroup) {
|
||||
aR := rg.Group("/login_log")
|
||||
cont := &admin.LoginLog{}
|
||||
aR.GET("/list", cont.List)
|
||||
aR.POST("/delete", cont.Delete)
|
||||
}
|
||||
func AuditBind(rg *gin.RouterGroup) {
|
||||
cont := &admin.Audit{}
|
||||
aR := rg.Group("/audit_conn").Use(middleware.AdminPrivilege())
|
||||
aR.GET("/list", cont.ConnList)
|
||||
aR.POST("/delete", cont.ConnDelete)
|
||||
afR := rg.Group("/audit_file").Use(middleware.AdminPrivilege())
|
||||
afR.GET("/list", cont.FileList)
|
||||
afR.POST("/delete", cont.FileDelete)
|
||||
}
|
||||
func AddressBookCollectionBind(rg *gin.RouterGroup) {
|
||||
aR := rg.Group("/address_book_collection")
|
||||
{
|
||||
cont := &admin.AddressBookCollection{}
|
||||
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)
|
||||
}
|
||||
|
||||
}
|
||||
func AddressBookCollectionRuleBind(rg *gin.RouterGroup) {
|
||||
aR := rg.Group("/address_book_collection_rule")
|
||||
{
|
||||
cont := &admin.AddressBookCollectionRule{}
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,9 +19,6 @@ func ApiInit(g *gin.Engine) {
|
||||
|
||||
frg := g.Group("/api")
|
||||
|
||||
frg.Use(middleware.Cors())
|
||||
frg.OPTIONS("/*any", nil)
|
||||
|
||||
i := &api.Index{}
|
||||
frg.GET("/", i.Index)
|
||||
|
||||
@@ -34,17 +31,31 @@ func ApiInit(g *gin.Engine) {
|
||||
frg.POST("/login", l.Login)
|
||||
|
||||
}
|
||||
{
|
||||
o := &api.Oauth{}
|
||||
// [method:POST] [uri:/api/oidc/auth]
|
||||
frg.POST("/oidc/auth", o.OidcAuth)
|
||||
// [method:GET] [uri:/api/oidc/auth-query?code=abc&id=xxxxx&uuid=xxxxx]
|
||||
frg.GET("/oidc/auth-query", o.OidcAuthQuery)
|
||||
//api/oauth/callback
|
||||
frg.GET("/oauth/callback", o.OauthCallback)
|
||||
frg.GET("/oauth/login", o.OauthCallback)
|
||||
}
|
||||
{
|
||||
pe := &api.Peer{}
|
||||
//提交系统信息
|
||||
frg.POST("/sysinfo", pe.SysInfo)
|
||||
}
|
||||
frg.Use(middleware.RustAuth())
|
||||
{
|
||||
w := &api.WebClient{}
|
||||
frg.POST("/server-config", w.ServerConfig)
|
||||
}
|
||||
|
||||
if global.Config.App.WebClient == 1 {
|
||||
WebClientRoutes(frg)
|
||||
}
|
||||
au := &api.Audit{}
|
||||
//[method:POST] [uri:/api/audit/conn]
|
||||
frg.POST("/audit/conn", au.AuditConn)
|
||||
//[method:POST] [uri:/api/audit/file]
|
||||
frg.POST("/audit/file", au.AuditFile)
|
||||
frg.Use(middleware.RustAuth())
|
||||
{
|
||||
u := &api.User{}
|
||||
frg.GET("/user/info", u.Info)
|
||||
@@ -67,7 +78,49 @@ func ApiInit(g *gin.Engine) {
|
||||
//更新地址
|
||||
frg.POST("/ab", ab.UpAb)
|
||||
}
|
||||
|
||||
PersonalRoutes(frg)
|
||||
//访问静态文件
|
||||
g.StaticFS("/upload", http.Dir(global.Config.Gin.ResourcesPath+"/public/upload"))
|
||||
}
|
||||
|
||||
func PersonalRoutes(frg *gin.RouterGroup) {
|
||||
{
|
||||
ab := &api.Ab{}
|
||||
frg.POST("/ab/personal", ab.Personal)
|
||||
//[method:POST] [uri:/api/ab/settings] Request
|
||||
frg.POST("/ab/settings", ab.Settings)
|
||||
// [method:POST] [uri:/api/ab/shared/profiles?current=1&pageSize=100]
|
||||
frg.POST("/ab/shared/profiles", ab.SharedProfiles)
|
||||
//[method:POST] [uri:/api/ab/peers?current=1&pageSize=100&ab=1]
|
||||
frg.POST("/ab/peers", ab.Peers)
|
||||
// [method:POST] [uri:/api/ab/tags/1]
|
||||
frg.POST("/ab/tags/:guid", ab.PTags)
|
||||
//[method:POST] api/ab/peer/add/1
|
||||
frg.POST("/ab/peer/add/:guid", ab.PeerAdd)
|
||||
//[method:DELETE] [uri:/api/ab/peer/1]
|
||||
frg.DELETE("/ab/peer/:guid", ab.PeerDel)
|
||||
//[method:PUT] [uri:/api/ab/peer/update/1]
|
||||
frg.PUT("/ab/peer/update/:guid", ab.PeerUpdate)
|
||||
//[method:POST] [uri:/api/ab/tag/add/1]
|
||||
frg.POST("/ab/tag/add/:guid", ab.TagAdd)
|
||||
//[method:PUT] [uri:/api/ab/tag/rename/1]
|
||||
frg.PUT("/ab/tag/rename/:guid", ab.TagRename)
|
||||
//[method:PUT] [uri:/api/ab/tag/update/1]
|
||||
frg.PUT("/ab/tag/update/:guid", ab.TagUpdate)
|
||||
//[method:DELETE] [uri:/api/ab/tag/1]
|
||||
frg.DELETE("/ab/tag/:guid", ab.TagDel)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func WebClientRoutes(frg *gin.RouterGroup) {
|
||||
w := &api.WebClient{}
|
||||
{
|
||||
frg.POST("/shared-peer", w.SharedPeer)
|
||||
}
|
||||
{
|
||||
frg.POST("/server-config", middleware.RustAuth(), w.ServerConfig)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -9,7 +9,15 @@ import (
|
||||
|
||||
func WebInit(g *gin.Engine) {
|
||||
i := &web.Index{}
|
||||
g.GET("/webclient-config/index.js", i.ConfigJs)
|
||||
g.StaticFS("/webclient", http.Dir(global.Config.Gin.ResourcesPath+"/web"))
|
||||
g.GET("/", i.Index)
|
||||
|
||||
if global.Config.App.WebClient == 1 {
|
||||
g.GET("/webclient-config/index.js", i.ConfigJs)
|
||||
}
|
||||
|
||||
if global.Config.App.WebClient == 1 {
|
||||
g.StaticFS("/webclient", http.Dir(global.Config.Gin.ResourcesPath+"/web"))
|
||||
g.StaticFS("/webclient2", http.Dir(global.Config.Gin.ResourcesPath+"/web2"))
|
||||
}
|
||||
g.StaticFS("/_admin", http.Dir(global.Config.Gin.ResourcesPath+"/admin"))
|
||||
}
|
||||
|
||||
@@ -51,30 +51,22 @@ func TestLocal_GetLock(t *testing.T) {
|
||||
func TestLocal_Lock(t *testing.T) {
|
||||
l := NewLocal()
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(3)
|
||||
m := 10
|
||||
wg.Add(m)
|
||||
i := 0
|
||||
go func() {
|
||||
l.Lock("key")
|
||||
fmt.Println("l1", i)
|
||||
i++
|
||||
l.UnLock("key")
|
||||
wg.Done()
|
||||
}()
|
||||
go func() {
|
||||
l.Lock("key")
|
||||
fmt.Println("l2", i)
|
||||
i++
|
||||
l.UnLock("key")
|
||||
wg.Done()
|
||||
}()
|
||||
go func() {
|
||||
l.Lock("key")
|
||||
fmt.Println("l3", i)
|
||||
i++
|
||||
l.UnLock("key")
|
||||
wg.Done()
|
||||
}()
|
||||
for j := 0; j < m; j++ {
|
||||
go func() {
|
||||
l.Lock("key")
|
||||
//fmt.Println(j, i)
|
||||
i++
|
||||
fmt.Println(j, i)
|
||||
l.UnLock("key")
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
fmt.Println(i)
|
||||
|
||||
}
|
||||
func TestSyncMap(t *testing.T) {
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
package orm
|
||||
|
||||
import (
|
||||
"Gwen/global"
|
||||
"fmt"
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/logger"
|
||||
"time"
|
||||
)
|
||||
|
||||
type MysqlConfig struct {
|
||||
@@ -22,6 +25,16 @@ func NewMysql(mysqlConf *MysqlConfig) *gorm.DB {
|
||||
//SkipInitializeWithVersion: false, // 根据当前 MySQL 版本自动配置
|
||||
}), &gorm.Config{
|
||||
DisableForeignKeyConstraintWhenMigrating: true,
|
||||
Logger: logger.New(
|
||||
global.Logger, // io writer
|
||||
logger.Config{
|
||||
SlowThreshold: time.Second, // Slow SQL threshold
|
||||
LogLevel: logger.Warn, // Log level
|
||||
IgnoreRecordNotFoundError: true, // Ignore ErrRecordNotFound error for logger
|
||||
ParameterizedQueries: true, // Don't include params in the SQL log
|
||||
Colorful: true,
|
||||
},
|
||||
),
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
package orm
|
||||
|
||||
import (
|
||||
"Gwen/global"
|
||||
"fmt"
|
||||
"gorm.io/driver/sqlite"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/logger"
|
||||
"time"
|
||||
)
|
||||
|
||||
type SqliteConfig struct {
|
||||
@@ -12,7 +15,19 @@ type SqliteConfig struct {
|
||||
}
|
||||
|
||||
func NewSqlite(sqliteConf *SqliteConfig) *gorm.DB {
|
||||
db, err := gorm.Open(sqlite.Open("./data/rustdeskapi.db"), &gorm.Config{})
|
||||
db, err := gorm.Open(sqlite.Open("./data/rustdeskapi.db"), &gorm.Config{
|
||||
DisableForeignKeyConstraintWhenMigrating: true,
|
||||
Logger: logger.New(
|
||||
global.Logger, // io writer
|
||||
logger.Config{
|
||||
SlowThreshold: time.Second, // Slow SQL threshold
|
||||
LogLevel: logger.Warn, // Log level
|
||||
IgnoreRecordNotFoundError: true, // Ignore ErrRecordNotFound error for logger
|
||||
ParameterizedQueries: true, // Don't include params in the SQL log
|
||||
Colorful: true,
|
||||
},
|
||||
),
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
@@ -19,22 +19,24 @@ import "Gwen/model/custom_types"
|
||||
|
||||
// AddressBook 有些字段是Personal才会上传的
|
||||
type AddressBook struct {
|
||||
RowId uint `gorm:"primaryKey" json:"row_id"`
|
||||
Id string `json:"id" gorm:"default:0;not null;index"`
|
||||
Username string `json:"username" gorm:"default:'';not null;"`
|
||||
Password string `json:"password" gorm:"default:'';not null;"`
|
||||
Hostname string `json:"hostname" gorm:"default:'';not null;"`
|
||||
Alias string `json:"alias" gorm:"default:'';not null;"`
|
||||
Platform string `json:"platform" gorm:"default:'';not null;"`
|
||||
Tags custom_types.AutoJson `json:"tags" gorm:"not null;" swaggertype:"array,string"`
|
||||
Hash string `json:"hash" gorm:"default:'';not null;"`
|
||||
UserId uint `json:"user_id" gorm:"default:0;not null;index"`
|
||||
ForceAlwaysRelay bool `json:"forceAlwaysRelay" gorm:"default:0;not null;"`
|
||||
RdpPort string `json:"rdpPort" gorm:"default:'';not null;"`
|
||||
RdpUsername string `json:"rdpUsername" gorm:"default:'';not null;"`
|
||||
Online bool `json:"online" gorm:"default:0;not null;"`
|
||||
LoginName string `json:"loginName" gorm:"default:'';not null;"`
|
||||
SameServer bool `json:"sameServer" gorm:"default:0;not null;"`
|
||||
RowId uint `gorm:"primaryKey" json:"row_id"`
|
||||
Id string `json:"id" gorm:"default:0;not null;index"`
|
||||
Username string `json:"username" gorm:"default:'';not null;"`
|
||||
Password string `json:"password" gorm:"default:'';not null;"`
|
||||
Hostname string `json:"hostname" gorm:"default:'';not null;"`
|
||||
Alias string `json:"alias" gorm:"default:'';not null;"`
|
||||
Platform string `json:"platform" gorm:"default:'';not null;"`
|
||||
Tags custom_types.AutoJson `json:"tags" gorm:"not null;" swaggertype:"array,string"`
|
||||
Hash string `json:"hash" gorm:"default:'';not null;"`
|
||||
UserId uint `json:"user_id" gorm:"default:0;not null;index"`
|
||||
ForceAlwaysRelay bool `json:"forceAlwaysRelay" gorm:"default:0;not null;"`
|
||||
RdpPort string `json:"rdpPort" gorm:"default:'';not null;"`
|
||||
RdpUsername string `json:"rdpUsername" gorm:"default:'';not null;"`
|
||||
Online bool `json:"online" gorm:"default:0;not null;"`
|
||||
LoginName string `json:"loginName" gorm:"default:'';not null;"`
|
||||
SameServer bool `json:"sameServer" gorm:"default:0;not null;"`
|
||||
CollectionId uint `json:"collection_id" gorm:"default:0;not null;index"`
|
||||
Collection *AddressBookCollection `json:"collection,omitempty"`
|
||||
TimeModel
|
||||
}
|
||||
|
||||
@@ -42,3 +44,37 @@ type AddressBookList struct {
|
||||
AddressBooks []*AddressBook `json:"list"`
|
||||
Pagination
|
||||
}
|
||||
|
||||
type AddressBookCollection struct {
|
||||
IdModel
|
||||
UserId uint `json:"user_id" gorm:"default:0;not null;index"`
|
||||
Name string `json:"name" gorm:"default:'';not null;" validate:"required"`
|
||||
TimeModel
|
||||
}
|
||||
type AddressBookCollectionList struct {
|
||||
AddressBookCollection []*AddressBookCollection `json:"list"`
|
||||
Pagination
|
||||
}
|
||||
type AddressBookCollectionRule struct {
|
||||
IdModel
|
||||
UserId uint `json:"user_id" gorm:"default:0;not null;"`
|
||||
CollectionId uint `json:"collection_id" gorm:"default:0;not null;index" validate:"required"`
|
||||
Rule int `json:"rule" gorm:"default:0;not null;" validate:"required,gte=1,lte=3"` // 0: 无 1: 读 2: 读写 3: 完全控制
|
||||
Type int `json:"type" gorm:"default:1;not null;" validate:"required,gte=1,lte=2"` // 1: 个人 2: 群组
|
||||
ToId uint `json:"to_id" gorm:"default:0;not null;" validate:"required,gt=0"`
|
||||
TimeModel
|
||||
}
|
||||
type AddressBookCollectionRuleList struct {
|
||||
AddressBookCollectionRule []*AddressBookCollectionRule `json:"list"`
|
||||
Pagination
|
||||
}
|
||||
|
||||
const (
|
||||
ShareAddressBookRuleTypePersonal = 1
|
||||
ShareAddressBookRuleTypeGroup = 2
|
||||
)
|
||||
const (
|
||||
ShareAddressBookRuleRuleRead = 1
|
||||
ShareAddressBookRuleRuleReadWrite = 2
|
||||
ShareAddressBookRuleRuleFullControl = 3
|
||||
)
|
||||
|
||||
46
model/audit.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package model
|
||||
|
||||
const (
|
||||
AuditActionNew = "new"
|
||||
AuditActionClose = "close"
|
||||
)
|
||||
|
||||
type AuditConn struct {
|
||||
IdModel
|
||||
Action string `json:"action" gorm:"default:'';not null;"`
|
||||
ConnId int64 `json:"conn_id" gorm:"default:0;not null;index"`
|
||||
PeerId string `json:"peer_id" gorm:"default:'';not null;index"`
|
||||
FromPeer string `json:"from_peer" gorm:"default:'';not null;"`
|
||||
FromName string `json:"from_name" gorm:"default:'';not null;"`
|
||||
Ip string `json:"ip" gorm:"default:'';not null;"`
|
||||
SessionId string `json:"session_id" gorm:"default:'';not null;"`
|
||||
Type int `json:"type" gorm:"default:0;not null;"`
|
||||
Uuid string `json:"uuid" gorm:"default:'';not null;"`
|
||||
CloseTime int64 `json:"close_time" gorm:"default:0;not null;"`
|
||||
TimeModel
|
||||
}
|
||||
|
||||
type AuditConnList struct {
|
||||
AuditConns []*AuditConn `json:"list"`
|
||||
Pagination
|
||||
}
|
||||
|
||||
type AuditFile struct {
|
||||
IdModel
|
||||
FromPeer string `json:"from_peer" gorm:"default:'';not null;index"`
|
||||
Info string `json:"info" gorm:"default:'';not null;"`
|
||||
IsFile bool `json:"is_file" gorm:"default:0;not null;"`
|
||||
Path string `json:"path" gorm:"default:'';not null;"`
|
||||
PeerId string `json:"peer_id" gorm:"default:'';not null;index"`
|
||||
Type int `json:"type" gorm:"default:0;not null;"`
|
||||
Uuid string `json:"uuid" gorm:"default:'';not null;"`
|
||||
Ip string `json:"ip" gorm:"default:'';not null;"`
|
||||
Num int `json:"num" gorm:"default:0;not null;"`
|
||||
FromName string `json:"from_name" gorm:"default:'';not null;"`
|
||||
TimeModel
|
||||
}
|
||||
|
||||
type AuditFileList struct {
|
||||
AuditFiles []*AuditFile `json:"list"`
|
||||
Pagination
|
||||
}
|
||||
23
model/loginLog.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package model
|
||||
|
||||
type LoginLog struct {
|
||||
IdModel
|
||||
UserId uint `json:"user_id"`
|
||||
Client string `json:"client"` //webadmin,webclient,app,
|
||||
Uuid string `json:"uuid"`
|
||||
Ip string `json:"ip"`
|
||||
Type string `json:"type"` //account,oauth
|
||||
Platform string `json:"platform"` //windows,linux,mac,android,ios
|
||||
|
||||
TimeModel
|
||||
}
|
||||
|
||||
const (
|
||||
LoginLogTypeAccount = "account"
|
||||
LoginLogTypeOauth = "oauth"
|
||||
)
|
||||
|
||||
type LoginLogList struct {
|
||||
LoginLogs []*LoginLog `json:"list"`
|
||||
Pagination
|
||||
}
|
||||