Compare commits
379 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c5687e1506 | ||
|
|
222792419f | ||
|
|
04789e08d5 | ||
|
|
929f6ddf20 | ||
|
|
6e3b16d425 | ||
|
|
ce0291a8f9 | ||
|
|
faee954f6d | ||
|
|
f09c898f16 | ||
|
|
a7c087afbb | ||
|
|
07dfba9d59 | ||
|
|
448725b760 | ||
|
|
b6be4dea21 | ||
|
|
862a1d431e | ||
|
|
8819c85ef4 | ||
|
|
e2ec387ba2 | ||
|
|
1d2d1d4c98 | ||
|
|
16b941e37f | ||
|
|
ff2c79a316 | ||
|
|
b3aee87655 | ||
|
|
73a8461a2d | ||
|
|
af4813db30 | ||
|
|
9d2b589faa | ||
|
|
aa04b225b9 | ||
|
|
fb34d0ac83 | ||
|
|
f68ec8f3a1 | ||
|
|
4b16ad29bb | ||
|
|
c554ff4a31 | ||
|
|
791a1850b8 | ||
|
|
175fdc3bd4 | ||
|
|
f20414b3a5 | ||
|
|
bf39a29e6c | ||
|
|
4ba805ac5f | ||
|
|
b2d71d4312 | ||
|
|
a35fa6f9a8 | ||
|
|
83baa140f5 | ||
|
|
b6eaa4b061 | ||
|
|
d4fdf3785b | ||
|
|
f2ea022965 | ||
|
|
fe4a115c9d | ||
|
|
4412f0309c | ||
|
|
dd78911ed9 | ||
|
|
b23a9bde8e | ||
|
|
3fe7184b4b | ||
|
|
4637879091 | ||
|
|
6d1184d332 | ||
|
|
fb8ef8a1fd | ||
|
|
ae59b6a733 | ||
|
|
605e88e04c | ||
|
|
3ef4454c05 | ||
|
|
35be09b37c | ||
|
|
891469909c | ||
|
|
6ed75bc517 | ||
|
|
caea60c732 | ||
|
|
0a78c206de | ||
|
|
a7f7c76e0e | ||
|
|
c10d132b3c | ||
|
|
8b951065ce | ||
|
|
08420f9db6 | ||
|
|
5db3c8bf0b | ||
|
|
403d9c2233 | ||
|
|
0f16f61ab3 | ||
|
|
0dd92311b2 | ||
|
|
ef4af39f94 | ||
|
|
73f3056878 | ||
|
|
f0b4b0d7c6 | ||
|
|
5d5aae6b3c | ||
|
|
4b5e316475 | ||
|
|
8ff9a12a34 | ||
|
|
6f55c5b642 | ||
|
|
99e63cadcf | ||
|
|
c0346ff9e1 | ||
|
|
d85a00f748 | ||
|
|
6a6a1ed02b | ||
|
|
4b3bbe809d | ||
|
|
63cf231bdc | ||
|
|
6aebf061a4 | ||
|
|
a2672c9489 | ||
|
|
a304da6e02 | ||
|
|
0e160f110b | ||
|
|
32a9eda8ca | ||
|
|
8622c78da0 | ||
|
|
7d6bc2a74f | ||
|
|
5224c31d61 | ||
|
|
983025538f | ||
|
|
cd5b247356 | ||
|
|
e2d72530b1 | ||
|
|
5660d40514 | ||
|
|
d486444680 | ||
|
|
2261a4292a | ||
|
|
829f8799dd | ||
|
|
e549416d94 | ||
|
|
ff77014acb | ||
|
|
daab7112be | ||
|
|
12442f1ba0 | ||
|
|
0e3f204327 | ||
|
|
d9fcfdde6a | ||
|
|
44fa22b7e5 | ||
|
|
59038e27f8 | ||
|
|
7d23ce3caf | ||
|
|
7c528facf9 | ||
|
|
f8548a6589 | ||
|
|
23c5a96fe1 | ||
|
|
c72865b181 | ||
|
|
9a2f7010e1 | ||
|
|
a72096b56d | ||
|
|
56014d35bd | ||
|
|
a7e4ce4f72 | ||
|
|
f2cf3e1060 | ||
|
|
a4433e6113 | ||
|
|
cca191aad3 | ||
|
|
edb095ab0b | ||
|
|
ac45a1b5ad | ||
|
|
83279a553a | ||
|
|
a5671e692d | ||
|
|
63f2fd2f07 | ||
|
|
5a09850fc4 | ||
|
|
aac44835ed | ||
|
|
4eed76d674 | ||
|
|
d1fc0036cb | ||
|
|
1555865612 | ||
|
|
147de065fa | ||
|
|
8dc5f82a0e | ||
|
|
3f8cb4dac5 | ||
|
|
4b9e1f02f2 | ||
|
|
18312336a4 | ||
|
|
72e3a9a841 | ||
|
|
16427164f8 | ||
|
|
5c96350e7d | ||
|
|
1dd920d192 | ||
|
|
c41038d2d8 | ||
|
|
88ec961312 | ||
|
|
a64b9ced1c | ||
|
|
c778990cf9 | ||
|
|
ba46dbbf7e | ||
|
|
3b8b8f37dd | ||
|
|
51db63cf35 | ||
|
|
9ebfd6de85 | ||
|
|
1c3afa4a70 | ||
|
|
5cddbf14c8 | ||
|
|
88765302e8 | ||
|
|
034ec6ca1b | ||
|
|
a55e501b50 | ||
|
|
e36d307d28 | ||
|
|
1e15287c83 | ||
|
|
c298ddea0c | ||
|
|
41fd387baf | ||
|
|
bf8eadfbaa | ||
|
|
a3012cfa18 | ||
|
|
592b04746d | ||
|
|
ef0410a8b9 | ||
|
|
3a430c72a2 | ||
|
|
1eaae8f213 | ||
|
|
8c0ac5621a | ||
|
|
cd74cd19ef | ||
|
|
1c5cd3ae62 | ||
|
|
7749ff0729 | ||
|
|
b77a104d9c | ||
|
|
fc898ba30c | ||
|
|
287bbd43fb | ||
|
|
65571ab2f5 | ||
|
|
17b65b412f | ||
|
|
b06d9c4e22 | ||
|
|
601d47570e | ||
|
|
29d6e36d50 | ||
|
|
abca3a9300 | ||
|
|
22e470fee5 | ||
|
|
78869a4a57 | ||
|
|
d0f8d79efb | ||
|
|
5417e383c7 | ||
|
|
49eaa831ed | ||
|
|
ae2e78e373 | ||
|
|
7bb2ce0a43 | ||
|
|
15a518c1ac | ||
|
|
4915cfa9ba | ||
|
|
de712ba9c5 | ||
|
|
c58f08af27 | ||
|
|
5609a1a618 | ||
|
|
70a03f1aba | ||
|
|
7c6aaa171e | ||
|
|
34a27ba073 | ||
|
|
956700c27f | ||
|
|
197a718f03 | ||
|
|
2c1892f40c | ||
|
|
943b1f8077 | ||
|
|
e71ea2243f | ||
|
|
daeae19194 | ||
|
|
4321a41cd7 | ||
|
|
3acfb36c5d | ||
|
|
5a53f180e4 | ||
|
|
ca79a63492 | ||
|
|
9dfe745629 | ||
|
|
18d59d7047 | ||
|
|
aee25a6c99 | ||
|
|
fea3960672 | ||
|
|
da7b70c471 | ||
|
|
3cd90c8f74 | ||
|
|
fb9173ed53 | ||
|
|
b9efc73025 | ||
|
|
64f28c17d8 | ||
|
|
1ca50b5e9d | ||
|
|
9ed376715f | ||
|
|
6698877761 | ||
|
|
2ceaa0091b | ||
|
|
6fb1fbc5b1 | ||
|
|
4909beee32 | ||
|
|
d85a799d0b | ||
|
|
d38117107d | ||
|
|
6a7ef29089 | ||
|
|
a4dd39043e | ||
|
|
dfcc7d54c1 | ||
|
|
71d1c431a9 | ||
|
|
d31d669734 | ||
|
|
c021ebfbdf | ||
|
|
485ae54e9e | ||
|
|
c5e3482538 | ||
|
|
c4b45a51d5 | ||
|
|
51186b66dc | ||
|
|
2627f6fb06 | ||
|
|
a6a6c3aefe | ||
|
|
d21b5809c4 | ||
|
|
503e908eb6 | ||
|
|
6c94566e83 | ||
|
|
095640af3f | ||
|
|
5488babfb3 | ||
|
|
962e07d65f | ||
|
|
bd2eeae01a | ||
|
|
efead3f9c7 | ||
|
|
7cb838693a | ||
|
|
e2fda47cbb | ||
|
|
0ed40318cb | ||
|
|
122b3baf6f | ||
|
|
06f007ebd9 | ||
|
|
7c1fc4fa6d | ||
|
|
e1424bcea7 | ||
|
|
b3edc9d112 | ||
|
|
68065d3372 | ||
|
|
f6a0c6466f | ||
|
|
1070022277 | ||
|
|
f9a8a966e5 | ||
|
|
3041fb82d9 | ||
|
|
0ddfbdbd23 | ||
|
|
eada376783 | ||
|
|
bc92ffc106 | ||
|
|
60b7ccdb3a | ||
|
|
9eff8ea33a | ||
|
|
17305b6a6f | ||
|
|
7d83226655 | ||
|
|
001884a4d8 | ||
|
|
461b100b6c | ||
|
|
4105f14a3f | ||
|
|
4baa8d392e | ||
|
|
bf48e8ba19 | ||
|
|
991fc86fa6 | ||
|
|
1191ade8af | ||
|
|
02232fc343 | ||
|
|
497dfb2402 | ||
|
|
f21fb5d437 | ||
|
|
8ee93b530d | ||
|
|
e833c0e0c0 | ||
|
|
d5cf3544f1 | ||
|
|
ca9a3155af | ||
|
|
b1817ca63f | ||
|
|
476f251d2c | ||
|
|
e2fa928648 | ||
|
|
3f9b370dd6 | ||
|
|
7cf4fb4157 | ||
|
|
6fb1235dd8 | ||
|
|
8aa779f611 | ||
|
|
5c4b6b5cd0 | ||
|
|
47ab3958c9 | ||
|
|
cbb3764b60 | ||
|
|
f5aadec6c0 | ||
|
|
de0523852a | ||
|
|
ba85790d8c | ||
|
|
0091a9dd7f | ||
|
|
e142cc00c6 | ||
|
|
488fedf784 | ||
|
|
ceb65cd391 | ||
|
|
72c196c6f9 | ||
|
|
28f047ec79 | ||
|
|
7e0cc33c91 | ||
|
|
05a2961814 | ||
|
|
dc8af3a460 | ||
|
|
e269341d51 | ||
|
|
ebe5bc4c3a | ||
|
|
b4965e8885 | ||
|
|
0eb733cc33 | ||
|
|
1e41784bb9 | ||
|
|
3d4e068266 | ||
|
|
abc0e9d907 | ||
|
|
5d361d05cc | ||
|
|
823c052d6d | ||
|
|
ebe39af682 | ||
|
|
8b266d4fee | ||
|
|
2ab61512cf | ||
|
|
ee679d09f7 | ||
|
|
71c44a501b | ||
|
|
f8a672070f | ||
|
|
609dfaeea0 | ||
|
|
71e3529c2f | ||
|
|
61fc53858b | ||
|
|
a843748f60 | ||
|
|
c266f57a46 | ||
|
|
86f22e2666 | ||
|
|
c42e865784 | ||
|
|
5ad31679a8 | ||
|
|
56c84c854a | ||
|
|
8118107f0f | ||
|
|
d3aebec722 | ||
|
|
808aa47856 | ||
|
|
90f8c6b4e6 | ||
|
|
5de0b522f1 | ||
|
|
98e9828e1f | ||
|
|
71ff7ebd42 | ||
|
|
bd084ced52 | ||
|
|
cab04765f0 | ||
|
|
5deaaa8a00 | ||
|
|
58d39c7aa3 | ||
|
|
87bec98a7f | ||
|
|
74662ae58b | ||
|
|
814bf48a54 | ||
|
|
a1dba6c564 | ||
|
|
418b182641 | ||
|
|
bcbbf1099d | ||
|
|
85c8377240 | ||
|
|
264ae2dffa | ||
|
|
b069e7876d | ||
|
|
41670b0d00 | ||
|
|
5bf8e176aa | ||
|
|
9787ca0895 | ||
|
|
04098cb6e1 | ||
|
|
6411be66ba | ||
|
|
0d3fd56c5e | ||
|
|
9e7908c035 | ||
|
|
08c7647c10 | ||
|
|
cbbc8259e8 | ||
|
|
aff735335b | ||
|
|
47f1043cc2 | ||
|
|
871bb328f0 | ||
|
|
d18d8c0e3f | ||
|
|
db7c43353d | ||
|
|
1847697ceb | ||
|
|
f5ffc433f7 | ||
|
|
fe36f7b2f0 | ||
|
|
4f2216d023 | ||
|
|
a214113de5 | ||
|
|
ea9dc84b91 | ||
|
|
cf6bd41319 | ||
|
|
0ab1ff9e18 | ||
|
|
c7b0996443 | ||
|
|
62ebc66259 | ||
|
|
6ce6a95e07 | ||
|
|
467b61fb20 | ||
|
|
c99261c12d | ||
|
|
c9d70584cc | ||
|
|
0a81b5b5f8 | ||
|
|
95a1bd6c21 | ||
|
|
8c2f6f0929 | ||
|
|
c5b43fae08 | ||
|
|
c485035e74 | ||
|
|
a161e3ba10 | ||
|
|
5b5943b287 | ||
|
|
69ad1b2e80 | ||
|
|
01aad9bb15 | ||
|
|
b4b246def5 | ||
|
|
752d376dd7 | ||
|
|
601e1de3b3 | ||
|
|
758f1fb1c9 | ||
|
|
d394931fce | ||
|
|
192b3b11a9 | ||
|
|
4ec08cf03a | ||
|
|
cb1a06e4a9 | ||
|
|
67b4678b2d | ||
|
|
8d390dae50 | ||
|
|
a4b40d746b | ||
|
|
ddfd9f7923 | ||
|
|
521e2c72f5 | ||
|
|
79aa573e0f | ||
|
|
5b6e56a8ec |
29
.dockerignore
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# Ignore Docker Compose configuration files
|
||||||
|
docker-compose.yaml
|
||||||
|
docker-compose-dev.yaml
|
||||||
|
|
||||||
|
# Ignore development Dockerfile
|
||||||
|
Dockerfile
|
||||||
|
Dockerfile.dev
|
||||||
|
docker-dev.sh
|
||||||
|
|
||||||
|
# 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
|
||||||
3
.gitattributes
vendored
@@ -1 +1,2 @@
|
|||||||
resources/web/**/* linguist-vendored
|
resources/web/**/* linguist-vendored
|
||||||
|
resources/web2/**/* linguist-vendored
|
||||||
444
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,444 @@
|
|||||||
|
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
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
repository: lejianwen/rustdesk-api-web
|
||||||
|
path: rustdesk-api-web
|
||||||
|
ref: master
|
||||||
|
|
||||||
|
- name: Set up Go environment
|
||||||
|
uses: actions/setup-go@v4
|
||||||
|
with:
|
||||||
|
go-version: '1.23' # 选择 Go 版本
|
||||||
|
|
||||||
|
- name: Set up npm
|
||||||
|
uses: actions/setup-node@v2
|
||||||
|
with:
|
||||||
|
node-version: '20'
|
||||||
|
|
||||||
|
- name: build rustdesk-api-web
|
||||||
|
working-directory: rustdesk-api-web
|
||||||
|
run: |
|
||||||
|
npm install
|
||||||
|
npm run build
|
||||||
|
mkdir -p ../resources/admin/
|
||||||
|
cp -ar dist/* ../resources/admin/
|
||||||
|
|
||||||
|
- name: tidy
|
||||||
|
run: go mod tidy
|
||||||
|
|
||||||
|
- name: Get tag version
|
||||||
|
run: |
|
||||||
|
TAG_VERSION="${GITHUB_REF##*/}"
|
||||||
|
VERSION="${TAG_VERSION#v}"
|
||||||
|
echo "VERSION=$VERSION" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Write version to resources/version
|
||||||
|
run: echo $VERSION > resources/version
|
||||||
|
|
||||||
|
- 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
|
||||||
|
echo @echo off > release/start.bat
|
||||||
|
echo cmd /c \"%~dp0apimain.exe\" >> release/start.bat
|
||||||
|
zip -r ${{ matrix.job.goos}}-${{ matrix.job.platform }}.${{matrix.job.file_ext}} ./release
|
||||||
|
else
|
||||||
|
if [ "${{ matrix.job.platform }}" = "arm64" ]; then
|
||||||
|
wget https://musl.ljw.red/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.ljw.red/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}}
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Generate Changelog
|
||||||
|
if: startsWith(github.ref, 'refs/tags/') && github.event_name == 'push'
|
||||||
|
run: npx changelogithub # or changelogithub@0.12 if ensure the stable result
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
||||||
|
|
||||||
|
deb-package:
|
||||||
|
name: debian package - ${{ matrix.job.platform }}
|
||||||
|
needs: build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
job:
|
||||||
|
- { platform: "amd64", goos: "linux", debian_platform: "amd64", crossbuild_package: ""}
|
||||||
|
- { platform: "arm64", goos: "linux", debian_platform: "arm64", crossbuild_package: "crossbuild-essential-arm64" }
|
||||||
|
- { platform: "armv7l", goos: "linux", debian_platform: "armhf", crossbuild_package: "crossbuild-essential-armhf" }
|
||||||
|
steps:
|
||||||
|
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v2
|
||||||
|
|
||||||
|
- name: Create packaging env
|
||||||
|
run: |
|
||||||
|
sudo apt update
|
||||||
|
DEBIAN_FRONTEND=noninteractive sudo apt install -y devscripts build-essential debhelper pkg-config ${{ matrix.job.crossbuild_package }}
|
||||||
|
mkdir -p debian-build/${{ matrix.job.platform }}/bin
|
||||||
|
|
||||||
|
- name: Get tag version
|
||||||
|
id: get_tag
|
||||||
|
run: |
|
||||||
|
TAG_VERSION="${GITHUB_REF##*/}"
|
||||||
|
VERSION="${TAG_VERSION#v}"
|
||||||
|
echo "TAG_VERSION=$TAG_VERSION" >> $GITHUB_ENV
|
||||||
|
echo "VERSION=$VERSION" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Update changelog
|
||||||
|
run: |
|
||||||
|
DATE=$(date -R)
|
||||||
|
sed -i "1i rustdesk-api-server (${VERSION}) stable; urgency=medium\n\n * Automatically generated release for version ${VERSION}.\n\n -- GitHub Actions <actions@github.com> ${DATE}\n" debian/changelog
|
||||||
|
|
||||||
|
- name: Download binaries
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: rustdesk-api-${{ matrix.job.goos }}-${{ matrix.job.platform }}
|
||||||
|
path: .
|
||||||
|
|
||||||
|
- name: Unzip binaries
|
||||||
|
run: |
|
||||||
|
mkdir -p ${{ matrix.job.platform }}
|
||||||
|
tar -xzf ${{ matrix.job.goos }}-${{ matrix.job.platform }}.tar.gz -C ${{ matrix.job.platform }}
|
||||||
|
|
||||||
|
- name: Build package for ${{ matrix.job.platform }} arch
|
||||||
|
run: |
|
||||||
|
mv ${{ matrix.job.platform }}/release/apimain debian-build/${{ matrix.job.platform }}/bin/rustdesk-api
|
||||||
|
mv ${{ matrix.job.platform }}/release/resources/admin resources
|
||||||
|
chmod -v a+x debian-build/${{ matrix.job.platform }}/bin/*
|
||||||
|
mkdir -p data
|
||||||
|
cp -vr debian systemd conf data resources runtime debian-build/${{ matrix.job.platform }}/
|
||||||
|
cat debian/control.tpl | sed 's/{{ ARCH }}/${{ matrix.job.debian_platform }}/' > debian-build/${{ matrix.job.platform }}/debian/control
|
||||||
|
cd debian-build/${{ matrix.job.platform }}/
|
||||||
|
debuild -i -us -uc -b -a${{ matrix.job.debian_platform}}
|
||||||
|
|
||||||
|
- name: Upload artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: rustdesk-api-${{ matrix.job.debian_platform }}
|
||||||
|
path: |
|
||||||
|
debian-build/*.deb
|
||||||
|
|
||||||
|
- name: Create Release
|
||||||
|
uses: softprops/action-gh-release@v2
|
||||||
|
with:
|
||||||
|
files: |
|
||||||
|
debian-build/rustdesk-api-server_*_${{ matrix.job.debian_platform }}.deb
|
||||||
|
|
||||||
|
docker:
|
||||||
|
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 }}
|
||||||
|
|
||||||
|
- 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 Full S6 image to Docker Hub ${{ matrix.job.platform }}
|
||||||
|
if: ${{ env.SKIP_DOCKER_HUB == 'false' }} # Only run this step if SKIP_DOCKER_HUB is false
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
with:
|
||||||
|
context: "."
|
||||||
|
file: ./Dockerfile_full_s6
|
||||||
|
platforms: ${{ matrix.job.docker_platform }}
|
||||||
|
push: true
|
||||||
|
provenance: false
|
||||||
|
build-args: |
|
||||||
|
BUILDARCH=${{ matrix.job.platform }}
|
||||||
|
tags: |
|
||||||
|
${{ env.DOCKERHUB_IMAGE_NAMESPACE }}/rustdesk-api:full-s6-${{ matrix.job.platform }}
|
||||||
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
|
||||||
|
- name: Build and push Docker image to GHCR ${{ matrix.job.platform }}
|
||||||
|
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 }}
|
||||||
|
|
||||||
|
- name: Build and push Docker Full S6 image to GHCR ${{ matrix.job.platform }}
|
||||||
|
if: ${{ env.SKIP_GHCR == 'false' }} # Only run this step if SKIP_GHCR is false
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
with:
|
||||||
|
context: "."
|
||||||
|
file: ./Dockerfile
|
||||||
|
platforms: ${{ matrix.job.docker_platform }}
|
||||||
|
push: true
|
||||||
|
provenance: false
|
||||||
|
build-args: |
|
||||||
|
BUILDARCH=${{ matrix.job.platform }}
|
||||||
|
tags: |
|
||||||
|
ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:full-s6-${{ matrix.job.platform }}
|
||||||
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
|
||||||
|
#
|
||||||
|
docker-manifest:
|
||||||
|
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@v0.2.3
|
||||||
|
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@v0.2.3
|
||||||
|
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@v0.2.3
|
||||||
|
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@v0.2.3
|
||||||
|
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
|
||||||
|
|
||||||
|
- name: Create and push Full S6 manifest Docker Hub (:version)
|
||||||
|
if: ${{ env.SKIP_DOCKER_HUB == 'false' }}
|
||||||
|
uses: Noelware/docker-manifest-action@v0.2.3
|
||||||
|
with:
|
||||||
|
base-image: ${{ env.BASE_IMAGE_NAMESPACE }}/rustdesk-api:full-s6
|
||||||
|
extra-images: ${{ env.DOCKERHUB_IMAGE_NAMESPACE }}/rustdesk-api:full-s6-amd64,
|
||||||
|
${{ env.DOCKERHUB_IMAGE_NAMESPACE }}/rustdesk-api:full-s6-armv7l,
|
||||||
|
${{ env.DOCKERHUB_IMAGE_NAMESPACE }}/rustdesk-api:full-s6-arm64
|
||||||
|
push: true
|
||||||
|
amend: true
|
||||||
|
|
||||||
|
- name: Create and push Full S6 manifest GHCR (:latest)
|
||||||
|
if: ${{ env.SKIP_GHCR == 'false' }}
|
||||||
|
uses: Noelware/docker-manifest-action@v0.2.3
|
||||||
|
with:
|
||||||
|
base-image: ghcr.io/${{ env.BASE_IMAGE_NAMESPACE }}/rustdesk-api:full-s6
|
||||||
|
extra-images: ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:full-s6-amd64,
|
||||||
|
ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:full-s6-armv7l,
|
||||||
|
ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:full-s6-arm64
|
||||||
|
push: true
|
||||||
|
amend: true
|
||||||
337
.github/workflows/build_test.yml
vendored
Normal file
@@ -0,0 +1,337 @@
|
|||||||
|
name: Build Test
|
||||||
|
|
||||||
|
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'
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
repository: lejianwen/rustdesk-api-web
|
||||||
|
path: rustdesk-api-web
|
||||||
|
ref: master
|
||||||
|
|
||||||
|
- name: Set up Go environment
|
||||||
|
uses: actions/setup-go@v4
|
||||||
|
with:
|
||||||
|
go-version: '1.23' # 选择 Go 版本
|
||||||
|
|
||||||
|
- name: Set up npm
|
||||||
|
uses: actions/setup-node@v2
|
||||||
|
with:
|
||||||
|
node-version: '20'
|
||||||
|
|
||||||
|
- name: build rustdesk-api-web
|
||||||
|
working-directory: rustdesk-api-web
|
||||||
|
run: |
|
||||||
|
npm install
|
||||||
|
npm run build
|
||||||
|
mkdir -p ../resources/admin/
|
||||||
|
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
|
||||||
|
echo @echo off > release/start.bat
|
||||||
|
echo cmd /c \"%~dp0apimain.exe\" >> release/start.bat
|
||||||
|
zip -r ${{ matrix.job.goos}}-${{ matrix.job.platform }}.${{matrix.job.file_ext}} ./release
|
||||||
|
else
|
||||||
|
if [ "${{ matrix.job.platform }}" = "arm64" ]; then
|
||||||
|
wget https://musl.ljw.red/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.ljw.red/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: test
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
deb-package:
|
||||||
|
name: debian package - ${{ matrix.job.platform }}
|
||||||
|
needs: build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
job:
|
||||||
|
- { platform: "amd64", goos: "linux", debian_platform: "amd64", crossbuild_package: ""}
|
||||||
|
- { platform: "arm64", goos: "linux", debian_platform: "arm64", crossbuild_package: "crossbuild-essential-arm64" }
|
||||||
|
- { platform: "armv7l", goos: "linux", debian_platform: "armhf", crossbuild_package: "crossbuild-essential-armhf" }
|
||||||
|
steps:
|
||||||
|
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v2
|
||||||
|
|
||||||
|
- name: Create packaging env
|
||||||
|
run: |
|
||||||
|
sudo apt update
|
||||||
|
DEBIAN_FRONTEND=noninteractive sudo apt install -y devscripts build-essential debhelper pkg-config ${{ matrix.job.crossbuild_package }}
|
||||||
|
mkdir -p debian-build/${{ matrix.job.platform }}/bin
|
||||||
|
|
||||||
|
- name: Download binaries
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: rustdesk-api-${{ matrix.job.goos }}-${{ matrix.job.platform }}
|
||||||
|
path: .
|
||||||
|
|
||||||
|
- name: Unzip binaries
|
||||||
|
run: |
|
||||||
|
mkdir -p ${{ matrix.job.platform }}
|
||||||
|
tar -xzf ${{ matrix.job.goos }}-${{ matrix.job.platform }}.tar.gz -C ${{ matrix.job.platform }}
|
||||||
|
|
||||||
|
- name: Build package for ${{ matrix.job.platform }} arch
|
||||||
|
run: |
|
||||||
|
mv ${{ matrix.job.platform }}/release/apimain debian-build/${{ matrix.job.platform }}/bin/rustdesk-api
|
||||||
|
chmod -v a+x debian-build/${{ matrix.job.platform }}/bin/*
|
||||||
|
mkdir -p data
|
||||||
|
cp -vr debian systemd conf data resources runtime debian-build/${{ matrix.job.platform }}/
|
||||||
|
cat debian/control.tpl | sed 's/{{ ARCH }}/${{ matrix.job.debian_platform }}/' > debian-build/${{ matrix.job.platform }}/debian/control
|
||||||
|
cd debian-build/${{ matrix.job.platform }}/
|
||||||
|
debuild -i -us -uc -b -a${{ matrix.job.debian_platform}}
|
||||||
|
|
||||||
|
- name: Upload artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: rustdesk-api-${{ matrix.job.debian_platform }}
|
||||||
|
path: |
|
||||||
|
debian-build/*.deb
|
||||||
|
|
||||||
|
- name: Upload to GitHub Release
|
||||||
|
uses: softprops/action-gh-release@v2
|
||||||
|
with:
|
||||||
|
tag_name: test
|
||||||
|
files: |
|
||||||
|
debian-build/rustdesk-api-server_*_${{ matrix.job.debian_platform }}.deb
|
||||||
|
|
||||||
|
docker:
|
||||||
|
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=test" >> $GITHUB_ENV # Default to 'test' 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 }}
|
||||||
|
|
||||||
|
- 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.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.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=test" >> $GITHUB_ENV # Default to 'test' 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@v0.2.3
|
||||||
|
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@v0.2.3
|
||||||
|
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
|
||||||
9
.gitignore
vendored
@@ -1,9 +1,8 @@
|
|||||||
.idea
|
.idea
|
||||||
runtime/*
|
runtime/*
|
||||||
!runtime/cache/.gitignore
|
!runtime
|
||||||
|
!runtime/cache/.gitkeep
|
||||||
go.sum
|
go.sum
|
||||||
resources/*
|
resources/admin
|
||||||
!resources/public/upload/.gitignore
|
|
||||||
!resources/web
|
|
||||||
release
|
release
|
||||||
data
|
data/rustdeskapi.db
|
||||||
37
Dockerfile
@@ -1,39 +1,10 @@
|
|||||||
FROM golang:1.22-alpine as builder
|
|
||||||
|
|
||||||
RUN set -eux; \
|
|
||||||
apk add --no-cache git gcc build-base sqlite-dev npm nodejs; \
|
|
||||||
git clone https://github.com/lejianwen/rustdesk-api-web; \
|
|
||||||
git clone https://github.com/lejianwen/rustdesk-api; \
|
|
||||||
#先编译后台
|
|
||||||
cd rustdesk-api-web; \
|
|
||||||
npm install; \
|
|
||||||
npm run build; \
|
|
||||||
cd ..; \
|
|
||||||
mkdir -p rustdesk-api/resources/admin; \
|
|
||||||
cp -ar rustdesk-api-web/dist/* rustdesk-api/resources/admin; \
|
|
||||||
cd rustdesk-api; \
|
|
||||||
go mod tidy; \
|
|
||||||
go install github.com/swaggo/swag/cmd/swag@latest; \
|
|
||||||
swag init -g cmd/apimain.go --output docs/api --instanceName api --exclude http/controller/admin; \
|
|
||||||
swag init -g cmd/apimain.go --output docs/admin --instanceName admin --exclude http/controller/api; \
|
|
||||||
go env -w GO111MODULE=on;\
|
|
||||||
go env -w CGO_ENABLED=1;\
|
|
||||||
go env -w GOOS=linux;\
|
|
||||||
go env -w GOARCH=amd64;\
|
|
||||||
go env -w CGO_LDFLAGS="-static"; \
|
|
||||||
go build -o release/apimain cmd/apimain.go; \
|
|
||||||
cp -ar resources release/; \
|
|
||||||
mkdir -p release/resources/public; \
|
|
||||||
cp -ar docs release/; \
|
|
||||||
cp -ar conf release/; \
|
|
||||||
mkdir -p release/data; \
|
|
||||||
mkdir -p release/runtime;
|
|
||||||
|
|
||||||
VOLUME /app/data
|
|
||||||
FROM alpine
|
FROM alpine
|
||||||
|
|
||||||
|
ARG BUILDARCH
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
RUN apk add --no-cache tzdata
|
RUN apk add --no-cache tzdata
|
||||||
COPY --from=builder /go/rustdesk-api/release /app/
|
COPY ./${BUILDARCH}/release /app/
|
||||||
|
VOLUME /app/data
|
||||||
|
|
||||||
EXPOSE 21114
|
EXPOSE 21114
|
||||||
CMD ["./apimain"]
|
CMD ["./apimain"]
|
||||||
|
|||||||
94
Dockerfile.dev
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
# 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 . .
|
||||||
|
|
||||||
|
# use --mount=type=cache,target=/go/pkg/mod to cache the go mod
|
||||||
|
# Step 2: Download dependencies
|
||||||
|
RUN --mount=type=cache,target=/go/pkg/mod \
|
||||||
|
go mod tidy && go mod download && go install github.com/swaggo/swag/cmd/swag@latest
|
||||||
|
|
||||||
|
# Step 3: Run swag build script
|
||||||
|
RUN --mount=type=cache,target=/go/pkg/mod \
|
||||||
|
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
|
||||||
|
|
||||||
|
# Step 4: Build the Go application with CGO enabled and specified ldflags
|
||||||
|
RUN --mount=type=cache,target=/go/pkg/mod \
|
||||||
|
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
|
||||||
|
|
||||||
|
ARG COUNTRY
|
||||||
|
# Install required tools without caching index to minimize image size
|
||||||
|
RUN if [ "$COUNTRY" = "CN" ] ; then \
|
||||||
|
echo "It is in China, updating the repositories"; \
|
||||||
|
sed -i 's#https\?://dl-cdn.alpinelinux.org/alpine#https://mirrors.tuna.tsinghua.edu.cn/alpine#g' /etc/apk/repositories; \
|
||||||
|
fi && \
|
||||||
|
apk update && apk add --no-cache git
|
||||||
|
|
||||||
|
ARG FRONTEND_GIT_REPO=https://github.com/lejianwen/rustdesk-api-web.git
|
||||||
|
ARG FRONTEND_GIT_BRANCH=master
|
||||||
|
# Clone the frontend repository
|
||||||
|
|
||||||
|
RUN git clone -b $FRONTEND_GIT_BRANCH $FRONTEND_GIT_REPO .
|
||||||
|
|
||||||
|
# Install required tools without caching index to minimize image size
|
||||||
|
RUN if [ "$COUNTRY" = "CN" ] ; then \
|
||||||
|
echo "It is in China, updating NPM_CONFIG_REGISTRY"; \
|
||||||
|
export NPM_CONFIG_REGISTRY="https://mirrors.huaweicloud.com/repository/npm/"; \
|
||||||
|
fi && \
|
||||||
|
npm install && npm run build
|
||||||
|
|
||||||
|
|
||||||
|
# Stage 2: Final Image
|
||||||
|
FROM alpine:latest
|
||||||
|
|
||||||
|
# Set up working directory
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Install necessary runtime dependencies
|
||||||
|
# Install required tools without caching index to minimize image size
|
||||||
|
ARG COUNTRY
|
||||||
|
RUN if [ "$COUNTRY" = "CN" ] ; then \
|
||||||
|
echo "It is in China, updating the repositories"; \
|
||||||
|
sed -i 's#https\?://dl-cdn.alpinelinux.org/alpine#https://mirrors.tuna.tsinghua.edu.cn/alpine#g' /etc/apk/repositories; \
|
||||||
|
fi && \
|
||||||
|
apk update && 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"]
|
||||||
38
Dockerfile_full_s6
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
FROM rustdesk/rustdesk-server-s6:latest AS server
|
||||||
|
|
||||||
|
FROM alpine
|
||||||
|
|
||||||
|
ARG BUILDARCH
|
||||||
|
WORKDIR /app
|
||||||
|
RUN apk add --no-cache tzdata
|
||||||
|
COPY ./${BUILDARCH}/release /app/
|
||||||
|
|
||||||
|
COPY --from=server /init /init
|
||||||
|
COPY --from=server /etc/s6-overlay /etc/s6-overlay
|
||||||
|
COPY --from=server /package /package
|
||||||
|
COPY --from=server /usr/bin/healthcheck.sh /usr/bin/healthcheck.sh
|
||||||
|
COPY --from=server /usr/bin/hbbr /usr/bin/hbbr
|
||||||
|
COPY --from=server /usr/bin/hbbs /usr/bin/hbbs
|
||||||
|
COPY --from=server /usr/bin/rustdesk-utils /usr/bin/rustdesk-utils
|
||||||
|
COPY --from=server /command /command
|
||||||
|
|
||||||
|
RUN \
|
||||||
|
mkdir -p /etc/s6-overlay/s6-rc.d/api && \
|
||||||
|
echo -e "key-secret\nhbbs" > /etc/s6-overlay/s6-rc.d/api/dependencies && \
|
||||||
|
echo "longrun" > /etc/s6-overlay/s6-rc.d/api/type && \
|
||||||
|
echo "#!/command/with-contenv sh" > /etc/s6-overlay/s6-rc.d/api/run && \
|
||||||
|
echo "cd /app" >> /etc/s6-overlay/s6-rc.d/api/run && \
|
||||||
|
echo "./apimain" >> /etc/s6-overlay/s6-rc.d/api/run && \
|
||||||
|
touch /etc/s6-overlay/s6-rc.d/user/contents.d/api && \
|
||||||
|
echo "/package/admin/s6/command/s6-svstat /run/s6-rc/servicedirs/api || exit 1" >> /usr/bin/healthcheck.sh && \
|
||||||
|
ln -s /run /var/run
|
||||||
|
|
||||||
|
ENV RELAY=relay.example.com
|
||||||
|
ENV ENCRYPTED_ONLY=0
|
||||||
|
|
||||||
|
VOLUME /data
|
||||||
|
VOLUME /app/data
|
||||||
|
|
||||||
|
EXPOSE 21114 21115 21116 21116/udp 21117 21118 21119
|
||||||
|
|
||||||
|
ENTRYPOINT ["/init"]
|
||||||
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2024-present Lejianwen and contributors
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
421
README.md
@@ -2,247 +2,231 @@
|
|||||||
|
|
||||||
[English Doc](README_EN.md)
|
[English Doc](README_EN.md)
|
||||||
|
|
||||||
本项目使用 Go 实现了 RustDesk 的 API,并包含了 Web UI 和 Web 客户端。RustDesk 是一个远程桌面软件,提供了自托管的解决方案。
|
本项目使用 Go 实现了 RustDesk 的 API,并包含了 Web Admin 和 Web 客户端。
|
||||||
|
|
||||||
<div align=center>
|
|
||||||
|
<div align=center>
|
||||||
<img src="https://img.shields.io/badge/golang-1.22-blue"/>
|
<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/gin-v1.9.0-lightBlue"/>
|
||||||
<img src="https://img.shields.io/badge/gorm-v1.25.7-green"/>
|
<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://img.shields.io/badge/swag-v1.16.3-yellow"/>
|
||||||
|
<img src="https://goreportcard.com/badge/github.com/lejianwen/rustdesk-api/v2"/>
|
||||||
|
<img src="https://github.com/lejianwen/rustdesk-api/actions/workflows/build.yml/badge.svg"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
## 使用前准备
|
## 搭配[lejianwen/rustdesk-server]使用更佳。
|
||||||
|
> [lejianwen/rustdesk-server]fork自RustDesk Server官方仓库
|
||||||
|
> 1. 解决了使用API链接超时问题
|
||||||
|
> 2. 可以强制登录后才能发起链接
|
||||||
|
> 3. 支持客户端websocket
|
||||||
|
|
||||||
### [Rustdesk](https://github.com/rustdesk/rustdesk)
|
|
||||||
|
|
||||||
1. PC客户端使用的是 ***1.3.0***,经测试 ***1.2.6+*** 都可以
|
|
||||||
2. server端必须指定key,不能用自带的生成的key,否则可能链接不上或者超时
|
|
||||||
|
|
||||||
```bash
|
# 特性
|
||||||
hbbs -r <relay-server-ip[:port]> -k <key>
|
|
||||||
hbbr -k <key>
|
|
||||||
```
|
|
||||||
|
|
||||||
比如
|
- PC端API
|
||||||
|
- 个人版API
|
||||||
```bash
|
- 登录
|
||||||
hbbs -r <relay-server-ip[:port]> -k abc1234567
|
- 地址簿
|
||||||
hbbr -k abc1234567
|
- 群组
|
||||||
```
|
- 授权登录
|
||||||
|
- 支持`github`, `google` 和 `OIDC` 登录,
|
||||||
|
- 支持`web后台`授权登录
|
||||||
|
- 支持`LDAP`(AD和OpenLDAP已测试), 如果API Server配置了LDAP
|
||||||
|
- i18n
|
||||||
|
- Web Admin
|
||||||
|
- 用户管理
|
||||||
|
- 设备管理
|
||||||
|
- 地址簿管理
|
||||||
|
- 标签管理
|
||||||
|
- 群组管理
|
||||||
|
- Oauth 管理
|
||||||
|
- 配置LDAP, 配置文件或者环境变量
|
||||||
|
- 登录日志
|
||||||
|
- 链接日志
|
||||||
|
- 文件传输日志
|
||||||
|
- 快速使用web client
|
||||||
|
- i18n
|
||||||
|
- 通过 web client 分享给游客
|
||||||
|
- server控制(一些官方的简单的指令 [WIKI](https://github.com/lejianwen/rustdesk-api/wiki/Rustdesk-Command))
|
||||||
|
- Web Client
|
||||||
|
- 自动获取API server
|
||||||
|
- 自动获取ID服务器和KEY
|
||||||
|
- 自动获取地址簿
|
||||||
|
- 游客通过临时分享链接直接远程到设备
|
||||||
|
- CLI
|
||||||
|
- 重置管理员密码
|
||||||
|
|
||||||
## 功能
|
## 功能
|
||||||
|
|
||||||
### API 服务: 基本实现了PC端基础的接口。
|
|
||||||
|
|
||||||
#### 登录
|
### API 服务
|
||||||
|
基本实现了PC端基础的接口。支持Personal版本接口,可以通过配置文件`rustdesk.personal`或环境变量`RUSTDESK_API_RUSTDESK_PERSONAL`来控制是否启用
|
||||||
|
|
||||||
- 添加了`github`和`google`授权登录,需要在后台配置好就可以用了,具体可看后台OAuth配置
|
<table>
|
||||||
- 添加了web后台授权登录,点击后直接登录后台就自动登录客户端了
|
<tr>
|
||||||
|
<td width="50%" align="center" colspan="2"><b>登录</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td width="50%" align="center" colspan="2"><img src="docs/pc_login.png"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td width="50%" align="center"><b>地址簿</b></td>
|
||||||
|
<td width="50%" align="center"><b>群组</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td width="50%" align="center"><img src="docs/pc_ab.png"></td>
|
||||||
|
<td width="50%" align="center"><img src="docs/pc_gr.png"></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||

|
### Web Admin:
|
||||||
|
|
||||||
#### 地址簿
|
* 使用前后端分离,提供用户友好的管理界面,主要用来管理和展示。前端代码在[rustdesk-api-web](https://github.com/lejianwen/rustdesk-api-web)
|
||||||
|
|
||||||

|
* 后台访问地址是`http://<your server>[:port]/_admin/`
|
||||||
|
* 初次安装管理员为用户名为`admin`,密码将在控制台打印,可以通过[命令行](#CLI)更改密码
|
||||||
|
|
||||||
#### 群组,群组分为`共享组`和`普通组`,共享组中所有人都能看到小组成员的地址,普通组只有管理员能看到所有小组成员的地址
|

|
||||||
|
|
||||||

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

|

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

|

|
||||||
3. 右上角也可以更改密码
|
|
||||||

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

|
5. 可以直接打开webclient,方便使用;也可以分享给游客,游客可以直接通过webclient远程到设备
|
||||||
5. 可以直接打开webclient,方便使用
|
6. Oauth,支持了`Github`, `Google` 以及 `OIDC`, 需要创建一个`OAuth App`,然后配置到后台
|
||||||

|
- 对于`Google` 和 `Github`, `Issuer` 和 `Scopes`不需要填写.
|
||||||
6. Oauth,暂时只支持了`Github`和`Google`, 需要创建一个`OAuth App`,然后配置到后台
|
- 对于`OIDC`, `Issuer`是必须的。`Scopes`是可选的,默认为 `openid,profile,email`. 确保可以获取 `sub`,`email` 和`preferred_username`
|
||||||

|
|
||||||
- `github oauth app`在`Settings`->`Developer settings`->`OAuth Apps`->`New OAuth App`
|
- `github oauth app`在`Settings`->`Developer settings`->`OAuth Apps`->`New OAuth App`
|
||||||
中创建,地址 [https://github.com/settings/developers](https://github.com/settings/developers)
|
中创建,地址 [https://github.com/settings/developers](https://github.com/settings/developers)
|
||||||
- `Authorization callback URL`填写`http://<your server[:port]>/api/oauth/callback`
|
- `Authorization callback URL`填写`http://<your server[:port]>/api/oidc/callback`
|
||||||
,比如`http://127.0.0.1:21114/api/oauth/callback`
|
,比如`http://127.0.0.1:21114/api/oidc/callback`
|
||||||
|
7. 登录日志
|
||||||
|
8. 链接日志
|
||||||
|
9. 文件传输日志
|
||||||
|
10. server控制
|
||||||
|
|
||||||
### **Web Client**:
|
- `简易模式`,已经界面化了一些简单的指令,可以直接在后台执行
|
||||||
|

|
||||||
|
|
||||||
|
- `高级模式`,直接在后台执行指令
|
||||||
|
* 可以官方指令
|
||||||
|
* 可以添加自定义指令
|
||||||
|
* 可以执行自定义指令
|
||||||
|
|
||||||
|
|
||||||
|
11. **LDAP 支持**, 当在API Server上设置了LDAP(已测试AD和LDAP),可以通过LDAP中的用户信息进行登录 https://github.com/lejianwen/rustdesk-api/issues/114 ,如果LDAP验证失败,返回本地用户
|
||||||
|
|
||||||
|
### Web Client:
|
||||||
|
|
||||||
1. 如果已经登录了后台,web client将自动直接登录
|
1. 如果已经登录了后台,web client将自动直接登录
|
||||||
2. 如果没登录后台,点击右上角登录即可,api server已经自动配置好了
|
2. 如果没登录后台,点击右上角登录即可,api server已经自动配置好了
|
||||||

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

|

|
||||||
|
|
||||||
|
### CLI
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 查看帮助
|
||||||
|
./apimain -h
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 重置管理员密码
|
||||||
|
```bash
|
||||||
|
./apimain reset-admin-pwd <pwd>
|
||||||
|
```
|
||||||
|
|
||||||
## 安装与运行
|
## 安装与运行
|
||||||
|
|
||||||
### 相关配置
|
### 相关配置
|
||||||
|
|
||||||
* 参考`conf/config.yaml`配置文件,修改相关配置。如果`gorm.type`是`sqlite`,则不需要配置mysql相关配置。
|
* [配置文件](./conf/config.yaml)
|
||||||
|
* 参考`conf/config.yaml`配置文件,修改相关配置。
|
||||||
|
* 如果`gorm.type`是`sqlite`,则不需要配置mysql相关配置。
|
||||||
|
* 语言如果不设置默认为`zh-CN`
|
||||||
|
|
||||||
```yaml
|
### 环境变量
|
||||||
gin:
|
环境变量和配置文件`conf/config.yaml`中的配置一一对应,变量名前缀是`RUSTDESK_API`
|
||||||
api-addr: "0.0.0.0:21114"
|
下面表格并未全部列出,可以参考`conf/config.yaml`中的配置。
|
||||||
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"
|
|
||||||
```
|
|
||||||
|
|
||||||
* 环境变量,变量名前缀是RUSTDESK_API,环境变量如果存在将覆盖配置文件中的配置
|
| 变量名 | 说明 | 示例 |
|
||||||
|
|--------------------------------------------------------|--------------------------------------------------------------------------------|------------------------------|
|
||||||
|
| TZ | 时区 | Asia/Shanghai |
|
||||||
|
| RUSTDESK_API_LANG | 语言 | `en`,`zh-CN` |
|
||||||
|
| RUSTDESK_API_APP_WEB_CLIENT | 是否启用web-client; 1:启用,0:不启用; 默认启用 | 1 |
|
||||||
|
| RUSTDESK_API_APP_REGISTER | 是否开启注册; `true`, `false` 默认`false` | `false` |
|
||||||
|
| RUSTDESK_API_APP_SHOW_SWAGGER | 是否可见swagger文档;`1`显示,`0`不显示,默认`0`不显示 | `1` |
|
||||||
|
| RUSTDESK_API_APP_TOKEN_EXPIRE | token有效时长 | `168h` |
|
||||||
|
| RUSTDESK_API_APP_DISABLE_PWD_LOGIN | 是否禁用密码登录; `true`, `false` 默认`false` | `false` |
|
||||||
|
| RUSTDESK_API_APP_REGISTER_STATUS | 注册用户默认状态; 1 启用,2 禁用, 默认 1 | `1` |
|
||||||
|
| RUSTDESK_API_APP_CAPTCHA_THRESHOLD | 验证码触发次数; -1 不启用, 0 一直启用, >0 登录错误次数后启用 ;默认 `3` | `3` |
|
||||||
|
| RUSTDESK_API_APP_BAN_THRESHOLD | 封禁IP触发次数; 0 不启用, >0 登录错误次数后封禁IP; 默认 `0` | `0` |
|
||||||
|
| -----ADMIN配置----- | ---------- | ---------- |
|
||||||
|
| RUSTDESK_API_ADMIN_TITLE | 后台标题 | `RustDesk Api Admin` |
|
||||||
|
| RUSTDESK_API_ADMIN_HELLO | 后台欢迎语,可以使用`html` | |
|
||||||
|
| RUSTDESK_API_ADMIN_HELLO_FILE | 后台欢迎语文件,如果内容多,使用文件更方便。<br>会覆盖`RUSTDESK_API_ADMIN_HELLO` | `./conf/admin/hello.html` |
|
||||||
|
| -----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_API_MYSQL_TLS | 是否启用TLS, 可选值: `true`, `false`, `skip-verify`, `custom` | `false` |
|
||||||
|
| -----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 |
|
||||||
|
| RUSTDESK_API_RUSTDESK_KEY_FILE | Rustdesk存放key的文件 | `./conf/data/id_ed25519.pub` |
|
||||||
|
| RUSTDESK_API_RUSTDESK_WEBCLIENT<br/>_MAGIC_QUERYONLINE | Web client v2 中是否启用新的在线状态查询方法; `1`:启用,`0`:不启用,默认不启用 | `0` |
|
||||||
|
| RUSTDESK_API_RUSTDESK_WS_HOST | 自定义Websocket Host | `wss://192.168.1.123:1234` |
|
||||||
|
| ----PROXY配置----- | ---------- | ---------- |
|
||||||
|
| RUSTDESK_API_PROXY_ENABLE | 是否启用代理:`false`, `true` | `false` |
|
||||||
|
| RUSTDESK_API_PROXY_HOST | 代理地址 | `http://127.0.0.1:1080` |
|
||||||
|
| ----JWT配置---- | -------- | -------- |
|
||||||
|
| RUSTDESK_API_JWT_KEY | 自定义JWT KEY,为空则不启用JWT<br/>如果没使用`lejianwen/rustdesk-server`中的`MUST_LOGIN`,建议设置为空 | |
|
||||||
|
| RUSTDESK_API_JWT_EXPIRE_DURATION | JWT有效时间 | `168h` |
|
||||||
|
|
||||||
| 变量名 | 说明 | 示例 |
|
|
||||||
|:------------------------------------|:-------------------------------------|-----------------------------|
|
|
||||||
| TZ | 时区 | Asia/Shanghai |
|
|
||||||
| -----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 |
|
|
||||||
| -----MYSQL配置----- | -----数据库类型为sqlite时不用填----- | ---------- |
|
|
||||||
| 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 |
|
|
||||||
|
|
||||||
### 安装步骤
|
### 运行
|
||||||
|
|
||||||
#### docker运行
|
#### docker运行
|
||||||
|
|
||||||
1. 直接docker运行,配置可以通过挂载配置文件`/app/conf/config.yaml`来修改,或者通过环境变量覆盖配置文件中的配置
|
1. 直接docker运行,配置可以通过挂载配置文件`/app/conf/config.yaml`来修改,或者通过环境变量覆盖配置文件中的配置
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker run -d --name rustdesk-api -p 21114:21114 \
|
docker run -d --name rustdesk-api -p 21114:21114 \
|
||||||
-v /data/rustdesk/api:/app/data \
|
-v /data/rustdesk/api:/app/data \
|
||||||
-e TZ=Asia/Shanghai \
|
-e TZ=Asia/Shanghai \
|
||||||
-e RUSTDESK_API_RUSTDESK_ID_SERVER=192.168.1.66:21116 \
|
-e RUSTDESK_API_LANG=zh-CN \
|
||||||
-e RUSTDESK_API_RUSTDESK_RELAY_SERVER=192.168.1.66:21117 \
|
-e RUSTDESK_API_RUSTDESK_ID_SERVER=192.168.1.66:21116 \
|
||||||
-e RUSTDESK_API_RUSTDESK_API_SERVER=http://192.168.1.66:21114 \
|
-e RUSTDESK_API_RUSTDESK_RELAY_SERVER=192.168.1.66:21117 \
|
||||||
-e RUSTDESK_API_RUSTDESK_KEY=123456789 \
|
-e RUSTDESK_API_RUSTDESK_API_SERVER=http://192.168.1.66:21114 \
|
||||||
lejianwen/rustdesk-api
|
-e RUSTDESK_API_RUSTDESK_KEY=<key> \
|
||||||
```
|
lejianwen/rustdesk-api
|
||||||
|
```
|
||||||
|
|
||||||
2. 使用`docker compose`
|
2. 使用`docker compose`,参考[WIKI](https://github.com/lejianwen/rustdesk-api/wiki)
|
||||||
|
|
||||||
- 简单示例
|
|
||||||
|
|
||||||
```docker-compose
|
|
||||||
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=123456789
|
|
||||||
ports:
|
|
||||||
- 21114:21114
|
|
||||||
image: lejianwen/rustdesk-api
|
|
||||||
volumes:
|
|
||||||
- /data/rustdesk/api:/app/data #将数据库挂载出来方便备份
|
|
||||||
networks:
|
|
||||||
- rustdesk-net
|
|
||||||
restart: unless-stopped
|
|
||||||
```
|
|
||||||
|
|
||||||
- 根据rustdesk提供的示例加上自己的rustdesk-api
|
|
||||||
|
|
||||||
```docker-compose
|
|
||||||
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 123456789 # 填入个人域名或 IP + hbbr 暴露端口
|
|
||||||
volumes:
|
|
||||||
- /data/rustdesk/hbbs:/root # 自定义挂载目录
|
|
||||||
networks:
|
|
||||||
- rustdesk-net
|
|
||||||
depends_on:
|
|
||||||
- hbbr
|
|
||||||
restart: unless-stopped
|
|
||||||
deploy:
|
|
||||||
resources:
|
|
||||||
limits:
|
|
||||||
memory: 64M
|
|
||||||
hbbr:
|
|
||||||
container_name: hbbr
|
|
||||||
ports:
|
|
||||||
- 21117:21117 # 自定义 hbbr 映射端口
|
|
||||||
image: rustdesk/rustdesk-server
|
|
||||||
command: hbbr -k 123456789
|
|
||||||
#command: hbbr
|
|
||||||
volumes:
|
|
||||||
- /data/rustdesk/hbbr:/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=123456789
|
|
||||||
ports:
|
|
||||||
- 21114:21114
|
|
||||||
image: lejianwen/rustdesk-api
|
|
||||||
volumes:
|
|
||||||
- /data/rustdesk/api:/app/data #将数据库挂载出来方便备份
|
|
||||||
networks:
|
|
||||||
- rustdesk-net
|
|
||||||
restart: unless-stopped
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 下载release直接运行
|
#### 下载release直接运行
|
||||||
|
|
||||||
下载地址[release](https://github.com/lejianwen/rustdesk-api/releases)
|
[下载地址](https://github.com/lejianwen/rustdesk-api/releases)
|
||||||
|
|
||||||
#### 源码安装
|
#### 源码安装
|
||||||
|
|
||||||
@@ -277,12 +261,75 @@ lejianwen/rustdesk-api
|
|||||||
#或者使用generate_api.go生成api并运行
|
#或者使用generate_api.go生成api并运行
|
||||||
go generate generate_api.go
|
go generate generate_api.go
|
||||||
```
|
```
|
||||||
|
> 注意:使用 `go run` 或编译后的二进制时,当前目录下必须存在 `conf` 和 `resources`
|
||||||
|
> 目录。如果在其他目录运行,可通过 `-c` 和环境变量
|
||||||
|
> `RUSTDESK_API_GIN_RESOURCES_PATH` 指定绝对路径,例如:
|
||||||
|
> ```bash
|
||||||
|
> RUSTDESK_API_GIN_RESOURCES_PATH=/opt/rustdesk-api/resources ./apimain -c /opt/rustdesk-api/conf/config.yaml
|
||||||
|
> ```
|
||||||
5. 编译,如果想自己编译,先cd到项目根目录,然后windows下直接运行`build.bat`,linux下运行`build.sh`,编译后会在`release`
|
5. 编译,如果想自己编译,先cd到项目根目录,然后windows下直接运行`build.bat`,linux下运行`build.sh`,编译后会在`release`
|
||||||
目录下生成对应的可执行文件。直接运行编译后的可执行文件即可。
|
目录下生成对应的可执行文件。直接运行编译后的可执行文件即可。
|
||||||
|
|
||||||
6. 打开浏览器访问`http://<your server>:21114/_admin/`,默认用户名密码为`admin`,请及时更改密码。
|
6. 打开浏览器访问`http://<your server[:port]>/_admin/`,默认用户名密码为`admin`,请及时更改密码。
|
||||||
|
|
||||||
|
|
||||||
|
#### 使用`lejianwen/server-s6`镜像运行
|
||||||
|
|
||||||
|
- 已解决链接超时问题
|
||||||
|
- 可以强制登录后才能发起链接
|
||||||
|
- github https://github.com/lejianwen/rustdesk-server
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
networks:
|
||||||
|
rustdesk-net:
|
||||||
|
external: false
|
||||||
|
services:
|
||||||
|
rustdesk:
|
||||||
|
ports:
|
||||||
|
- 21114:21114
|
||||||
|
- 21115:21115
|
||||||
|
- 21116:21116
|
||||||
|
- 21116:21116/udp
|
||||||
|
- 21117:21117
|
||||||
|
- 21118:21118
|
||||||
|
- 21119:21119
|
||||||
|
image: lejianwen/rustdesk-server-s6:latest
|
||||||
|
environment:
|
||||||
|
- RELAY=<relay_server[:port]>
|
||||||
|
- ENCRYPTED_ONLY=1
|
||||||
|
- MUST_LOGIN=N
|
||||||
|
- TZ=Asia/Shanghai
|
||||||
|
- RUSTDESK_API_RUSTDESK_ID_SERVER=<id_server[:21116]>
|
||||||
|
- RUSTDESK_API_RUSTDESK_RELAY_SERVER=<relay_server[:21117]>
|
||||||
|
- RUSTDESK_API_RUSTDESK_API_SERVER=http://<api_server[:21114]>
|
||||||
|
- RUSTDESK_API_KEY_FILE=/data/id_ed25519.pub
|
||||||
|
- RUSTDESK_API_JWT_KEY=xxxxxx # jwt key
|
||||||
|
volumes:
|
||||||
|
- /data/rustdesk/server:/data
|
||||||
|
- /data/rustdesk/api:/app/data #将数据库挂载
|
||||||
|
networks:
|
||||||
|
- rustdesk-net
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
## 其他
|
## 其他
|
||||||
|
|
||||||
|
- [WIKI](https://github.com/lejianwen/rustdesk-api/wiki)
|
||||||
|
- [链接超时问题](https://github.com/lejianwen/rustdesk-api/issues/92)
|
||||||
- [修改客户端ID](https://github.com/abdullah-erturk/RustDesk-ID-Changer)
|
- [修改客户端ID](https://github.com/abdullah-erturk/RustDesk-ID-Changer)
|
||||||
- [webclient](https://hub.docker.com/r/keyurbhole/flutter_web_desk)
|
- [webclient来源](https://hub.docker.com/r/keyurbhole/flutter_web_desk)
|
||||||
|
|
||||||
|
|
||||||
|
## 鸣谢
|
||||||
|
|
||||||
|
感谢所有做过贡献的人!
|
||||||
|
|
||||||
|
<a href="https://github.com/lejianwen/rustdesk-api/graphs/contributors">
|
||||||
|
<img src="https://contrib.rocks/image?repo=lejianwen/rustdesk-api" />
|
||||||
|
</a>
|
||||||
|
|
||||||
|
## 感谢你的支持!如果这个项目对你有帮助,请点个⭐️鼓励一下,谢谢!
|
||||||
|
|
||||||
|
[lejianwen/rustdesk-server]: https://github.com/lejianwen/rustdesk-server
|
||||||
432
README_EN.md
@@ -8,138 +8,198 @@ desktop software that provides self-hosted solutions.
|
|||||||
<img src="https://img.shields.io/badge/gin-v1.9.0-lightBlue"/>
|
<img src="https://img.shields.io/badge/gin-v1.9.0-lightBlue"/>
|
||||||
<img src="https://img.shields.io/badge/gorm-v1.25.7-green"/>
|
<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://img.shields.io/badge/swag-v1.16.3-yellow"/>
|
||||||
|
<img src="https://goreportcard.com/badge/github.com/lejianwen/rustdesk-api/v2"/>
|
||||||
|
<img src="https://github.com/lejianwen/rustdesk-api/actions/workflows/build.yml/badge.svg"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
## Prerequisites
|
## Better used with [lejianwen/rustdesk-server].
|
||||||
|
> [lejianwen/rustdesk-server] is a fork of the official RustDesk Server repository.
|
||||||
|
> 1. Solves the API connection timeout issue.
|
||||||
|
> 2. Can enforce login before initiating a connection.
|
||||||
|
> 3. Supports client websocket.
|
||||||
|
|
||||||
### [Rustdesk](https://github.com/rustdesk/rustdesk)
|
|
||||||
|
|
||||||
1. The PC client version used is ***1.3.0***, and versions ***1.2.6+*** have been tested to work.
|
# Features
|
||||||
2. The server must specify a key, and not use the auto-generated key, otherwise there may be connection failures or
|
|
||||||
timeouts.
|
|
||||||
|
|
||||||
```bash
|
- PC API
|
||||||
hbbs -r <relay-server-ip[:port]> -k <key>
|
- Personal API
|
||||||
hbbr -k <key>
|
- Login
|
||||||
```
|
- Address Book
|
||||||
|
- Groups
|
||||||
|
- Authorized login,
|
||||||
|
- supports `GitHub`, `Google` and `OIDC` login,
|
||||||
|
- supports `web admin` authorized login,
|
||||||
|
- supports LDAP(test AD and openladp) if API Server config
|
||||||
|
- i18n
|
||||||
|
- Web Admin
|
||||||
|
- User Management
|
||||||
|
- Device Management
|
||||||
|
- Address Book Management
|
||||||
|
- Tag Management
|
||||||
|
- Group Management
|
||||||
|
- OAuth Management
|
||||||
|
- LDAP Config by config file or ENV
|
||||||
|
- Login Logs
|
||||||
|
- Connection Logs
|
||||||
|
- File Transfer Logs
|
||||||
|
- Quick access to web client
|
||||||
|
- i18n
|
||||||
|
- Share to guest by web client
|
||||||
|
- Server control (some simple official commands [WIKI](https://github.com/lejianwen/rustdesk-api/wiki/Rustdesk-Command))
|
||||||
|
- 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
|
||||||
|
- CLI
|
||||||
|
- Reset admin password
|
||||||
|
|
||||||
Example:
|
## Overview
|
||||||
|
|
||||||
```bash
|
### API Service
|
||||||
hbbs -r <relay-server-ip[:port]> -k abc1234567
|
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.
|
||||||
hbbr -k abc1234567
|
|
||||||
```
|
|
||||||
|
|
||||||
## Features
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td width="50%" align="center" colspan="2"><b>Login</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td width="50%" align="center" colspan="2"><img src="docs/en_img/pc_login.png"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td width="50%" align="center"><b>Address Book</b></td>
|
||||||
|
<td width="50%" align="center"><b>Groups</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td width="50%" align="center"><img src="docs/en_img/pc_ab.png"></td>
|
||||||
|
<td width="50%" align="center"><img src="docs/en_img/pc_gr.png"></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
### API Service: Basic implementation of the PC client's primary interfaces.
|
### Web Admin
|
||||||
|
|
||||||
#### Login
|
* 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)
|
||||||
|
|
||||||
- Added `GitHub` and `Google` login, which can be used after configuration in the admin panel. See the OAuth configuration section
|
* Admin panel URL: `http://<your server[:port]>/_admin/`
|
||||||
for details.
|
* For the initial installation, the admin username is `admin`, and the password will be printed in the console. You can change the password via the [command line](#CLI).
|
||||||
- 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 addresses of all group members, while in regular groups, only administrators can see all members' addresses.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### **Web UI**: 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:
|
1. Admin interface:
|
||||||

|

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

|

|
||||||
3. You can change your password from the top right corner:
|
|
||||||

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

|
5. You can directly launch the client or open the web client for convenience; you can also share it with guests, who can remotely access the device via the web client.
|
||||||
5. You can open the web client directly for convenience:
|
6. OAuth support: Currently, `GitHub`, `Google` and `OIDC` are supported. You need to create an `OAuth App` and configure it in
|
||||||

|
the admin panel.
|
||||||
6. OAuth support: Currently, `GitHub` and `Google` is supported. You need to create an `OAuth App` and configure it in the admin
|
- For `Google` and `Github`, you don't need to fill the `Issuer` and `Scpoes`
|
||||||
panel.
|
- 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`
|
- Create a `GitHub OAuth App`
|
||||||
at `Settings` -> `Developer settings` -> `OAuth Apps` -> `New OAuth App` [here](https://github.com/settings/developers).
|
at `Settings` -> `Developer settings` -> `OAuth Apps` -> `New OAuth App` [here](https://github.com/settings/developers).
|
||||||
- Set the `Authorization callback URL` to `http://<your server[:port]>/api/oauth/callback`,
|
- Set the `Authorization callback URL` to `http://<your server[:port]>/api/oidc/callback`,
|
||||||
e.g., `http://127.0.0.1:21114/api/oauth/callback`.
|
e.g., `http://127.0.0.1:21114/api/oidc/callback`.
|
||||||
|
|
||||||
|
7. Login logs
|
||||||
|
8. Connection logs
|
||||||
|
9. File transfer logs
|
||||||
|
10. Server control
|
||||||
|
- `Simple mode`, some simple commands have been GUI-ized and can be executed directly in the backend
|
||||||
|

|
||||||
|
|
||||||
### **Web Client**:
|
- `Advanced mode`, commands can be executed directly in the backend
|
||||||
|
* Official commands can be used
|
||||||
|
* Custom commands can be added
|
||||||
|
* Custom commands can be executed
|
||||||
|
|
||||||
|
11. **LDAP Support**, When you setup the LDAP(test for OpenLDAP and AD), you can login with the LDAP's user. https://github.com/lejianwen/rustdesk-api/issues/114 , if LDAP fail fallback local user
|
||||||
|
|
||||||
|
### Web Client:
|
||||||
|
|
||||||
1. If you're already logged into the admin panel, the web client will log in automatically.
|
1. If you're already logged into the admin panel, the web client will log in automatically.
|
||||||
2. If you're not logged in, simply click the login button at the top right corner, and the API server will be
|
2. If you're not logged in, simply click the login button in the top right corner, and the API server will be
|
||||||
pre-configured.
|
pre-configured.
|
||||||

|
|
||||||
3. After logging in, the ID server and key will be automatically synced.
|
3. After logging in, the ID server and key will be automatically synced.
|
||||||
4. The address book will also be automatically saved to the web client for convenient use.
|
4. The address book will also be automatically saved to the web client for convenient use.
|
||||||
|
|
||||||
### **Automated Documentation** : API documentation is generated using Swag, making it easier for developers to understand and use the API.
|
### Automated Documentation : API documentation is generated using Swag, making it easier for developers to understand and use the API.
|
||||||
|
|
||||||
1. Admin panel docs: `<your server>/admin/swagger/index.html`
|
1. Admin panel docs: `<your server[:port]>/admin/swagger/index.html`
|
||||||
2. PC client docs: `<your server>/swagger/index.html`
|
2. PC client docs: `<your server[:port]>/swagger/index.html`
|
||||||

|

|
||||||
|
|
||||||
|
### CLI
|
||||||
|
```bash
|
||||||
|
# help
|
||||||
|
./apimain -h
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Reset admin password
|
||||||
|
```bash
|
||||||
|
./apimain reset-admin-pwd <pwd>
|
||||||
|
```
|
||||||
|
|
||||||
## Installation and Setup
|
## Installation and Setup
|
||||||
|
|
||||||
### Configuration
|
### Configuration
|
||||||
|
|
||||||
* Modify the configuration in `conf/config.yaml`. If `gorm.type` is set to `sqlite`, MySQL-related configurations are
|
* [Config File](./conf/config.yaml)
|
||||||
not required.
|
* 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
|
|
||||||
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"
|
|
||||||
```
|
|
||||||
|
|
||||||
* Environment variables, with the prefix `RUSTDESK_API`, will override the settings in the configuration file if
|
### Environment Variables
|
||||||
present.
|
The environment variables correspond one-to-one with the configurations in the `conf/config.yaml` file. The prefix for variable names is `RUSTDESK_API`.
|
||||||
|
The table below does not list all configurations. Please refer to the configurations in `conf/config.yaml`.
|
||||||
|
|
||||||
| Variable Name | Description | Example |
|
| Variable Name | Description | Example |
|
||||||
|------------------------------------|-----------------------------------------------------------|--------------------------------|
|
|--------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------|
|
||||||
| ----- GIN Configuration ----- | --------------------------------------- | ------------------------------ |
|
| TZ | timezone | Asia/Shanghai |
|
||||||
| RUSTDESK_API_GIN_TRUST_PROXY | Trusted proxy IPs, separated by commas. | 192.168.1.2,192.168.1.3 |
|
| RUSTDESK_API_LANG | Language | `en`,`zh-CN` |
|
||||||
| ----- GORM Configuration ----- | --------------------------------------- | ------------------------------ |
|
| RUSTDESK_API_APP_WEB_CLIENT | web client on/off; 1: on, 0 off, default: 1 | 1 |
|
||||||
| RUSTDESK_API_GORM_TYPE | Database type (`sqlite` or `mysql`). Default is `sqlite`. | sqlite |
|
| RUSTDESK_API_APP_REGISTER | register enable; `true`, `false`; default:`false` | `false` |
|
||||||
| RUSTDESK_API_GORM_MAX_IDLE_CONNS | Maximum idle connections | 10 |
|
| RUSTDESK_API_APP_SHOW_SWAGGER | swagger visible; 1: yes, 0: no; default: 0 | `0` |
|
||||||
| RUSTDESK_API_GORM_MAX_OPEN_CONNS | Maximum open connections | 100 |
|
| RUSTDESK_API_APP_TOKEN_EXPIRE | token expire duration | `168h` |
|
||||||
| ----- MYSQL Configuration ----- | --------------------------------------- | ------------------------------ |
|
| RUSTDESK_API_APP_DISABLE_PWD_LOGIN | disable password login | `false` |
|
||||||
| RUSTDESK_API_MYSQL_USERNAME | MySQL username | root |
|
| RUSTDESK_API_APP_REGISTER_STATUS | register user default status ; 1 enabled , 2 disabled ; default 1 | `1` |
|
||||||
| RUSTDESK_API_MYSQL_PASSWORD | MySQL password | 111111 |
|
| RUSTDESK_API_APP_CAPTCHA_THRESHOLD | captcha threshold; -1 disabled, 0 always enable, >0 threshold ;default `3` | `3` |
|
||||||
| RUSTDESK_API_MYSQL_ADDR | MySQL address | 192.168.1.66:3306 |
|
| RUSTDESK_API_APP_BAN_THRESHOLD | ban ip threshold; 0 disabled, >0 threshold ; default `0` | `0` |
|
||||||
| RUSTDESK_API_MYSQL_DBNAME | MySQL database name | rustdesk |
|
| ----- ADMIN Configuration----- | ---------- | ---------- |
|
||||||
| ----- RUSTDESK Configuration ----- | --------------------------------------- | ------------------------------ |
|
| RUSTDESK_API_ADMIN_TITLE | Admin Title | `RustDesk Api Admin` |
|
||||||
| RUSTDESK_API_RUSTDESK_ID_SERVER | Rustdesk ID server address | 192.168.1.66:21116 |
|
| RUSTDESK_API_ADMIN_HELLO | Admin welcome message, you can use `html` | |
|
||||||
| RUSTDESK_API_RUSTDESK_RELAY_SERVER | Rustdesk relay server address | 192.168.1.66:21117 |
|
| RUSTDESK_API_ADMIN_HELLO_FILE | Admin welcome message file,<br>will override `RUSTDESK_API_ADMIN_HELLO` | `./conf/admin/hello.html` |
|
||||||
| RUSTDESK_API_RUSTDESK_API_SERVER | Rustdesk API server address | http://192.168.1.66:21114 |
|
| ----- GIN Configuration ----- | --------------------------------------- | ----------------------------- |
|
||||||
| RUSTDESK_API_RUSTDESK_KEY | Rustdesk key | 123456789 |
|
| 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_API_MYSQL_TLS | Whether to enable TLS, optional values: `true`, `false`, `skip-verify`, `custom` | `false` |
|
||||||
|
| ----- 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 |
|
||||||
|
| RUSTDESK_API_RUSTDESK_KEY_FILE | Rustdesk key file | `./conf/data/id_ed25519.pub` |
|
||||||
|
| RUSTDESK_API_RUSTDESK<br/>_WEBCLIENT_MAGIC_QUERYONLINE | New online query method is enabled in the web client v2; '1': Enabled, '0': Disabled, not enabled by default | `0` |
|
||||||
|
| RUSTDESK_API_RUSTDESK_WS_HOST | Custom Websocket Host | `wss://192.168.1.123:1234` |
|
||||||
|
| ---- PROXY ----- | --------------- | ---------- |
|
||||||
|
| RUSTDESK_API_PROXY_ENABLE | proxy_enable :`false`, `true` | `false` |
|
||||||
|
| RUSTDESK_API_PROXY_HOST | proxy_host | `http://127.0.0.1:1080` |
|
||||||
|
| ----JWT---- | -------- | -------- |
|
||||||
|
| RUSTDESK_API_JWT_KEY | Custom JWT KEY, if empty JWT is not enabled.<br/>If `MUST_LOGIN` from `lejianwen/rustdesk-server` is not used, it is recommended to leave it empty. | |
|
||||||
|
| RUSTDESK_API_JWT_EXPIRE_DURATION | JWT expire duration | `168h` |
|
||||||
|
|
||||||
### Installation Steps
|
### Installation Steps
|
||||||
|
|
||||||
@@ -147,100 +207,19 @@ rustdesk:
|
|||||||
|
|
||||||
1. Run directly with Docker. Configuration can be modified by mounting the config file `/app/conf/config.yaml`, or by
|
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.
|
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
|
||||||
|
```
|
||||||
|
|
||||||
```bash
|
2. Using `docker-compose`,look [WIKI](https://github.com/lejianwen/rustdesk-api/wiki)
|
||||||
docker run -d --name rustdesk-api -p 21114:21114 \
|
|
||||||
-v /data/rustdesk/api:/app/data \
|
|
||||||
-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=123456789 \
|
|
||||||
lejianwen/rustdesk-api
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Using `docker-compose`
|
|
||||||
|
|
||||||
- Simple example:
|
|
||||||
|
|
||||||
```docker-compose
|
|
||||||
services:
|
|
||||||
rustdesk-api:
|
|
||||||
container_name: rustdesk-api
|
|
||||||
environment:
|
|
||||||
- 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=123456789
|
|
||||||
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:
|
|
||||||
|
|
||||||
```docker-compose
|
|
||||||
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 123456789 # 填入个人域名或 IP + hbbr 暴露端口
|
|
||||||
volumes:
|
|
||||||
- /data/rustdesk/hbbs:/root # 自定义挂载目录
|
|
||||||
networks:
|
|
||||||
- rustdesk-net
|
|
||||||
depends_on:
|
|
||||||
- hbbr
|
|
||||||
restart: unless-stopped
|
|
||||||
deploy:
|
|
||||||
resources:
|
|
||||||
limits:
|
|
||||||
memory: 64M
|
|
||||||
hbbr:
|
|
||||||
container_name: hbbr
|
|
||||||
ports:
|
|
||||||
- 21117:21117 # 自定义 hbbr 映射端口
|
|
||||||
image: rustdesk/rustdesk-server
|
|
||||||
command: hbbr -k 123456789
|
|
||||||
#command: hbbr
|
|
||||||
volumes:
|
|
||||||
- /data/rustdesk/hbbr:/root # 自定义挂载目录
|
|
||||||
networks:
|
|
||||||
- rustdesk-net
|
|
||||||
restart: unless-stopped
|
|
||||||
deploy:
|
|
||||||
resources:
|
|
||||||
limits:
|
|
||||||
memory: 64M
|
|
||||||
rustdesk-api:
|
|
||||||
container_name: rustdesk-api
|
|
||||||
environment:
|
|
||||||
- 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=123456789
|
|
||||||
ports:
|
|
||||||
- 21114:21114
|
|
||||||
image: lejianwen/rustdesk-api
|
|
||||||
volumes:
|
|
||||||
- /data/rustdesk/api:/app/data #将数据库挂载出来方便备份
|
|
||||||
networks:
|
|
||||||
- rustdesk-net
|
|
||||||
restart: unless-stopped
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Running from Release
|
#### Running from Release
|
||||||
|
|
||||||
@@ -277,19 +256,80 @@ Download the release from [release](https://github.com/lejianwen/rustdesk-api/re
|
|||||||
4. Run:
|
4. Run:
|
||||||
```bash
|
```bash
|
||||||
# Run directly
|
# Run directly
|
||||||
go run cmd/apimain.go
|
go run cmd/apimain.go
|
||||||
# Or generate and run the API using generate_api.go
|
# Or generate and run the API using generate_api.go
|
||||||
go generate generate_api.go
|
go generate generate_api.go
|
||||||
```
|
```
|
||||||
|
> **Note:** When using `go run` or the compiled binary, the `conf` and `resources`
|
||||||
|
> directories must exist relative to the current working directory. If you run
|
||||||
|
> the program from another location, specify absolute paths with `-c` and the
|
||||||
|
> `RUSTDESK_API_GIN_RESOURCES_PATH` environment variable. Example:
|
||||||
|
> ```bash
|
||||||
|
> RUSTDESK_API_GIN_RESOURCES_PATH=/opt/rustdesk-api/resources ./apimain -c /opt/rustdesk-api/conf/config.yaml
|
||||||
|
> ```
|
||||||
|
|
||||||
5. To compile, change to the project root directory. For Windows, run `build.bat`, and for Linux, run `build.sh`. After
|
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
|
compiling, the corresponding executables will be generated in the `release` directory. Run the compiled executables
|
||||||
directly.
|
directly.
|
||||||
|
|
||||||
6. Open your browser and visit `http://<your server>:21114/_admin/`, with default credentials `admin admin`. Please
|
6. Open your browser and visit `http://<your server[:port]>/_admin/`, with default credentials `admin admin`. Please
|
||||||
change the password promptly.
|
change the password promptly.
|
||||||
|
|
||||||
## Miscellaneous
|
#### Running with my forked server-s6 image
|
||||||
|
|
||||||
|
- Connection timeout issue resolved
|
||||||
|
- Can enforce login before initiating a connection
|
||||||
|
- github https://github.com/lejianwen/rustdesk-server
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
networks:
|
||||||
|
rustdesk-net:
|
||||||
|
external: false
|
||||||
|
services:
|
||||||
|
rustdesk:
|
||||||
|
ports:
|
||||||
|
- 21114:21114
|
||||||
|
- 21115:21115
|
||||||
|
- 21116:21116
|
||||||
|
- 21116:21116/udp
|
||||||
|
- 21117:21117
|
||||||
|
- 21118:21118
|
||||||
|
- 21119:21119
|
||||||
|
image: lejianwen/rustdesk-server-s6:latest
|
||||||
|
environment:
|
||||||
|
- RELAY=<relay_server[:port]>
|
||||||
|
- ENCRYPTED_ONLY=1
|
||||||
|
- MUST_LOGIN=N
|
||||||
|
- TZ=Asia/Shanghai
|
||||||
|
- RUSTDESK_API_RUSTDESK_ID_SERVER=<id_server[:21116]>
|
||||||
|
- RUSTDESK_API_RUSTDESK_RELAY_SERVER=<relay_server[:21117]>
|
||||||
|
- RUSTDESK_API_RUSTDESK_API_SERVER=http://<api_server[:21114]>
|
||||||
|
- RUSTDESK_API_KEY_FILE=/data/id_ed25519.pub
|
||||||
|
- RUSTDESK_API_JWT_KEY=xxxxxx # jwt key
|
||||||
|
volumes:
|
||||||
|
- /data/rustdesk/server:/data
|
||||||
|
- /data/rustdesk/api:/app/data #将数据库挂载
|
||||||
|
networks:
|
||||||
|
- rustdesk-net
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
```
|
||||||
|
## Others
|
||||||
|
|
||||||
|
- [WIKI](https://github.com/lejianwen/rustdesk-api/wiki)
|
||||||
|
- [Connection Timeout](https://github.com/lejianwen/rustdesk-api/issues/92)
|
||||||
- [Change client ID](https://github.com/abdullah-erturk/RustDesk-ID-Changer)
|
- [Change client ID](https://github.com/abdullah-erturk/RustDesk-ID-Changer)
|
||||||
- [webclient](https://hub.docker.com/r/keyurbhole/flutter_web_desk)
|
- [Web client source](https://hub.docker.com/r/keyurbhole/flutter_web_desk)
|
||||||
|
|
||||||
|
## Acknowledgements
|
||||||
|
|
||||||
|
Thanks to everyone who contributed!
|
||||||
|
|
||||||
|
<a href="https://github.com/lejianwen/rustdesk-api/graphs/contributors">
|
||||||
|
<img src="https://contrib.rocks/image?repo=lejianwen/rustdesk-api" />
|
||||||
|
</a>
|
||||||
|
|
||||||
|
## Thanks for your support! If you find this project useful, please give it a ⭐️. Thank you!
|
||||||
|
|
||||||
|
|
||||||
|
[lejianwen/rustdesk-server]: https://github.com/lejianwen/rustdesk-server
|
||||||
38
build.sh
Normal file → Executable file
@@ -1,16 +1,46 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
rm release -rf
|
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 GO111MODULE=on
|
||||||
go env -w GOPROXY=https://goproxy.cn,direct
|
go env -w GOPROXY=https://goproxy.cn,direct
|
||||||
go env -w CGO_ENABLED=1
|
go env -w CGO_ENABLED=1
|
||||||
go env -w GOOS=linux
|
go env -w GOOS=linux
|
||||||
go env -w GOARCH=amd64
|
go env -w GOARCH=${GOARCH}
|
||||||
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
|
|
||||||
|
# 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
|
go build -o release/apimain cmd/apimain.go
|
||||||
|
|
||||||
|
# Copy resource files to the release directory
|
||||||
cp -ar resources release/
|
cp -ar resources release/
|
||||||
cp -ar docs release/
|
cp -ar docs release/
|
||||||
cp -ar conf release/
|
cp -ar conf release/
|
||||||
|
|
||||||
|
# Create necessary directory structures
|
||||||
mkdir -p release/data
|
mkdir -p release/data
|
||||||
mkdir -p release/runtime
|
mkdir -p release/runtime
|
||||||
|
|
||||||
|
echo "Build and setup completed successfully."
|
||||||
290
cmd/apimain.go
@@ -1,26 +1,30 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"Gwen/config"
|
|
||||||
"Gwen/global"
|
|
||||||
"Gwen/http"
|
|
||||||
"Gwen/lib/cache"
|
|
||||||
"Gwen/lib/lock"
|
|
||||||
"Gwen/lib/logger"
|
|
||||||
"Gwen/lib/orm"
|
|
||||||
"Gwen/lib/upload"
|
|
||||||
"Gwen/model"
|
|
||||||
"Gwen/service"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/go-playground/locales/en"
|
"os"
|
||||||
"github.com/go-playground/locales/zh_Hans_CN"
|
"strconv"
|
||||||
ut "github.com/go-playground/universal-translator"
|
"time"
|
||||||
"github.com/go-playground/validator/v10"
|
|
||||||
zh_translations "github.com/go-playground/validator/v10/translations/zh"
|
|
||||||
"github.com/go-redis/redis/v8"
|
"github.com/go-redis/redis/v8"
|
||||||
"reflect"
|
"github.com/lejianwen/rustdesk-api/v2/config"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/global"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/http"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/lib/cache"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/lib/jwt"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/lib/lock"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/lib/logger"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/lib/orm"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/lib/upload"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/model"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/service"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/utils"
|
||||||
|
"github.com/nicksnyder/go-i18n/v2/i18n"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const DatabaseVersion = 265
|
||||||
|
|
||||||
// @title 管理系统API
|
// @title 管理系统API
|
||||||
// @version 1.0
|
// @version 1.0
|
||||||
// @description 接口
|
// @description 接口
|
||||||
@@ -31,9 +35,84 @@ import (
|
|||||||
// @securitydefinitions.apikey BearerAuth
|
// @securitydefinitions.apikey BearerAuth
|
||||||
// @in header
|
// @in header
|
||||||
// @name Authorization
|
// @name Authorization
|
||||||
|
|
||||||
|
var rootCmd = &cobra.Command{
|
||||||
|
Use: "apimain",
|
||||||
|
Short: "RUSTDESK API SERVER",
|
||||||
|
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
||||||
|
InitGlobal()
|
||||||
|
},
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
global.Logger.Info("API SERVER START")
|
||||||
|
http.ApiInit()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var resetPwdCmd = &cobra.Command{
|
||||||
|
Use: "reset-admin-pwd [pwd]",
|
||||||
|
Example: "reset-admin-pwd 123456",
|
||||||
|
Short: "Reset Admin Password",
|
||||||
|
Args: cobra.ExactArgs(1),
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
pwd := args[0]
|
||||||
|
admin := service.AllService.UserService.InfoById(1)
|
||||||
|
if admin.Id == 0 {
|
||||||
|
global.Logger.Warn("user not found! ")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err := service.AllService.UserService.UpdatePassword(admin, pwd)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Error("reset password fail! ", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
global.Logger.Info("reset password success! ")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
var resetUserPwdCmd = &cobra.Command{
|
||||||
|
Use: "reset-pwd [userId] [pwd]",
|
||||||
|
Example: "reset-pwd 2 123456",
|
||||||
|
Short: "Reset User Password",
|
||||||
|
Args: cobra.ExactArgs(2),
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
userId := args[0]
|
||||||
|
pwd := args[1]
|
||||||
|
uid, err := strconv.Atoi(userId)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Warn("userId must be int!")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if uid <= 0 {
|
||||||
|
global.Logger.Warn("userId must be greater than 0! ")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
u := service.AllService.UserService.InfoById(uint(uid))
|
||||||
|
if u.Id == 0 {
|
||||||
|
global.Logger.Warn("user not found! ")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = service.AllService.UserService.UpdatePassword(u, pwd)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Warn("reset password fail! ", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
global.Logger.Info("reset password success!")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.PersistentFlags().StringVarP(&global.ConfigPath, "config", "c", "./conf/config.yaml", "choose config file")
|
||||||
|
rootCmd.AddCommand(resetPwdCmd, resetUserPwdCmd)
|
||||||
|
}
|
||||||
func main() {
|
func main() {
|
||||||
|
if err := rootCmd.Execute(); err != nil {
|
||||||
|
global.Logger.Error(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func InitGlobal() {
|
||||||
//配置解析
|
//配置解析
|
||||||
global.Viper = config.Init(&global.Config)
|
global.Viper = config.Init(&global.Config, global.ConfigPath)
|
||||||
|
|
||||||
//日志
|
//日志
|
||||||
global.Logger = logger.New(&logger.Config{
|
global.Logger = logger.New(&logger.Config{
|
||||||
@@ -42,6 +121,8 @@ func main() {
|
|||||||
ReportCaller: global.Config.Logger.ReportCaller,
|
ReportCaller: global.Config.Logger.ReportCaller,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
global.InitI18n()
|
||||||
|
|
||||||
//redis
|
//redis
|
||||||
global.Redis = redis.NewClient(&redis.Options{
|
global.Redis = redis.NewClient(&redis.Options{
|
||||||
Addr: global.Config.Redis.Addr,
|
Addr: global.Config.Redis.Addr,
|
||||||
@@ -63,23 +144,45 @@ func main() {
|
|||||||
}
|
}
|
||||||
//gorm
|
//gorm
|
||||||
if global.Config.Gorm.Type == config.TypeMysql {
|
if global.Config.Gorm.Type == config.TypeMysql {
|
||||||
dns := global.Config.Mysql.Username + ":" + global.Config.Mysql.Password + "@(" + global.Config.Mysql.Addr + ")/" + global.Config.Mysql.Dbname + "?charset=utf8mb4&parseTime=True&loc=Local"
|
|
||||||
|
dsn := fmt.Sprintf("%s:%s@(%s)/%s?charset=utf8mb4&parseTime=True&loc=Local&tls=%s",
|
||||||
|
global.Config.Mysql.Username,
|
||||||
|
global.Config.Mysql.Password,
|
||||||
|
global.Config.Mysql.Addr,
|
||||||
|
global.Config.Mysql.Dbname,
|
||||||
|
global.Config.Mysql.Tls,
|
||||||
|
)
|
||||||
|
|
||||||
global.DB = orm.NewMysql(&orm.MysqlConfig{
|
global.DB = orm.NewMysql(&orm.MysqlConfig{
|
||||||
Dns: dns,
|
Dsn: dsn,
|
||||||
MaxIdleConns: global.Config.Gorm.MaxIdleConns,
|
MaxIdleConns: global.Config.Gorm.MaxIdleConns,
|
||||||
MaxOpenConns: global.Config.Gorm.MaxOpenConns,
|
MaxOpenConns: global.Config.Gorm.MaxOpenConns,
|
||||||
})
|
}, global.Logger)
|
||||||
|
} else if global.Config.Gorm.Type == config.TypePostgresql {
|
||||||
|
dsn := fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=%s TimeZone=%s",
|
||||||
|
global.Config.Postgresql.Host,
|
||||||
|
global.Config.Postgresql.Port,
|
||||||
|
global.Config.Postgresql.User,
|
||||||
|
global.Config.Postgresql.Password,
|
||||||
|
global.Config.Postgresql.Dbname,
|
||||||
|
global.Config.Postgresql.Sslmode,
|
||||||
|
global.Config.Postgresql.TimeZone,
|
||||||
|
)
|
||||||
|
global.DB = orm.NewPostgresql(&orm.PostgresqlConfig{
|
||||||
|
Dsn: dsn,
|
||||||
|
MaxIdleConns: global.Config.Gorm.MaxIdleConns,
|
||||||
|
MaxOpenConns: global.Config.Gorm.MaxOpenConns,
|
||||||
|
}, global.Logger)
|
||||||
} else {
|
} else {
|
||||||
//sqlite
|
//sqlite
|
||||||
global.DB = orm.NewSqlite(&orm.SqliteConfig{
|
global.DB = orm.NewSqlite(&orm.SqliteConfig{
|
||||||
MaxIdleConns: global.Config.Gorm.MaxIdleConns,
|
MaxIdleConns: global.Config.Gorm.MaxIdleConns,
|
||||||
MaxOpenConns: global.Config.Gorm.MaxOpenConns,
|
MaxOpenConns: global.Config.Gorm.MaxOpenConns,
|
||||||
})
|
}, global.Logger)
|
||||||
}
|
}
|
||||||
DatabaseAutoUpdate()
|
|
||||||
|
|
||||||
//validator
|
//validator
|
||||||
ApiInitValidator()
|
global.ApiInitValidator()
|
||||||
|
|
||||||
//oss
|
//oss
|
||||||
global.Oss = &upload.Oss{
|
global.Oss = &upload.Oss{
|
||||||
@@ -93,101 +196,60 @@ func main() {
|
|||||||
|
|
||||||
//jwt
|
//jwt
|
||||||
//fmt.Println(global.Config.Jwt.PrivateKey)
|
//fmt.Println(global.Config.Jwt.PrivateKey)
|
||||||
//global.Jwt = jwt.NewJwt(global.Config.Jwt.PrivateKey, global.Config.Jwt.ExpireDuration*time.Second)
|
global.Jwt = jwt.NewJwt(global.Config.Jwt.Key, global.Config.Jwt.ExpireDuration)
|
||||||
|
|
||||||
//locker
|
//locker
|
||||||
global.Lock = lock.NewLocal()
|
global.Lock = lock.NewLocal()
|
||||||
|
|
||||||
//gin
|
//service
|
||||||
http.ApiInit()
|
service.New(&global.Config, global.DB, global.Logger, global.Jwt, global.Lock)
|
||||||
|
|
||||||
}
|
global.LoginLimiter = utils.NewLoginLimiter(utils.SecurityPolicy{
|
||||||
|
CaptchaThreshold: global.Config.App.CaptchaThreshold,
|
||||||
func ApiInitValidator() {
|
BanThreshold: global.Config.App.BanThreshold,
|
||||||
validate := validator.New()
|
AttemptsWindow: 10 * time.Minute,
|
||||||
enT := en.New()
|
BanDuration: 30 * time.Minute,
|
||||||
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.LoginLimiter.RegisterProvider(utils.B64StringCaptchaProvider{})
|
||||||
global.Validator.VTrans = trans
|
DatabaseAutoUpdate()
|
||||||
|
|
||||||
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() {
|
func DatabaseAutoUpdate() {
|
||||||
version := 103
|
version := DatabaseVersion
|
||||||
|
|
||||||
db := global.DB
|
db := global.DB
|
||||||
|
|
||||||
if global.Config.Gorm.Type == config.TypeMysql {
|
if global.Config.Gorm.Type == config.TypeMysql {
|
||||||
//检查存不存在数据库,不存在则创建
|
//检查存不存在数据库,不存在则创建
|
||||||
dbName := db.Migrator().CurrentDatabase()
|
dbName := db.Migrator().CurrentDatabase()
|
||||||
fmt.Println("dbName", dbName)
|
|
||||||
if dbName == "" {
|
if dbName == "" {
|
||||||
dbName = global.Config.Mysql.Dbname
|
dbName = global.Config.Mysql.Dbname
|
||||||
// 移除 DSN 中的数据库名称,以便初始连接时不指定数据库
|
// 移除 DSN 中的数据库名称,以便初始连接时不指定数据库
|
||||||
dsnWithoutDB := global.Config.Mysql.Username + ":" + global.Config.Mysql.Password + "@(" + global.Config.Mysql.Addr + ")/?charset=utf8mb4&parseTime=True&loc=Local"
|
dsnWithoutDB := fmt.Sprintf("%s:%s@(%s)/%s?charset=utf8mb4&parseTime=True&loc=Local",
|
||||||
|
global.Config.Mysql.Username,
|
||||||
|
global.Config.Mysql.Password,
|
||||||
|
global.Config.Mysql.Addr,
|
||||||
|
"",
|
||||||
|
)
|
||||||
|
|
||||||
//新链接
|
//新链接
|
||||||
dbWithoutDB := orm.NewMysql(&orm.MysqlConfig{
|
dbWithoutDB := orm.NewMysql(&orm.MysqlConfig{
|
||||||
Dns: dsnWithoutDB,
|
Dsn: dsnWithoutDB,
|
||||||
})
|
}, global.Logger)
|
||||||
// 获取底层的 *sql.DB 对象,并确保在程序退出时关闭连接
|
// 获取底层的 *sql.DB 对象,并确保在程序退出时关闭连接
|
||||||
sqlDBWithoutDB, err := dbWithoutDB.DB()
|
sqlDBWithoutDB, err := dbWithoutDB.DB()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("获取底层 *sql.DB 对象失败: %v\n", err)
|
global.Logger.Errorf("获取底层 *sql.DB 对象失败: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if err := sqlDBWithoutDB.Close(); err != nil {
|
if err := sqlDBWithoutDB.Close(); err != nil {
|
||||||
fmt.Printf("关闭连接失败: %v\n", err)
|
global.Logger.Errorf("关闭连接失败: %v", err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
err = dbWithoutDB.Exec("CREATE DATABASE IF NOT EXISTS " + dbName + " DEFAULT CHARSET utf8mb4").Error
|
err = dbWithoutDB.Exec("CREATE DATABASE IF NOT EXISTS " + dbName + " DEFAULT CHARSET utf8mb4").Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
global.Logger.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -202,11 +264,30 @@ func DatabaseAutoUpdate() {
|
|||||||
if v.Version < uint(version) {
|
if v.Version < uint(version) {
|
||||||
Migrate(uint(version))
|
Migrate(uint(version))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 245迁移
|
||||||
|
if v.Version < 245 {
|
||||||
|
//oauths 表的 oauth_type 字段设置为 op同样的值
|
||||||
|
db.Exec("update oauths set oauth_type = op")
|
||||||
|
db.Exec("update oauths set issuer = 'https://accounts.google.com' where op = 'google'")
|
||||||
|
db.Exec("update user_thirds set oauth_type = third_type, op = third_type")
|
||||||
|
//通过email迁移旧的google授权
|
||||||
|
uts := make([]model.UserThird, 0)
|
||||||
|
db.Where("oauth_type = ?", "google").Find(&uts)
|
||||||
|
for _, ut := range uts {
|
||||||
|
if ut.UserId > 0 {
|
||||||
|
db.Model(&model.User{}).Where("id = ?", ut.UserId).Update("email", ut.OpenId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if v.Version < 246 {
|
||||||
|
db.Exec("update oauths set issuer = 'https://accounts.google.com' where op = 'google' and issuer is null")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
func Migrate(version uint) {
|
func Migrate(version uint) {
|
||||||
fmt.Println("migrating....", version)
|
global.Logger.Info("Migrating....", version)
|
||||||
err := global.DB.AutoMigrate(
|
err := global.DB.AutoMigrate(
|
||||||
&model.Version{},
|
&model.Version{},
|
||||||
&model.User{},
|
&model.User{},
|
||||||
@@ -218,22 +299,37 @@ func Migrate(version uint) {
|
|||||||
&model.UserThird{},
|
&model.UserThird{},
|
||||||
&model.Oauth{},
|
&model.Oauth{},
|
||||||
&model.LoginLog{},
|
&model.LoginLog{},
|
||||||
|
&model.ShareRecord{},
|
||||||
|
&model.AuditConn{},
|
||||||
|
&model.AuditFile{},
|
||||||
|
&model.AddressBookCollection{},
|
||||||
|
&model.AddressBookCollectionRule{},
|
||||||
|
&model.ServerCmd{},
|
||||||
|
&model.DeviceGroup{},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("migrate err :=>", err)
|
global.Logger.Error("migrate err :=>", err)
|
||||||
}
|
}
|
||||||
global.DB.Create(&model.Version{Version: version})
|
global.DB.Create(&model.Version{Version: version})
|
||||||
//如果是初次则创建一个默认用户
|
//如果是初次则创建一个默认用户
|
||||||
var vc int64
|
var vc int64
|
||||||
global.DB.Model(&model.Version{}).Count(&vc)
|
global.DB.Model(&model.Version{}).Count(&vc)
|
||||||
if vc == 1 {
|
if vc == 1 {
|
||||||
|
localizer := global.Localizer("")
|
||||||
|
defaultGroup, _ := localizer.LocalizeMessage(&i18n.Message{
|
||||||
|
ID: "DefaultGroup",
|
||||||
|
})
|
||||||
group := &model.Group{
|
group := &model.Group{
|
||||||
Name: "默认组",
|
Name: defaultGroup,
|
||||||
Type: model.GroupTypeDefault,
|
Type: model.GroupTypeDefault,
|
||||||
}
|
}
|
||||||
service.AllService.GroupService.Create(group)
|
service.AllService.GroupService.Create(group)
|
||||||
|
|
||||||
|
shareGroup, _ := localizer.LocalizeMessage(&i18n.Message{
|
||||||
|
ID: "ShareGroup",
|
||||||
|
})
|
||||||
groupShare := &model.Group{
|
groupShare := &model.Group{
|
||||||
Name: "共享组",
|
Name: shareGroup,
|
||||||
Type: model.GroupTypeShare,
|
Type: model.GroupTypeShare,
|
||||||
}
|
}
|
||||||
service.AllService.GroupService.Create(groupShare)
|
service.AllService.GroupService.Create(groupShare)
|
||||||
@@ -241,12 +337,20 @@ func Migrate(version uint) {
|
|||||||
is_admin := true
|
is_admin := true
|
||||||
admin := &model.User{
|
admin := &model.User{
|
||||||
Username: "admin",
|
Username: "admin",
|
||||||
Nickname: "管理员",
|
Nickname: "Admin",
|
||||||
Status: model.COMMON_STATUS_ENABLE,
|
Status: model.COMMON_STATUS_ENABLE,
|
||||||
IsAdmin: &is_admin,
|
IsAdmin: &is_admin,
|
||||||
GroupId: 1,
|
GroupId: 1,
|
||||||
}
|
}
|
||||||
admin.Password = service.AllService.UserService.EncryptPassword("admin")
|
|
||||||
|
// 生成随机密码
|
||||||
|
pwd := utils.RandomString(8)
|
||||||
|
global.Logger.Info("Admin Password Is: ", pwd)
|
||||||
|
var err error
|
||||||
|
admin.Password, err = utils.EncryptPassword(pwd)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Fatalf("failed to generate admin password: %v", err)
|
||||||
|
}
|
||||||
global.DB.Create(admin)
|
global.DB.Create(admin)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
1
conf/admin/hello.html
Normal file
@@ -0,0 +1 @@
|
|||||||
|
### 👏👏👏 你好 ***{{username}}***, 欢迎使用 [RustDesk API](https://github.com/lejianwen/rustdesk-api)
|
||||||
@@ -1,3 +1,22 @@
|
|||||||
|
lang: "zh-CN"
|
||||||
|
app:
|
||||||
|
web-client: 1 # 1:启用 0:禁用
|
||||||
|
register: false #是否开启注册
|
||||||
|
register-status: 1 # 注册用户默认状态 1:启用 2:禁用
|
||||||
|
captcha-threshold: 3 # <0:disabled, 0 always, >0:enabled
|
||||||
|
ban-threshold: 0 # 0:disabled, >0:enabled
|
||||||
|
show-swagger: 0 # 1:启用 0:禁用
|
||||||
|
token-expire: 168h
|
||||||
|
web-sso: true #web auth sso
|
||||||
|
disable-pwd-login: false #禁用密码登录
|
||||||
|
|
||||||
|
admin:
|
||||||
|
title: "RustDesk API Admin"
|
||||||
|
hello-file: "./conf/admin/hello.html" #优先使用file
|
||||||
|
hello: ""
|
||||||
|
# ID Server and Relay Server ports https://github.com/lejianwen/rustdesk-api/issues/257
|
||||||
|
id-server-port: 21116 # ID Server port (for server cmd)
|
||||||
|
relay-server-port: 21117 # ID Server port (for server cmd)
|
||||||
gin:
|
gin:
|
||||||
api-addr: "0.0.0.0:21114"
|
api-addr: "0.0.0.0:21114"
|
||||||
mode: "release" #release,debug,test
|
mode: "release" #release,debug,test
|
||||||
@@ -12,32 +31,54 @@ mysql:
|
|||||||
password: ""
|
password: ""
|
||||||
addr: ""
|
addr: ""
|
||||||
dbname: ""
|
dbname: ""
|
||||||
|
tls: "false" # true / false / skip-verify / custom
|
||||||
|
|
||||||
|
postgresql:
|
||||||
|
host: "127.0.0.1"
|
||||||
|
port: "5432"
|
||||||
|
user: ""
|
||||||
|
password: ""
|
||||||
|
dbname: "postgres"
|
||||||
|
sslmode: "disable" # disable, require, verify-ca, verify-full
|
||||||
|
time-zone: "Asia/Shanghai" # Time zone for PostgreSQL connection
|
||||||
|
|
||||||
rustdesk:
|
rustdesk:
|
||||||
id-server: "192.168.1.66:21116"
|
id-server: "192.168.1.66:21116"
|
||||||
relay-server: "192.168.1.66:21117"
|
relay-server: "192.168.1.66:21117"
|
||||||
api-server: "http://192.168.1.66:21114"
|
api-server: "http://127.0.0.1:21114"
|
||||||
key: "123456789"
|
key: ""
|
||||||
|
key-file: "/data/id_ed25519.pub"
|
||||||
|
personal: 1
|
||||||
|
webclient-magic-queryonline: 0
|
||||||
|
ws-host: "" #eg: wss://192.168.1.3:4443
|
||||||
logger:
|
logger:
|
||||||
path: "./runtime/log.txt"
|
path: "./runtime/log.txt"
|
||||||
level: "warn" #trace,debug,info,warn,error,fatal
|
level: "info" #trace,debug,info,warn,error,fatal
|
||||||
report-caller: true
|
report-caller: true
|
||||||
redis:
|
proxy:
|
||||||
addr: "127.0.0.1:6379"
|
enable: false
|
||||||
password: ""
|
host: "http://127.0.0.1:1080"
|
||||||
db: 0
|
|
||||||
cache:
|
|
||||||
type: "file"
|
|
||||||
file-dir: "./runtime/cache"
|
|
||||||
redis-addr: "127.0.0.1:6379"
|
|
||||||
redis-pwd: ""
|
|
||||||
redis-db: 0
|
|
||||||
oss:
|
|
||||||
access-key-id: ""
|
|
||||||
access-key-secret: ""
|
|
||||||
host: ""
|
|
||||||
callback-url: ""
|
|
||||||
expire-time: 30
|
|
||||||
max-byte: 10240
|
|
||||||
jwt:
|
jwt:
|
||||||
private-key: "./conf/jwt_pri.pem"
|
key: ""
|
||||||
expire-duration: 360000
|
expire-duration: 168h
|
||||||
|
ldap:
|
||||||
|
enable: false
|
||||||
|
url: "ldap://ldap.example.com:389"
|
||||||
|
tls-ca-file: ""
|
||||||
|
tls-verify: false
|
||||||
|
base-dn: "dc=example,dc=com"
|
||||||
|
bind-dn: "cn=admin,dc=example,dc=com"
|
||||||
|
bind-password: "password"
|
||||||
|
|
||||||
|
user:
|
||||||
|
base-dn: "ou=users,dc=example,dc=com"
|
||||||
|
enable-attr: "" #The attribute name of the user for enabling, in AD it is "userAccountControl", empty means no enable attribute, all users are enabled
|
||||||
|
enable-attr-value: "" # The value of the enable attribute when the user is enabled. If you are using AD, just set random value, it will be ignored.
|
||||||
|
filter: "(cn=*)"
|
||||||
|
username: "uid" # The attribute name of the user for usernamem if you are using AD, it should be "sAMAccountName"
|
||||||
|
email: "mail"
|
||||||
|
first-name: "givenName"
|
||||||
|
last-name: "sn"
|
||||||
|
sync: false # If true, the user will be synchronized to the database when the user logs in. If false, the user will be synchronized to the database when the user be created.
|
||||||
|
admin-group: "cn=admin,dc=example,dc=com" # The group name of the admin group, if the user is in this group, the user will be an admin.
|
||||||
|
allow-group: "cn=users,dc=example,dc=com" # The group name of the users group, if the user is in this group, the user will be an login.
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/fsnotify/fsnotify"
|
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -14,47 +13,86 @@ const (
|
|||||||
DefaultConfig = "conf/config.yaml"
|
DefaultConfig = "conf/config.yaml"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type App struct {
|
||||||
|
WebClient int `mapstructure:"web-client"`
|
||||||
|
Register bool `mapstructure:"register"`
|
||||||
|
RegisterStatus int `mapstructure:"register-status"`
|
||||||
|
ShowSwagger int `mapstructure:"show-swagger"`
|
||||||
|
TokenExpire time.Duration `mapstructure:"token-expire"`
|
||||||
|
WebSso bool `mapstructure:"web-sso"`
|
||||||
|
DisablePwdLogin bool `mapstructure:"disable-pwd-login"`
|
||||||
|
CaptchaThreshold int `mapstructure:"captcha-threshold"`
|
||||||
|
BanThreshold int `mapstructure:"ban-threshold"`
|
||||||
|
}
|
||||||
|
type Admin struct {
|
||||||
|
Title string `mapstructure:"title"`
|
||||||
|
Hello string `mapstructure:"hello"`
|
||||||
|
HelloFile string `mapstructure:"hello-file"`
|
||||||
|
IdServerPort int `mapstructure:"id-server-port"`
|
||||||
|
RelayServerPort int `mapstructure:"relay-server-port"`
|
||||||
|
}
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Gorm Gorm
|
Lang string `mapstructure:"lang"`
|
||||||
Mysql Mysql
|
App App
|
||||||
Gin Gin
|
Admin Admin
|
||||||
Logger Logger
|
Gorm Gorm
|
||||||
Redis Redis
|
Mysql Mysql
|
||||||
Cache Cache
|
Postgresql Postgresql
|
||||||
Oss Oss
|
Gin Gin
|
||||||
Jwt Jwt
|
Logger Logger
|
||||||
Rustdesk Rustdesk
|
Redis Redis
|
||||||
|
Cache Cache
|
||||||
|
Oss Oss
|
||||||
|
Jwt Jwt
|
||||||
|
Rustdesk Rustdesk
|
||||||
|
Proxy Proxy
|
||||||
|
Ldap Ldap
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Admin) Init() {
|
||||||
|
if a.IdServerPort == 0 {
|
||||||
|
a.IdServerPort = DefaultIdServerPort
|
||||||
|
}
|
||||||
|
if a.RelayServerPort == 0 {
|
||||||
|
a.RelayServerPort = DefaultRelayServerPort
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init 初始化配置
|
// Init 初始化配置
|
||||||
func Init(rowVal interface{}) *viper.Viper {
|
func Init(rowVal *Config, path string) *viper.Viper {
|
||||||
var config string
|
if path == "" {
|
||||||
flag.StringVar(&config, "c", "", "choose config file.")
|
path = DefaultConfig
|
||||||
flag.Parse()
|
|
||||||
if config == "" { // 优先级: 命令行 > 默认值
|
|
||||||
config = DefaultConfig
|
|
||||||
}
|
}
|
||||||
v := viper.New()
|
v := viper.GetViper()
|
||||||
v.AutomaticEnv()
|
v.AutomaticEnv()
|
||||||
v.SetEnvKeyReplacer(strings.NewReplacer(".", "_", "-", "_"))
|
v.SetEnvKeyReplacer(strings.NewReplacer(".", "_", "-", "_"))
|
||||||
v.SetEnvPrefix("RUSTDESK_API")
|
v.SetEnvPrefix("RUSTDESK_API")
|
||||||
v.SetConfigFile(config)
|
v.SetConfigFile(path)
|
||||||
v.SetConfigType("yaml")
|
v.SetConfigType("yaml")
|
||||||
err := v.ReadInConfig()
|
err := v.ReadInConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Errorf("Fatal error config file: %s \n", err))
|
panic(fmt.Errorf("Fatal error config file: %s \n", err))
|
||||||
}
|
}
|
||||||
v.WatchConfig()
|
/*
|
||||||
v.OnConfigChange(func(e fsnotify.Event) {
|
v.WatchConfig()
|
||||||
//配置文件修改监听
|
|
||||||
fmt.Println("config file changed:", e.Name)
|
|
||||||
if err2 := v.Unmarshal(rowVal); err2 != nil {
|
//监听配置修改没什么必要
|
||||||
fmt.Println(err2)
|
v.OnConfigChange(func(e fsnotify.Event) {
|
||||||
}
|
//配置文件修改监听
|
||||||
})
|
fmt.Println("config file changed:", e.Name)
|
||||||
|
if err2 := v.Unmarshal(rowVal); err2 != nil {
|
||||||
|
fmt.Println(err2)
|
||||||
|
}
|
||||||
|
rowVal.Rustdesk.LoadKeyFile()
|
||||||
|
rowVal.Rustdesk.ParsePort()
|
||||||
|
})
|
||||||
|
*/
|
||||||
if err := v.Unmarshal(rowVal); err != nil {
|
if err := v.Unmarshal(rowVal); err != nil {
|
||||||
fmt.Println(err)
|
panic(fmt.Errorf("Fatal error config: %s \n", err))
|
||||||
}
|
}
|
||||||
|
rowVal.Rustdesk.LoadKeyFile()
|
||||||
|
rowVal.Admin.Init()
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
const (
|
const (
|
||||||
TypeSqlite = "sqlite"
|
TypeSqlite = "sqlite"
|
||||||
TypeMysql = "mysql"
|
TypeMysql = "mysql"
|
||||||
|
TypePostgresql = "postgresql"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Gorm struct {
|
type Gorm struct {
|
||||||
@@ -16,4 +17,15 @@ type Mysql struct {
|
|||||||
Username string `mapstructure:"username"`
|
Username string `mapstructure:"username"`
|
||||||
Password string `mapstructure:"password"`
|
Password string `mapstructure:"password"`
|
||||||
Dbname string `mapstructure:"dbname"`
|
Dbname string `mapstructure:"dbname"`
|
||||||
|
Tls string `mapstructure:"tls"` // true / false / skip-verify / custom
|
||||||
|
}
|
||||||
|
|
||||||
|
type Postgresql struct {
|
||||||
|
Host string `mapstructure:"host"`
|
||||||
|
Port string `mapstructure:"port"`
|
||||||
|
User string `mapstructure:"user"`
|
||||||
|
Password string `mapstructure:"password"`
|
||||||
|
Dbname string `mapstructure:"dbname"`
|
||||||
|
Sslmode string `mapstructure:"sslmode"` // "disable", "require", "verify-ca", "verify-full"
|
||||||
|
TimeZone string `mapstructure:"time-zone"` // e.g., "Asia/Shanghai"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,6 @@ package config
|
|||||||
import "time"
|
import "time"
|
||||||
|
|
||||||
type Jwt struct {
|
type Jwt struct {
|
||||||
PrivateKey string `mapstructure:"private-key"`
|
Key string `mapstructure:"key"`
|
||||||
ExpireDuration time.Duration `mapstructure:"expire-duration"`
|
ExpireDuration time.Duration `mapstructure:"expire-duration"`
|
||||||
}
|
}
|
||||||
|
|||||||
37
config/ldap.go
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
type LdapUser struct {
|
||||||
|
BaseDn string `mapstructure:"base-dn"` // The base DN of the user for searching
|
||||||
|
EnableAttr string `mapstructure:"enable-attr"` // The attribute name of the user for enabling, in AD it is "userAccountControl", empty means no enable attribute, all users are enabled
|
||||||
|
EnableAttrValue string `mapstructure:"enable-attr-value"` // The value of the enable attribute when the user is enabled. If you are using AD, just leave it random str, it will be ignored.
|
||||||
|
Filter string `mapstructure:"filter"`
|
||||||
|
Username string `mapstructure:"username"`
|
||||||
|
Email string `mapstructure:"email"`
|
||||||
|
FirstName string `mapstructure:"first-name"`
|
||||||
|
LastName string `mapstructure:"last-name"`
|
||||||
|
Sync bool `mapstructure:"sync"` // Will sync the user's information to the internal database
|
||||||
|
AdminGroup string `mapstructure:"admin-group"` // Which group is the admin group
|
||||||
|
AllowGroup string `mapstructure:"allow-group"` // Which group is allowed to login
|
||||||
|
}
|
||||||
|
|
||||||
|
// type LdapGroup struct {
|
||||||
|
// BaseDn string `mapstructure:"base-dn"` // The base DN of the group for searching
|
||||||
|
// Name string `mapstructure:"name"` // The attribute name of the group
|
||||||
|
// Filter string `mapstructure:"filter"`
|
||||||
|
// Admin string `mapstructure:"admin"` // Which group is the admin group
|
||||||
|
// Member string `mapstructure:"member"` // How to get the member of the group: member, uniqueMember, or memberOf (default: member)
|
||||||
|
// Mode string `mapstructure:"mode"`
|
||||||
|
// Map map[string]string `mapstructure:"map"` // If mode is "map", map the LDAP group to the internal group
|
||||||
|
// }
|
||||||
|
|
||||||
|
type Ldap struct {
|
||||||
|
Enable bool `mapstructure:"enable"`
|
||||||
|
Url string `mapstructure:"url"`
|
||||||
|
TlsCaFile string `mapstructure:"tls-ca-file"`
|
||||||
|
TlsVerify bool `mapstructure:"tls-verify"`
|
||||||
|
BaseDn string `mapstructure:"base-dn"`
|
||||||
|
BindDn string `mapstructure:"bind-dn"`
|
||||||
|
BindPassword string `mapstructure:"bind-password"`
|
||||||
|
User LdapUser `mapstructure:"user"`
|
||||||
|
// Group LdapGroup `mapstructure:"group"`
|
||||||
|
}
|
||||||
@@ -3,11 +3,20 @@ package config
|
|||||||
type GithubOauth struct {
|
type GithubOauth struct {
|
||||||
ClientId string `mapstructure:"client-id"`
|
ClientId string `mapstructure:"client-id"`
|
||||||
ClientSecret string `mapstructure:"client-secret"`
|
ClientSecret string `mapstructure:"client-secret"`
|
||||||
RedirectUrl string `mapstructure:"redirect-url"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type GoogleOauth struct {
|
type GoogleOauth struct {
|
||||||
ClientId string `mapstructure:"client-id"`
|
ClientId string `mapstructure:"client-id"`
|
||||||
ClientSecret string `mapstructure:"client-secret"`
|
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"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type LinuxdoOauth struct {
|
||||||
|
ClientId string `mapstructure:"client-id"`
|
||||||
|
ClientSecret string `mapstructure:"client-secret"`
|
||||||
}
|
}
|
||||||
|
|||||||
6
config/proxy.go
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
type Proxy struct {
|
||||||
|
Enable bool `mapstructure:"enable"`
|
||||||
|
Host string `mapstructure:"host"`
|
||||||
|
}
|
||||||
@@ -1,8 +1,40 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
DefaultIdServerPort = 21116
|
||||||
|
DefaultRelayServerPort = 21117
|
||||||
|
)
|
||||||
|
|
||||||
type Rustdesk struct {
|
type Rustdesk struct {
|
||||||
IdServer string `mapstructure:"id-server"`
|
IdServer string `mapstructure:"id-server"`
|
||||||
RelayServer string `mapstructure:"relay-server"`
|
IdServerPort int `mapstructure:"-"`
|
||||||
ApiServer string `mapstructure:"api-server"`
|
RelayServer string `mapstructure:"relay-server"`
|
||||||
Key string `mapstructure:"key"`
|
RelayServerPort int `mapstructure:"-"`
|
||||||
|
ApiServer string `mapstructure:"api-server"`
|
||||||
|
Key string `mapstructure:"key"`
|
||||||
|
KeyFile string `mapstructure:"key-file"`
|
||||||
|
Personal int `mapstructure:"personal"`
|
||||||
|
//webclient-magic-queryonline
|
||||||
|
WebclientMagicQueryonline int `mapstructure:"webclient-magic-queryonline"`
|
||||||
|
WsHost string `mapstructure:"ws-host"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rd *Rustdesk) LoadKeyFile() {
|
||||||
|
// Load key file
|
||||||
|
if rd.Key != "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if rd.KeyFile != "" {
|
||||||
|
// Load key from file
|
||||||
|
b, err := os.ReadFile(rd.KeyFile)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rd.Key = string(b)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
5
debian/changelog
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
rustdesk-api-server (1.3.6) UNRELEASED; urgency=medium
|
||||||
|
|
||||||
|
* Update the version to 1.3.6 to match the client.
|
||||||
|
|
||||||
|
-- rustdesk-api <ymwlpoolc@qq.com> Tue, 24 Dec 2024 13:48:34 +0800
|
||||||
1
debian/compat
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
10
|
||||||
13
debian/control.tpl
vendored
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
Source: rustdesk-api-server
|
||||||
|
Section: net
|
||||||
|
Priority: optional
|
||||||
|
Maintainer: ymwl <ymwlpoolc@qq.com>
|
||||||
|
Build-Depends: debhelper (>= 10), pkg-config
|
||||||
|
Standards-Version: 4.5.0
|
||||||
|
Homepage: https://github.com/lejianwen/rustdesk-api/
|
||||||
|
|
||||||
|
Package: rustdesk-api-server
|
||||||
|
Architecture: {{ ARCH }}
|
||||||
|
Depends: systemd ${misc:Depends}
|
||||||
|
Description: RustDesk api server
|
||||||
|
RustDesk api server, it is free and open source.
|
||||||
21
debian/copyright
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2024-present Lejianwen and contributors
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
6
debian/rules
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#!/usr/bin/make -f
|
||||||
|
%:
|
||||||
|
dh $@
|
||||||
|
|
||||||
|
override_dh_builddeb:
|
||||||
|
dh_builddeb -- -Zgzip
|
||||||
6
debian/rustdesk-api-server.install
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
bin/rustdesk-api usr/bin
|
||||||
|
systemd/rustdesk-api.service lib/systemd/system
|
||||||
|
conf var/lib/rustdesk-api
|
||||||
|
data var/lib/rustdesk-api
|
||||||
|
resources var/lib/rustdesk-api
|
||||||
|
runtime var/lib/rustdesk-api
|
||||||
28
debian/rustdesk-api-server.postinst
vendored
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
|
|
||||||
|
SERVICE=rustdesk-api.service
|
||||||
|
|
||||||
|
if [ "$1" = "configure" ]; then
|
||||||
|
mkdir -p /var/log/rustdesk-api
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$1" in
|
||||||
|
configure|abort-upgrade|abort-deconfigure|abort-remove)
|
||||||
|
mkdir -p /var/lib/rustdesk-api/
|
||||||
|
deb-systemd-helper unmask "${SERVICE}" >/dev/null || true
|
||||||
|
if deb-systemd-helper --quiet was-enabled "${SERVICE}"; then
|
||||||
|
deb-systemd-invoke enable "${SERVICE}" >/dev/null || true
|
||||||
|
else
|
||||||
|
deb-systemd-invoke update-state "${SERVICE}" >/dev/null || true
|
||||||
|
fi
|
||||||
|
systemctl --system daemon-reload >/dev/null || true
|
||||||
|
if [ -n "$2" ]; then
|
||||||
|
deb-systemd-invoke restart "${SERVICE}" >/dev/null || true
|
||||||
|
else
|
||||||
|
deb-systemd-invoke start "${SERVICE}" >/dev/null || true
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
exit 0
|
||||||
18
debian/rustdesk-api-server.postrm
vendored
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
|
|
||||||
|
SERVICE=rustdesk-api.service
|
||||||
|
|
||||||
|
systemctl --system daemon-reload >/dev/null || true
|
||||||
|
|
||||||
|
if [ "$1" = "purge" ]; then
|
||||||
|
rm -rf /var/log/rustdesk-api/rustdesk-api.*
|
||||||
|
deb-systemd-helper purge "${SERVICE}" >/dev/null || true
|
||||||
|
deb-systemd-helper unmask "${SERVICE}" >/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$1" = "remove" ]; then
|
||||||
|
deb-systemd-helper mask "${SERVICE}" >/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit 0
|
||||||
13
debian/rustdesk-api-server.prerm
vendored
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
|
|
||||||
|
SERVICE=rustdesk-api.service
|
||||||
|
|
||||||
|
case "$1" in
|
||||||
|
remove|deconfigure)
|
||||||
|
deb-systemd-invoke stop "${SERVICE}" >/dev/null || true
|
||||||
|
deb-systemd-invoke disable "${SERVICE}" >/dev/null || true
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
exit 0
|
||||||
1
debian/source/format
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
3.0 (native)
|
||||||
24
docker-compose-dev.yaml
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
services:
|
||||||
|
rustdesk-api:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile.dev
|
||||||
|
args:
|
||||||
|
COUNTRY: CN
|
||||||
|
FRONTEND_GIT_REPO: https://github.com/lejianwen/rustdesk-api-web.git
|
||||||
|
FRONTEND_GIT_BRANCH: master
|
||||||
|
# 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
|
||||||
@@ -6,10 +6,12 @@ services:
|
|||||||
- TZ=Asia/Shanghai
|
- TZ=Asia/Shanghai
|
||||||
- RUSTDESK_API_RUSTDESK_ID_SERVER=192.168.1.66:21116
|
- RUSTDESK_API_RUSTDESK_ID_SERVER=192.168.1.66:21116
|
||||||
- RUSTDESK_API_RUSTDESK_RELAY_SERVER=192.168.1.66:21117
|
- RUSTDESK_API_RUSTDESK_RELAY_SERVER=192.168.1.66:21117
|
||||||
- RUSTDESK_API_RUSTDESK_API_SERVER=http://192.168.1.66:21114
|
- RUSTDESK_API_RUSTDESK_API_SERVER=http://127.0.0.1:21114
|
||||||
- RUSTDESK_API_RUSTDESK_KEY=123456789
|
- RUSTDESK_API_RUSTDESK_KEY=123456789
|
||||||
ports:
|
ports:
|
||||||
- 21114:21114
|
- 21114:21114
|
||||||
volumes:
|
volumes:
|
||||||
- /data/rustdesk/api:/app/data #将数据库挂载出来方便备份
|
- ./data/rustdesk/api:/app/data # database
|
||||||
|
# - ./conf:/app/conf # config
|
||||||
|
# - ./resources:/app/resources # 静态资源
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
35
docker-dev.sh
Executable file
@@ -0,0 +1,35 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Define Docker Compose file and cache option
|
||||||
|
COMPOSE_FILE_NAME="docker-compose-dev.yaml"
|
||||||
|
CACHE=""
|
||||||
|
# Uncomment the next line to enable no-cache option
|
||||||
|
# CACHE="--no-cache"
|
||||||
|
|
||||||
|
# Define the base Docker Compose command
|
||||||
|
DCS="docker compose -f ${COMPOSE_FILE_NAME}"
|
||||||
|
|
||||||
|
# Function to build and start services
|
||||||
|
build_and_run() {
|
||||||
|
echo "Building services..."
|
||||||
|
if ! $DCS build ${CACHE}; then
|
||||||
|
echo "Error: Failed to build services"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Starting services..."
|
||||||
|
if ! $DCS up -d; then
|
||||||
|
echo "Error: Failed to start services"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "Services started successfully"
|
||||||
|
echo "If you want to stop the services, run"
|
||||||
|
echo "docker compose -f ${COMPOSE_FILE_NAME} down"
|
||||||
|
|
||||||
|
echo "If you want to see the logs, run"
|
||||||
|
echo "docker compose -f ${COMPOSE_FILE_NAME} logs -f"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Execute build and start function
|
||||||
|
build_and_run
|
||||||
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 97 KiB |
@@ -6,6 +6,44 @@ 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}"}'
|
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: string
|
||||||
type: object
|
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:
|
api.DeviceInfoInLogin:
|
||||||
properties:
|
properties:
|
||||||
name:
|
name:
|
||||||
@@ -24,14 +62,14 @@ definitions:
|
|||||||
id:
|
id:
|
||||||
type: string
|
type: string
|
||||||
password:
|
password:
|
||||||
maxLength: 20
|
maxLength: 32
|
||||||
minLength: 4
|
minLength: 4
|
||||||
type: string
|
type: string
|
||||||
type:
|
type:
|
||||||
type: string
|
type: string
|
||||||
username:
|
username:
|
||||||
maxLength: 10
|
maxLength: 32
|
||||||
minLength: 4
|
minLength: 2
|
||||||
type: string
|
type: string
|
||||||
uuid:
|
uuid:
|
||||||
type: string
|
type: string
|
||||||
@@ -86,8 +124,27 @@ definitions:
|
|||||||
status:
|
status:
|
||||||
type: integer
|
type: integer
|
||||||
type: object
|
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:
|
model.Tag:
|
||||||
properties:
|
properties:
|
||||||
|
collection:
|
||||||
|
$ref: '#/definitions/model.AddressBookCollection'
|
||||||
|
collection_id:
|
||||||
|
type: integer
|
||||||
color:
|
color:
|
||||||
description: color 是flutter的颜色值,从0x00000000 到 0xFFFFFFFF; 前两位表示透明度,后面6位表示颜色,
|
description: color 是flutter的颜色值,从0x00000000 到 0xFFFFFFFF; 前两位表示透明度,后面6位表示颜色,
|
||||||
可以转成rgba
|
可以转成rgba
|
||||||
@@ -103,6 +160,19 @@ definitions:
|
|||||||
user_id:
|
user_id:
|
||||||
type: integer
|
type: integer
|
||||||
type: object
|
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:
|
response.DataResponse:
|
||||||
properties:
|
properties:
|
||||||
data: {}
|
data: {}
|
||||||
@@ -195,11 +265,17 @@ paths:
|
|||||||
summary: 地址更新
|
summary: 地址更新
|
||||||
tags:
|
tags:
|
||||||
- 地址
|
- 地址
|
||||||
/ab/add:
|
/ab/peer/add/{guid}:
|
||||||
post:
|
delete:
|
||||||
consumes:
|
consumes:
|
||||||
- application/json
|
- application/json
|
||||||
description: 标签
|
description: 删除地址
|
||||||
|
parameters:
|
||||||
|
- description: guid
|
||||||
|
in: path
|
||||||
|
name: guid
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
produces:
|
produces:
|
||||||
- application/json
|
- application/json
|
||||||
responses:
|
responses:
|
||||||
@@ -213,14 +289,101 @@ paths:
|
|||||||
$ref: '#/definitions/response.ErrorResponse'
|
$ref: '#/definitions/response.ErrorResponse'
|
||||||
security:
|
security:
|
||||||
- BearerAuth: []
|
- BearerAuth: []
|
||||||
summary: 标签添加
|
summary: 删除地址
|
||||||
tags:
|
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:
|
/ab/personal:
|
||||||
post:
|
post:
|
||||||
consumes:
|
consumes:
|
||||||
- application/json
|
- application/json
|
||||||
description: 个人信息
|
description: 个人地址
|
||||||
parameters:
|
parameters:
|
||||||
- description: string valid
|
- description: string valid
|
||||||
in: body
|
in: body
|
||||||
@@ -240,10 +403,254 @@ paths:
|
|||||||
$ref: '#/definitions/response.Response'
|
$ref: '#/definitions/response.Response'
|
||||||
security:
|
security:
|
||||||
- BearerAuth: []
|
- BearerAuth: []
|
||||||
summary: 个人信息
|
summary: 个人地址
|
||||||
tags:
|
tags:
|
||||||
- 用户
|
- 地址[Personal]
|
||||||
/api:
|
/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]
|
||||||
|
/audit/conn:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 审计连接
|
||||||
|
parameters:
|
||||||
|
- description: 审计连接
|
||||||
|
in: body
|
||||||
|
name: body
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/api.AuditConnForm'
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Response'
|
||||||
|
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:
|
||||||
|
- 审计
|
||||||
|
/currentUser:
|
||||||
get:
|
get:
|
||||||
consumes:
|
consumes:
|
||||||
- application/json
|
- application/json
|
||||||
@@ -264,6 +671,44 @@ paths:
|
|||||||
summary: 用户信息
|
summary: 用户信息
|
||||||
tags:
|
tags:
|
||||||
- 用户
|
- 用户
|
||||||
|
/device-group/accessible:
|
||||||
|
get:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 机器
|
||||||
|
parameters:
|
||||||
|
- description: 页码
|
||||||
|
in: query
|
||||||
|
name: page
|
||||||
|
type: integer
|
||||||
|
- description: 每页数量
|
||||||
|
in: query
|
||||||
|
name: pageSize
|
||||||
|
type: integer
|
||||||
|
- description: 状态
|
||||||
|
in: query
|
||||||
|
name: status
|
||||||
|
type: integer
|
||||||
|
- description: accessible
|
||||||
|
in: query
|
||||||
|
name: accessible
|
||||||
|
type: string
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.DataResponse'
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Response'
|
||||||
|
security:
|
||||||
|
- BearerAuth: []
|
||||||
|
summary: 设备
|
||||||
|
tags:
|
||||||
|
- 群组
|
||||||
/heartbeat:
|
/heartbeat:
|
||||||
post:
|
post:
|
||||||
consumes:
|
consumes:
|
||||||
@@ -308,7 +753,7 @@ paths:
|
|||||||
tags:
|
tags:
|
||||||
- 登录
|
- 登录
|
||||||
/login-options:
|
/login-options:
|
||||||
post:
|
get:
|
||||||
consumes:
|
consumes:
|
||||||
- application/json
|
- application/json
|
||||||
description: 登录选项
|
description: 登录选项
|
||||||
@@ -347,44 +792,6 @@ paths:
|
|||||||
summary: 登出
|
summary: 登出
|
||||||
tags:
|
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
|
|
||||||
/oauth/login:
|
|
||||||
get:
|
|
||||||
consumes:
|
|
||||||
- application/json
|
|
||||||
description: WebOauthLogin
|
|
||||||
produces:
|
|
||||||
- application/json
|
|
||||||
responses:
|
|
||||||
"200":
|
|
||||||
description: OK
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
"500":
|
|
||||||
description: Internal Server Error
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
summary: WebOauthLogin
|
|
||||||
tags:
|
|
||||||
- Oauth
|
|
||||||
/oidc/auth:
|
/oidc/auth:
|
||||||
post:
|
post:
|
||||||
consumes:
|
consumes:
|
||||||
@@ -423,6 +830,25 @@ paths:
|
|||||||
summary: OidcAuthQuery
|
summary: OidcAuthQuery
|
||||||
tags:
|
tags:
|
||||||
- Oauth
|
- Oauth
|
||||||
|
/oidc/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
|
||||||
/peers:
|
/peers:
|
||||||
get:
|
get:
|
||||||
consumes:
|
consumes:
|
||||||
@@ -482,6 +908,46 @@ paths:
|
|||||||
summary: 服务配置
|
summary: 服务配置
|
||||||
tags:
|
tags:
|
||||||
- WEBCLIENT
|
- WEBCLIENT
|
||||||
|
/server-config-v2:
|
||||||
|
get:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 服务配置,给webclient提供api-server
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Response'
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Response'
|
||||||
|
security:
|
||||||
|
- token: []
|
||||||
|
summary: 服务配置
|
||||||
|
tags:
|
||||||
|
- WEBCLIENT_V2
|
||||||
|
/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:
|
/sysinfo:
|
||||||
post:
|
post:
|
||||||
consumes:
|
consumes:
|
||||||
@@ -505,34 +971,28 @@ paths:
|
|||||||
description: Internal Server Error
|
description: Internal Server Error
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/response.ErrorResponse'
|
$ref: '#/definitions/response.ErrorResponse'
|
||||||
security:
|
|
||||||
- BearerAuth: []
|
|
||||||
summary: 提交系统信息
|
summary: 提交系统信息
|
||||||
tags:
|
tags:
|
||||||
- 地址
|
- System
|
||||||
/tags:
|
/sysinfo_ver:
|
||||||
post:
|
post:
|
||||||
consumes:
|
consumes:
|
||||||
- application/json
|
- application/json
|
||||||
description: 标签
|
description: 获取系统版本信息
|
||||||
produces:
|
produces:
|
||||||
- application/json
|
- application/json
|
||||||
responses:
|
responses:
|
||||||
"200":
|
"200":
|
||||||
description: OK
|
description: OK
|
||||||
schema:
|
schema:
|
||||||
items:
|
type: string
|
||||||
$ref: '#/definitions/model.Tag'
|
|
||||||
type: array
|
|
||||||
"500":
|
"500":
|
||||||
description: Internal Server Error
|
description: Internal Server Error
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/response.ErrorResponse'
|
$ref: '#/definitions/response.ErrorResponse'
|
||||||
security:
|
summary: 获取系统版本信息
|
||||||
- BearerAuth: []
|
|
||||||
summary: 标签
|
|
||||||
tags:
|
tags:
|
||||||
- 地址
|
- System
|
||||||
/users:
|
/users:
|
||||||
get:
|
get:
|
||||||
consumes:
|
consumes:
|
||||||
@@ -578,6 +1038,25 @@ paths:
|
|||||||
summary: 用户列表
|
summary: 用户列表
|
||||||
tags:
|
tags:
|
||||||
- 群组
|
- 群组
|
||||||
|
/version:
|
||||||
|
get:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 版本
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Response'
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Response'
|
||||||
|
summary: 版本
|
||||||
|
tags:
|
||||||
|
- 首页
|
||||||
securityDefinitions:
|
securityDefinitions:
|
||||||
BearerAuth:
|
BearerAuth:
|
||||||
in: header
|
in: header
|
||||||
|
|||||||
BIN
docs/en_img/admin_webclient.png
Normal file
|
After Width: | Height: | Size: 97 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/rustdesk_command_advance.png
Normal file
|
After Width: | Height: | Size: 99 KiB |
BIN
docs/en_img/rustdesk_command_simple.png
Normal file
|
After Width: | Height: | Size: 67 KiB |
BIN
docs/en_img/web_admin.png
Normal file
|
After Width: | Height: | Size: 49 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: 19 KiB |
BIN
docs/en_img/web_admin_user.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
docs/en_img/web_resetpwd.png
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
BIN
docs/init_admin_pwd.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
docs/rustdesk_command_advance.png
Normal file
|
After Width: | Height: | Size: 99 KiB |
BIN
docs/rustdesk_command_simple.png
Normal file
|
After Width: | Height: | Size: 67 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 7.7 KiB After Width: | Height: | Size: 8.5 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 13 KiB |
BIN
docs/webclientv2.png
Normal file
|
After Width: | Height: | Size: 31 KiB |
@@ -1,4 +1,4 @@
|
|||||||
package Gwen
|
package main
|
||||||
|
|
||||||
//go:generate swag init -g cmd/apimain.go --output docs/api --instanceName api --exclude http/controller/admin
|
//go:generate swag init -g cmd/apimain.go --output docs/api --instanceName api --exclude http/controller/admin
|
||||||
//go:generate swag init -g cmd/apimain.go --output docs/admin --instanceName admin --exclude http/controller/api
|
//go:generate swag init -g cmd/apimain.go --output docs/admin --instanceName admin --exclude http/controller/api
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
package Gwen
|
package main
|
||||||
|
|
||||||
//go:generate go run cmd/apimain.go
|
//go:generate go run cmd/apimain.go
|
||||||
|
|||||||
160
global/apiValidator.go
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
package global
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/go-playground/locales/en"
|
||||||
|
"github.com/go-playground/locales/es"
|
||||||
|
"github.com/go-playground/locales/fr"
|
||||||
|
"github.com/go-playground/locales/ko"
|
||||||
|
"github.com/go-playground/locales/ru"
|
||||||
|
"github.com/go-playground/locales/zh_Hans_CN"
|
||||||
|
"github.com/go-playground/locales/zh_Hant"
|
||||||
|
ut "github.com/go-playground/universal-translator"
|
||||||
|
"github.com/go-playground/validator/v10"
|
||||||
|
en_translations "github.com/go-playground/validator/v10/translations/en"
|
||||||
|
es_translations "github.com/go-playground/validator/v10/translations/es"
|
||||||
|
fr_translations "github.com/go-playground/validator/v10/translations/fr"
|
||||||
|
ko_translations "github.com/go-playground/validator/v10/translations/ko"
|
||||||
|
ru_translations "github.com/go-playground/validator/v10/translations/ru"
|
||||||
|
zh_translations "github.com/go-playground/validator/v10/translations/zh"
|
||||||
|
zh_tw_translations "github.com/go-playground/validator/v10/translations/zh_tw"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ApiInitValidator() {
|
||||||
|
validate := validator.New()
|
||||||
|
|
||||||
|
// 定义不同的语言翻译
|
||||||
|
enT := en.New()
|
||||||
|
cn := zh_Hans_CN.New()
|
||||||
|
koT := ko.New()
|
||||||
|
ruT := ru.New()
|
||||||
|
esT := es.New()
|
||||||
|
frT := fr.New()
|
||||||
|
zhTwT := zh_Hant.New()
|
||||||
|
|
||||||
|
uni := ut.New(enT, cn, koT, ruT, esT, frT, zhTwT)
|
||||||
|
|
||||||
|
enTrans, _ := uni.GetTranslator("en")
|
||||||
|
zhTrans, _ := uni.GetTranslator("zh_Hans_CN")
|
||||||
|
koTrans, _ := uni.GetTranslator("ko")
|
||||||
|
ruTrans, _ := uni.GetTranslator("ru")
|
||||||
|
esTrans, _ := uni.GetTranslator("es")
|
||||||
|
frTrans, _ := uni.GetTranslator("fr")
|
||||||
|
zhTwTrans, _ := uni.GetTranslator("zh_Hant")
|
||||||
|
|
||||||
|
err := zh_translations.RegisterDefaultTranslations(validate, zhTrans)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
err = en_translations.RegisterDefaultTranslations(validate, enTrans)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ko_translations.RegisterDefaultTranslations(validate, koTrans)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
err = ru_translations.RegisterDefaultTranslations(validate, ruTrans)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
err = es_translations.RegisterDefaultTranslations(validate, esTrans)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
err = fr_translations.RegisterDefaultTranslations(validate, frTrans)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
err = zh_tw_translations.RegisterDefaultTranslations(validate, zhTwTrans)
|
||||||
|
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 "zh_TW":
|
||||||
|
fallthrough
|
||||||
|
case "zh-TW":
|
||||||
|
fallthrough
|
||||||
|
case "zh-tw":
|
||||||
|
trans, _ := Validator.UT.GetTranslator("zh_Hant")
|
||||||
|
return trans
|
||||||
|
case "ko":
|
||||||
|
trans, _ := Validator.UT.GetTranslator("ko")
|
||||||
|
return trans
|
||||||
|
case "ru":
|
||||||
|
trans, _ := Validator.UT.GetTranslator("ru")
|
||||||
|
return trans
|
||||||
|
case "es":
|
||||||
|
trans, _ := Validator.UT.GetTranslator("es")
|
||||||
|
return trans
|
||||||
|
case "fr":
|
||||||
|
trans, _ := Validator.UT.GetTranslator("fr")
|
||||||
|
return trans
|
||||||
|
case "en":
|
||||||
|
fallthrough
|
||||||
|
default:
|
||||||
|
trans, _ := Validator.UT.GetTranslator("en")
|
||||||
|
return trans
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,33 +1,40 @@
|
|||||||
package global
|
package global
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"Gwen/config"
|
"github.com/gin-gonic/gin"
|
||||||
"Gwen/lib/cache"
|
|
||||||
"Gwen/lib/jwt"
|
|
||||||
"Gwen/lib/lock"
|
|
||||||
"Gwen/lib/upload"
|
|
||||||
ut "github.com/go-playground/universal-translator"
|
ut "github.com/go-playground/universal-translator"
|
||||||
"github.com/go-playground/validator/v10"
|
"github.com/go-playground/validator/v10"
|
||||||
"github.com/go-redis/redis/v8"
|
"github.com/go-redis/redis/v8"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/config"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/lib/cache"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/lib/jwt"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/lib/lock"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/lib/upload"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/utils"
|
||||||
|
"github.com/nicksnyder/go-i18n/v2/i18n"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
DB *gorm.DB
|
DB *gorm.DB
|
||||||
Logger *logrus.Logger
|
Logger *logrus.Logger
|
||||||
Config config.Config
|
ConfigPath string = ""
|
||||||
Viper *viper.Viper
|
Config config.Config
|
||||||
Redis *redis.Client
|
Viper *viper.Viper
|
||||||
Cache cache.Handler
|
Redis *redis.Client
|
||||||
Validator struct {
|
Cache cache.Handler
|
||||||
|
Validator struct {
|
||||||
Validate *validator.Validate
|
Validate *validator.Validate
|
||||||
|
UT *ut.UniversalTranslator
|
||||||
VTrans ut.Translator
|
VTrans ut.Translator
|
||||||
ValidStruct func(interface{}) []string
|
ValidStruct func(*gin.Context, interface{}) []string
|
||||||
ValidVar func(field interface{}, tag string) []string
|
ValidVar func(ctx *gin.Context, field interface{}, tag string) []string
|
||||||
}
|
}
|
||||||
Oss *upload.Oss
|
Oss *upload.Oss
|
||||||
Jwt *jwt.Jwt
|
Jwt *jwt.Jwt
|
||||||
Lock lock.Locker
|
Lock lock.Locker
|
||||||
|
Localizer func(lang string) *i18n.Localizer
|
||||||
|
LoginLimiter *utils.LoginLimiter
|
||||||
)
|
)
|
||||||
|
|||||||
52
global/i18n.go
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
|
||||||
|
}
|
||||||
46
go.mod
@@ -1,30 +1,41 @@
|
|||||||
module Gwen
|
module github.com/lejianwen/rustdesk-api/v2
|
||||||
|
|
||||||
go 1.22
|
go 1.23
|
||||||
|
|
||||||
|
toolchain go1.23.10
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/BurntSushi/toml v1.3.2
|
||||||
github.com/antonfisher/nested-logrus-formatter v1.3.1
|
github.com/antonfisher/nested-logrus-formatter v1.3.1
|
||||||
github.com/fsnotify/fsnotify v1.5.1
|
github.com/coreos/go-oidc/v3 v3.12.0
|
||||||
github.com/fvbock/endless v0.0.0-20170109170031-447134032cb6
|
github.com/fvbock/endless v0.0.0-20170109170031-447134032cb6
|
||||||
github.com/gin-gonic/gin v1.9.0
|
github.com/gin-gonic/gin v1.9.0
|
||||||
|
github.com/go-ldap/ldap/v3 v3.4.10
|
||||||
github.com/go-playground/locales v0.14.1
|
github.com/go-playground/locales v0.14.1
|
||||||
github.com/go-playground/universal-translator v0.18.1
|
github.com/go-playground/universal-translator v0.18.1
|
||||||
github.com/go-playground/validator/v10 v10.11.2
|
github.com/go-playground/validator/v10 v10.26.0
|
||||||
github.com/go-redis/redis/v8 v8.11.4
|
github.com/go-redis/redis/v8 v8.11.4
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.1
|
github.com/golang-jwt/jwt/v5 v5.2.1
|
||||||
|
github.com/google/uuid v1.6.0
|
||||||
|
github.com/mojocn/base64Captcha v1.3.6
|
||||||
|
github.com/nicksnyder/go-i18n/v2 v2.4.0
|
||||||
github.com/sirupsen/logrus v1.8.1
|
github.com/sirupsen/logrus v1.8.1
|
||||||
|
github.com/spf13/cobra v1.8.1
|
||||||
github.com/spf13/viper v1.9.0
|
github.com/spf13/viper v1.9.0
|
||||||
github.com/swaggo/files v1.0.1
|
github.com/swaggo/files v1.0.1
|
||||||
github.com/swaggo/gin-swagger v1.6.0
|
github.com/swaggo/gin-swagger v1.6.0
|
||||||
github.com/swaggo/swag v1.16.3
|
github.com/swaggo/swag v1.16.3
|
||||||
|
golang.org/x/crypto v0.33.0
|
||||||
golang.org/x/oauth2 v0.23.0
|
golang.org/x/oauth2 v0.23.0
|
||||||
|
golang.org/x/text v0.22.0
|
||||||
gorm.io/driver/mysql v1.5.7
|
gorm.io/driver/mysql v1.5.7
|
||||||
|
gorm.io/driver/postgres v1.6.0
|
||||||
gorm.io/driver/sqlite v1.5.6
|
gorm.io/driver/sqlite v1.5.6
|
||||||
gorm.io/gorm v1.25.7
|
gorm.io/gorm v1.25.10
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
cloud.google.com/go/compute/metadata v0.5.1 // indirect
|
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
|
||||||
github.com/KyleBanks/depth v1.2.1 // indirect
|
github.com/KyleBanks/depth v1.2.1 // indirect
|
||||||
github.com/PuerkitoBio/purell v1.1.1 // indirect
|
github.com/PuerkitoBio/purell v1.1.1 // indirect
|
||||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
|
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
|
||||||
@@ -32,20 +43,30 @@ require (
|
|||||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||||
|
github.com/fsnotify/fsnotify v1.5.1 // indirect
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
|
||||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||||
|
github.com/go-asn1-ber/asn1-ber v1.5.7 // indirect
|
||||||
|
github.com/go-jose/go-jose/v4 v4.0.2 // indirect
|
||||||
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
||||||
github.com/go-openapi/jsonreference v0.19.6 // indirect
|
github.com/go-openapi/jsonreference v0.19.6 // indirect
|
||||||
github.com/go-openapi/spec v0.20.4 // indirect
|
github.com/go-openapi/spec v0.20.4 // indirect
|
||||||
github.com/go-openapi/swag v0.19.15 // indirect
|
github.com/go-openapi/swag v0.19.15 // indirect
|
||||||
github.com/go-sql-driver/mysql v1.7.0 // indirect
|
github.com/go-sql-driver/mysql v1.7.0 // indirect
|
||||||
github.com/goccy/go-json v0.10.0 // indirect
|
github.com/goccy/go-json v0.10.0 // indirect
|
||||||
|
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
|
||||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||||
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
|
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||||
|
github.com/jackc/pgx/v5 v5.6.0 // indirect
|
||||||
|
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
||||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
github.com/jinzhu/now v1.1.5 // indirect
|
github.com/jinzhu/now v1.1.5 // indirect
|
||||||
github.com/josharian/intern v1.0.0 // indirect
|
github.com/josharian/intern v1.0.0 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.0.9 // indirect
|
github.com/klauspost/cpuid/v2 v2.0.9 // indirect
|
||||||
github.com/leodido/go-urn v1.2.1 // indirect
|
github.com/leodido/go-urn v1.4.0 // indirect
|
||||||
github.com/magiconair/properties v1.8.5 // indirect
|
github.com/magiconair/properties v1.8.5 // indirect
|
||||||
github.com/mailru/easyjson v0.7.7 // indirect
|
github.com/mailru/easyjson v0.7.7 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.17 // indirect
|
github.com/mattn/go-isatty v0.0.17 // indirect
|
||||||
@@ -55,6 +76,7 @@ require (
|
|||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/pelletier/go-toml v1.9.4 // indirect
|
github.com/pelletier/go-toml v1.9.4 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
|
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
|
||||||
|
github.com/rogpeppe/go-internal v1.14.1 // indirect
|
||||||
github.com/spf13/afero v1.6.0 // indirect
|
github.com/spf13/afero v1.6.0 // indirect
|
||||||
github.com/spf13/cast v1.4.1 // indirect
|
github.com/spf13/cast v1.4.1 // indirect
|
||||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||||
@@ -63,11 +85,11 @@ require (
|
|||||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
github.com/ugorji/go/codec v1.2.9 // indirect
|
github.com/ugorji/go/codec v1.2.9 // indirect
|
||||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
|
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
|
||||||
golang.org/x/crypto v0.23.0 // indirect
|
golang.org/x/image v0.13.0 // indirect
|
||||||
golang.org/x/net v0.21.0 // indirect
|
golang.org/x/net v0.34.0 // indirect
|
||||||
golang.org/x/sys v0.25.0 // indirect
|
golang.org/x/sync v0.11.0 // indirect
|
||||||
golang.org/x/text v0.15.0 // indirect
|
golang.org/x/sys v0.30.0 // indirect
|
||||||
golang.org/x/tools v0.7.0 // indirect
|
golang.org/x/tools v0.26.0 // indirect
|
||||||
google.golang.org/protobuf v1.33.0 // indirect
|
google.golang.org/protobuf v1.33.0 // indirect
|
||||||
gopkg.in/ini.v1 v1.63.2 // indirect
|
gopkg.in/ini.v1 v1.63.2 // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
package admin
|
package admin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"Gwen/global"
|
"encoding/json"
|
||||||
"Gwen/http/request/admin"
|
|
||||||
"Gwen/http/response"
|
|
||||||
"Gwen/service"
|
|
||||||
_ "encoding/json"
|
_ "encoding/json"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/global"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/http/response"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/service"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
@@ -29,16 +30,11 @@ func (ct *AddressBook) Detail(c *gin.Context) {
|
|||||||
id := c.Param("id")
|
id := c.Param("id")
|
||||||
iid, _ := strconv.Atoi(id)
|
iid, _ := strconv.Atoi(id)
|
||||||
t := service.AllService.AddressBookService.InfoByRowId(uint(iid))
|
t := service.AllService.AddressBookService.InfoByRowId(uint(iid))
|
||||||
u := service.AllService.UserService.CurUser(c)
|
|
||||||
if !service.AllService.UserService.IsAdmin(u) && t.UserId != u.Id {
|
|
||||||
response.Fail(c, 101, "无权限")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if t.RowId > 0 {
|
if t.RowId > 0 {
|
||||||
response.Success(c, t)
|
response.Success(c, t)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
response.Fail(c, 101, "信息不存在")
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,25 +52,100 @@ func (ct *AddressBook) Detail(c *gin.Context) {
|
|||||||
func (ct *AddressBook) Create(c *gin.Context) {
|
func (ct *AddressBook) Create(c *gin.Context) {
|
||||||
f := &admin.AddressBookForm{}
|
f := &admin.AddressBookForm{}
|
||||||
if err := c.ShouldBindJSON(f); err != nil {
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
response.Fail(c, 101, "参数错误")
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
errList := global.Validator.ValidStruct(f)
|
errList := global.Validator.ValidStruct(c, f)
|
||||||
if len(errList) > 0 {
|
if len(errList) > 0 {
|
||||||
response.Fail(c, 101, errList[0])
|
response.Fail(c, 101, errList[0])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
t := f.ToAddressBook()
|
t := f.ToAddressBook()
|
||||||
u := service.AllService.UserService.CurUser(c)
|
if t.UserId == 0 {
|
||||||
if !service.AllService.UserService.IsAdmin(u) || t.UserId == 0 {
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||||
t.UserId = u.Id
|
|
||||||
}
|
|
||||||
err := service.AllService.AddressBookService.Create(t)
|
|
||||||
if err != nil {
|
|
||||||
response.Fail(c, 101, "创建失败")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
response.Success(c, u)
|
if t.CollectionId > 0 && !service.AllService.AddressBookService.CheckCollectionOwner(t.UserId, t.CollectionId) {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ex := service.AllService.AddressBookService.InfoByUserIdAndIdAndCid(t.UserId, t.Id, t.CollectionId)
|
||||||
|
if ex.RowId > 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemExists"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err := service.AllService.AddressBookService.Create(t)
|
||||||
|
if err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Success(c, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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/batchCreate [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
|
||||||
|
}
|
||||||
|
ul := len(f.UserIds)
|
||||||
|
|
||||||
|
if ul == 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ul > 1 {
|
||||||
|
//多用户置空标签
|
||||||
|
f.Tags = []string{}
|
||||||
|
//多用户只能创建到默认地址簿
|
||||||
|
f.CollectionId = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
//创建标签
|
||||||
|
/*for _, fu := range f.UserIds {
|
||||||
|
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.InfoByUserIdAndIdAndCid(t.UserId, t.Id, t.CollectionId)
|
||||||
|
if ex.RowId == 0 {
|
||||||
|
service.AllService.AddressBookService.Create(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
response.Success(c, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// List 列表
|
// List 列表
|
||||||
@@ -94,18 +165,34 @@ func (ct *AddressBook) Create(c *gin.Context) {
|
|||||||
func (ct *AddressBook) List(c *gin.Context) {
|
func (ct *AddressBook) List(c *gin.Context) {
|
||||||
query := &admin.AddressBookQuery{}
|
query := &admin.AddressBookQuery{}
|
||||||
if err := c.ShouldBindQuery(query); err != nil {
|
if err := c.ShouldBindQuery(query); err != nil {
|
||||||
response.Fail(c, 101, "参数错误")
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
u := service.AllService.UserService.CurUser(c)
|
|
||||||
if !service.AllService.UserService.IsAdmin(u) || query.IsMy == 1 {
|
|
||||||
query.UserId = int(u.Id)
|
|
||||||
}
|
|
||||||
res := service.AllService.AddressBookService.List(query.Page, query.PageSize, func(tx *gorm.DB) {
|
res := service.AllService.AddressBookService.List(query.Page, query.PageSize, func(tx *gorm.DB) {
|
||||||
|
tx.Preload("Collection", func(txc *gorm.DB) *gorm.DB {
|
||||||
|
return txc.Select("id,name")
|
||||||
|
})
|
||||||
|
if query.Id != "" {
|
||||||
|
tx.Where("id like ?", "%"+query.Id+"%")
|
||||||
|
}
|
||||||
if query.UserId > 0 {
|
if query.UserId > 0 {
|
||||||
tx.Where("user_id = ?", query.UserId)
|
tx.Where("user_id = ?", query.UserId)
|
||||||
}
|
}
|
||||||
|
if query.Username != "" {
|
||||||
|
tx.Where("username like ?", "%"+query.Username+"%")
|
||||||
|
}
|
||||||
|
if query.Hostname != "" {
|
||||||
|
tx.Where("hostname like ?", "%"+query.Hostname+"%")
|
||||||
|
}
|
||||||
|
if query.CollectionId != nil && *query.CollectionId >= 0 {
|
||||||
|
tx.Where("collection_id = ?", query.CollectionId)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
abCIds := make([]uint, 0)
|
||||||
|
for _, ab := range res.AddressBooks {
|
||||||
|
abCIds = append(abCIds, ab.CollectionId)
|
||||||
|
}
|
||||||
response.Success(c, res)
|
response.Success(c, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,27 +210,31 @@ func (ct *AddressBook) List(c *gin.Context) {
|
|||||||
func (ct *AddressBook) Update(c *gin.Context) {
|
func (ct *AddressBook) Update(c *gin.Context) {
|
||||||
f := &admin.AddressBookForm{}
|
f := &admin.AddressBookForm{}
|
||||||
if err := c.ShouldBindJSON(f); err != nil {
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
response.Fail(c, 101, "参数错误")
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
errList := global.Validator.ValidStruct(f)
|
errList := global.Validator.ValidStruct(c, f)
|
||||||
if len(errList) > 0 {
|
if len(errList) > 0 {
|
||||||
response.Fail(c, 101, errList[0])
|
response.Fail(c, 101, errList[0])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if f.RowId == 0 {
|
if f.RowId == 0 {
|
||||||
response.Fail(c, 101, "参数错误")
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ex := service.AllService.AddressBookService.InfoByRowId(f.RowId)
|
||||||
|
if ex.RowId == 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
t := f.ToAddressBook()
|
t := f.ToAddressBook()
|
||||||
u := service.AllService.UserService.CurUser(c)
|
if t.CollectionId > 0 && !service.AllService.AddressBookService.CheckCollectionOwner(t.UserId, t.CollectionId) {
|
||||||
if !service.AllService.UserService.IsAdmin(u) && t.UserId != u.Id {
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||||
response.Fail(c, 101, "无权限")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err := service.AllService.AddressBookService.Update(t)
|
err := service.AllService.AddressBookService.UpdateAll(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.Fail(c, 101, "更新失败")
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
response.Success(c, nil)
|
response.Success(c, nil)
|
||||||
@@ -163,29 +254,109 @@ func (ct *AddressBook) Update(c *gin.Context) {
|
|||||||
func (ct *AddressBook) Delete(c *gin.Context) {
|
func (ct *AddressBook) Delete(c *gin.Context) {
|
||||||
f := &admin.AddressBookForm{}
|
f := &admin.AddressBookForm{}
|
||||||
if err := c.ShouldBindJSON(f); err != nil {
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
response.Fail(c, 101, "系统错误")
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
id := f.RowId
|
id := f.RowId
|
||||||
errList := global.Validator.ValidVar(id, "required,gt=0")
|
errList := global.Validator.ValidVar(c, id, "required,gt=0")
|
||||||
if len(errList) > 0 {
|
if len(errList) > 0 {
|
||||||
response.Fail(c, 101, errList[0])
|
response.Fail(c, 101, errList[0])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
t := service.AllService.AddressBookService.InfoByRowId(f.RowId)
|
t := service.AllService.AddressBookService.InfoByRowId(f.RowId)
|
||||||
u := service.AllService.UserService.CurUser(c)
|
if t.RowId == 0 {
|
||||||
if !service.AllService.UserService.IsAdmin(u) && t.UserId != u.Id {
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
response.Fail(c, 101, "无权限")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if u.Id > 0 {
|
err := service.AllService.AddressBookService.Delete(t)
|
||||||
err := service.AllService.AddressBookService.Delete(t)
|
if err == nil {
|
||||||
if err == nil {
|
response.Success(c, nil)
|
||||||
response.Success(c, nil)
|
return
|
||||||
|
}
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ct *AddressBook) BatchCreateFromPeers(c *gin.Context) {
|
||||||
|
f := &admin.BatchCreateFromPeersForm{}
|
||||||
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.UserId == 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.CollectionId != 0 {
|
||||||
|
collection := service.AllService.AddressBookService.CollectionInfoById(f.CollectionId)
|
||||||
|
if collection.Id == 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
response.Fail(c, 101, err.Error())
|
}
|
||||||
|
|
||||||
|
pl := int64(len(f.PeerIds))
|
||||||
|
peers := service.AllService.PeerService.List(1, uint(pl), func(tx *gorm.DB) {
|
||||||
|
tx.Where("row_id in ?", f.PeerIds)
|
||||||
|
})
|
||||||
|
if peers.Total == 0 || pl != peers.Total {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
response.Fail(c, 101, "信息不存在")
|
|
||||||
|
tags, _ := json.Marshal(f.Tags)
|
||||||
|
for _, peer := range peers.Peers {
|
||||||
|
ab := service.AllService.AddressBookService.FromPeer(peer)
|
||||||
|
ab.Tags = tags
|
||||||
|
ab.CollectionId = f.CollectionId
|
||||||
|
ab.UserId = f.UserId
|
||||||
|
ex := service.AllService.AddressBookService.InfoByUserIdAndIdAndCid(f.UserId, ab.Id, ab.CollectionId)
|
||||||
|
if ex.RowId != 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
service.AllService.AddressBookService.Create(ab)
|
||||||
|
}
|
||||||
|
response.Success(c, nil)
|
||||||
}
|
}
|
||||||
|
|||||||
172
http/controller/admin/addressBookCollection.go
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
package admin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/global"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/http/response"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/model"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/service"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AddressBookCollection struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detail 地址簿名称
|
||||||
|
// @Tags 地址簿名称
|
||||||
|
// @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))
|
||||||
|
if t.Id > 0 {
|
||||||
|
response.Success(c, t)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create 创建地址簿名称
|
||||||
|
// @Tags 地址簿名称
|
||||||
|
// @Summary 创建地址簿名称
|
||||||
|
// @Description 创建地址簿名称
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param body body model.AddressBookCollection true "地址簿名称信息"
|
||||||
|
// @Success 200 {object} response.Response{data=model.AddressBookCollection}
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/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
|
||||||
|
}
|
||||||
|
if f.UserId == 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t := f
|
||||||
|
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 列表
|
||||||
|
// @Tags 地址簿名称
|
||||||
|
// @Summary 地址簿名称列表
|
||||||
|
// @Description 地址簿名称列表
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param page query int false "页码"
|
||||||
|
// @Param page_size query int false "页大小"
|
||||||
|
// @Param is_my query int false "是否是我的"
|
||||||
|
// @Param user_id query int false "用户id"
|
||||||
|
// @Success 200 {object} response.Response{data=model.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
|
||||||
|
}
|
||||||
|
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 编辑
|
||||||
|
// @Tags 地址簿名称
|
||||||
|
// @Summary 地址簿名称编辑
|
||||||
|
// @Description 地址簿名称编辑
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param body body model.AddressBookCollection true "地址簿名称信息"
|
||||||
|
// @Success 200 {object} response.Response{data=model.AddressBookCollection}
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/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()
|
||||||
|
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 删除
|
||||||
|
// @Tags 地址簿名称
|
||||||
|
// @Summary 地址簿名称删除
|
||||||
|
// @Description 地址簿名称删除
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param body body model.AddressBookCollection true "地址簿名称信息"
|
||||||
|
// @Success 200 {object} response.Response
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/address_book_collection/delete [post]
|
||||||
|
// @Security token
|
||||||
|
func (abc *AddressBookCollection) Delete(c *gin.Context) {
|
||||||
|
f := &model.AddressBookCollection{}
|
||||||
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
id := f.Id
|
||||||
|
errList := global.Validator.ValidVar(c, id, "required,gt=0")
|
||||||
|
if len(errList) > 0 {
|
||||||
|
response.Fail(c, 101, errList[0])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ex := service.AllService.AddressBookService.CollectionInfoById(f.Id)
|
||||||
|
if ex.Id == 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err := service.AllService.AddressBookService.DeleteCollection(ex)
|
||||||
|
if err == nil {
|
||||||
|
response.Success(c, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
|
}
|
||||||
222
http/controller/admin/addressBookCollectionRule.go
Normal file
@@ -0,0 +1,222 @@
|
|||||||
|
package admin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/global"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/http/response"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/model"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/service"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AddressBookCollectionRule struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// List 列表
|
||||||
|
// @Tags 地址簿规则
|
||||||
|
// @Summary 地址簿规则列表
|
||||||
|
// @Description 地址簿规则列表
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param page query int false "页码"
|
||||||
|
// @Param page_size query int false "页大小"
|
||||||
|
// @Param is_my query int false "是否是我的"
|
||||||
|
// @Param user_id query int false "用户id"
|
||||||
|
// @Param collection_id query int false "地址簿集合id"
|
||||||
|
// @Success 200 {object} response.Response{data=model.AddressBookCollectionList}
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/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
|
||||||
|
}
|
||||||
|
|
||||||
|
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 地址簿规则
|
||||||
|
// @Tags 地址簿规则
|
||||||
|
// @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))
|
||||||
|
if t.Id > 0 {
|
||||||
|
response.Success(c, t)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create 创建地址簿规则
|
||||||
|
// @Tags 地址簿规则
|
||||||
|
// @Summary 创建地址簿规则
|
||||||
|
// @Description 创建地址簿规则
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param body body model.AddressBookCollectionRule true "地址簿规则信息"
|
||||||
|
// @Success 200 {object} response.Response{data=model.AddressBookCollection}
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/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
|
||||||
|
msg, res := abcr.CheckForm(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(t *model.AddressBookCollectionRule) (string, bool) {
|
||||||
|
if t.UserId == 0 {
|
||||||
|
return "ParamsError", 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 "CannotShareToSelf", false
|
||||||
|
}
|
||||||
|
tou := service.AllService.UserService.InfoById(t.ToId)
|
||||||
|
if tou.Id == 0 {
|
||||||
|
return "ItemNotFound", false
|
||||||
|
}
|
||||||
|
} else if t.Type == model.ShareAddressBookRuleTypeGroup {
|
||||||
|
tog := service.AllService.GroupService.InfoById(t.ToId)
|
||||||
|
if tog.Id == 0 {
|
||||||
|
return "ItemNotFound", false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return "ParamsError", false
|
||||||
|
}
|
||||||
|
// 重复检查
|
||||||
|
ex := service.AllService.AddressBookService.RuleInfoByToIdAndCid(t.Type, t.ToId, t.CollectionId)
|
||||||
|
if t.Id == 0 && ex.Id > 0 {
|
||||||
|
return "ItemExists", false
|
||||||
|
}
|
||||||
|
if t.Id > 0 && ex.Id > 0 && t.Id != ex.Id {
|
||||||
|
return "ItemExists", false
|
||||||
|
}
|
||||||
|
return "", true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update 编辑
|
||||||
|
// @Tags 地址簿规则
|
||||||
|
// @Summary 地址簿规则编辑
|
||||||
|
// @Description 地址簿规则编辑
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param body body model.AddressBookCollectionRule true "地址簿规则信息"
|
||||||
|
// @Success 200 {object} response.Response{data=model.AddressBookCollection}
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/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
|
||||||
|
msg, res := abcr.CheckForm(t)
|
||||||
|
if !res {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, msg))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err := service.AllService.AddressBookService.UpdateRule(t)
|
||||||
|
if err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Success(c, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete 删除
|
||||||
|
// @Tags 地址簿规则
|
||||||
|
// @Summary 地址簿规则删除
|
||||||
|
// @Description 地址簿规则删除
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param body body model.AddressBookCollectionRule true "地址簿规则信息"
|
||||||
|
// @Success 200 {object} response.Response
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/address_book_collection_rule/delete [post]
|
||||||
|
// @Security token
|
||||||
|
func (abcr *AddressBookCollectionRule) Delete(c *gin.Context) {
|
||||||
|
f := &model.AddressBookCollectionRule{}
|
||||||
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
id := f.Id
|
||||||
|
errList := global.Validator.ValidVar(c, id, "required,gt=0")
|
||||||
|
if len(errList) > 0 {
|
||||||
|
response.Fail(c, 101, errList[0])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ex := service.AllService.AddressBookService.RuleInfoById(f.Id)
|
||||||
|
if ex.Id == 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err := service.AllService.AddressBookService.DeleteRule(ex)
|
||||||
|
if err == nil {
|
||||||
|
response.Success(c, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
|
}
|
||||||
212
http/controller/admin/audit.go
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
package admin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/global"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/http/response"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/model"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/service"
|
||||||
|
"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"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// BatchConnDelete 删除
|
||||||
|
// @Tags 链接日志
|
||||||
|
// @Summary 链接日志批量删除
|
||||||
|
// @Description 链接日志批量删除
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param body body admin.AuditConnLogIds true "链接日志"
|
||||||
|
// @Success 200 {object} response.Response
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/audit_conn/batchDelete [post]
|
||||||
|
// @Security token
|
||||||
|
func (a *Audit) BatchConnDelete(c *gin.Context) {
|
||||||
|
f := &admin.AuditConnLogIds{}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
err := service.AllService.AuditService.BatchDeleteAuditConn(f.Ids)
|
||||||
|
if err == nil {
|
||||||
|
response.Success(c, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Fail(c, 101, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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_file/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_file/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"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// BatchFileDelete 删除
|
||||||
|
// @Tags 文件日志
|
||||||
|
// @Summary 文件日志批量删除
|
||||||
|
// @Description 文件日志批量删除
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param body body admin.AuditFileLogIds true "文件日志"
|
||||||
|
// @Success 200 {object} response.Response
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/audit_file/batchDelete [post]
|
||||||
|
// @Security token
|
||||||
|
func (a *Audit) BatchFileDelete(c *gin.Context) {
|
||||||
|
f := &admin.AuditFileLogIds{}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
err := service.AllService.AuditService.BatchDeleteAuditFile(f.Ids)
|
||||||
|
if err == nil {
|
||||||
|
response.Success(c, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Fail(c, 101, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
97
http/controller/admin/config.go
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
package admin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/global"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/http/response"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/model"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/service"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerConfig RUSTDESK服务配置
|
||||||
|
// @Tags ADMIN
|
||||||
|
// @Summary RUSTDESK服务配置
|
||||||
|
// @Description 服务配置,给webclient提供api-server
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 {object} response.Response
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/config/server [get]
|
||||||
|
// @Security token
|
||||||
|
func (co *Config) ServerConfig(c *gin.Context) {
|
||||||
|
cf := &response.ServerConfigResponse{
|
||||||
|
IdServer: global.Config.Rustdesk.IdServer,
|
||||||
|
Key: global.Config.Rustdesk.Key,
|
||||||
|
RelayServer: global.Config.Rustdesk.RelayServer,
|
||||||
|
ApiServer: global.Config.Rustdesk.ApiServer,
|
||||||
|
}
|
||||||
|
response.Success(c, cf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AppConfig APP服务配置
|
||||||
|
// @Tags ADMIN
|
||||||
|
// @Summary APP服务配置
|
||||||
|
// @Description APP服务配置
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 {object} response.Response
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/config/app [get]
|
||||||
|
// @Security token
|
||||||
|
func (co *Config) AppConfig(c *gin.Context) {
|
||||||
|
response.Success(c, &gin.H{
|
||||||
|
"web_client": global.Config.App.WebClient,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// AdminConfig ADMIN服务配置
|
||||||
|
// @Tags ADMIN
|
||||||
|
// @Summary ADMIN服务配置
|
||||||
|
// @Description ADMIN服务配置
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 {object} response.Response
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/config/admin [get]
|
||||||
|
// @Security token
|
||||||
|
func (co *Config) AdminConfig(c *gin.Context) {
|
||||||
|
|
||||||
|
u := &model.User{}
|
||||||
|
token := c.GetHeader("api-token")
|
||||||
|
if token != "" {
|
||||||
|
u, _ = service.AllService.UserService.InfoByAccessToken(token)
|
||||||
|
if !service.AllService.UserService.CheckUserEnable(u) {
|
||||||
|
u.Id = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if u.Id == 0 {
|
||||||
|
response.Success(c, &gin.H{
|
||||||
|
"title": global.Config.Admin.Title,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
hello := global.Config.Admin.Hello
|
||||||
|
if hello == "" {
|
||||||
|
helloFile := global.Config.Admin.HelloFile
|
||||||
|
if helloFile != "" {
|
||||||
|
b, err := os.ReadFile(helloFile)
|
||||||
|
if err == nil && len(b) > 0 {
|
||||||
|
hello = string(b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//replace {{username}} to username
|
||||||
|
hello = strings.Replace(hello, "{{username}}", u.Username, -1)
|
||||||
|
response.Success(c, &gin.H{
|
||||||
|
"title": global.Config.Admin.Title,
|
||||||
|
"hello": hello,
|
||||||
|
})
|
||||||
|
}
|
||||||
160
http/controller/admin/deviceGroup.go
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
package admin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/global"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/http/response"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/service"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DeviceGroup struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detail 设备群组
|
||||||
|
// @Tags 设备群组
|
||||||
|
// @Summary 设备群组详情
|
||||||
|
// @Description 设备群组详情
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param id path int true "ID"
|
||||||
|
// @Success 200 {object} response.Response{data=model.Group}
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/device_group/detail/{id} [get]
|
||||||
|
// @Security token
|
||||||
|
func (ct *DeviceGroup) Detail(c *gin.Context) {
|
||||||
|
id := c.Param("id")
|
||||||
|
iid, _ := strconv.Atoi(id)
|
||||||
|
u := service.AllService.GroupService.DeviceGroupInfoById(uint(iid))
|
||||||
|
if u.Id > 0 {
|
||||||
|
response.Success(c, u)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create 创建设备群组
|
||||||
|
// @Tags 设备群组
|
||||||
|
// @Summary 创建设备群组
|
||||||
|
// @Description 创建设备群组
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param body body admin.DeviceGroupForm true "设备群组信息"
|
||||||
|
// @Success 200 {object} response.Response{data=model.DeviceGroup}
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/device_group/create [post]
|
||||||
|
// @Security token
|
||||||
|
func (ct *DeviceGroup) Create(c *gin.Context) {
|
||||||
|
f := &admin.DeviceGroupForm{}
|
||||||
|
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 := f.ToDeviceGroup()
|
||||||
|
err := service.AllService.GroupService.DeviceGroupCreate(u)
|
||||||
|
if err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Success(c, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// List 列表
|
||||||
|
// @Tags 群组
|
||||||
|
// @Summary 群组列表
|
||||||
|
// @Description 群组列表
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param page query int false "页码"
|
||||||
|
// @Param page_size query int false "页大小"
|
||||||
|
// @Success 200 {object} response.Response{data=model.GroupList}
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/device_group/list [get]
|
||||||
|
// @Security token
|
||||||
|
func (ct *DeviceGroup) 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.GroupService.DeviceGroupList(query.Page, query.PageSize, nil)
|
||||||
|
response.Success(c, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update 编辑
|
||||||
|
// @Tags 设备群组
|
||||||
|
// @Summary 设备群组编辑
|
||||||
|
// @Description 设备群组编辑
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param body body admin.DeviceGroupForm true "群组信息"
|
||||||
|
// @Success 200 {object} response.Response{data=model.Group}
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/device_group/update [post]
|
||||||
|
// @Security token
|
||||||
|
func (ct *DeviceGroup) Update(c *gin.Context) {
|
||||||
|
f := &admin.DeviceGroupForm{}
|
||||||
|
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.ToDeviceGroup()
|
||||||
|
err := service.AllService.GroupService.DeviceGroupUpdate(u)
|
||||||
|
if err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Success(c, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete 删除
|
||||||
|
// @Tags 设备群组
|
||||||
|
// @Summary 设备群组删除
|
||||||
|
// @Description 设备群组删除
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param body body admin.DeviceGroupForm true "群组信息"
|
||||||
|
// @Success 200 {object} response.Response
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/device_group/delete [post]
|
||||||
|
// @Security token
|
||||||
|
func (ct *DeviceGroup) Delete(c *gin.Context) {
|
||||||
|
f := &admin.DeviceGroupForm{}
|
||||||
|
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.GroupService.DeviceGroupInfoById(f.Id)
|
||||||
|
if u.Id > 0 {
|
||||||
|
err := service.AllService.GroupService.DeviceGroupDelete(u)
|
||||||
|
if err == nil {
|
||||||
|
response.Success(c, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
|
}
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
package admin
|
package admin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"Gwen/global"
|
|
||||||
"Gwen/http/response"
|
|
||||||
"Gwen/lib/upload"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/global"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/http/response"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/lib/upload"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -38,7 +38,7 @@ func (f *File) Notify(c *gin.Context) {
|
|||||||
|
|
||||||
res := global.Oss.Verify(c.Request)
|
res := global.Oss.Verify(c.Request)
|
||||||
if !res {
|
if !res {
|
||||||
response.Fail(c, 101, "权限错误")
|
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fm := &FileBack{}
|
fm := &FileBack{}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
package admin
|
package admin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"Gwen/global"
|
|
||||||
"Gwen/http/request/admin"
|
|
||||||
"Gwen/http/response"
|
|
||||||
"Gwen/service"
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/global"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/http/response"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/service"
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -31,7 +31,7 @@ func (ct *Group) Detail(c *gin.Context) {
|
|||||||
response.Success(c, u)
|
response.Success(c, u)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
response.Fail(c, 101, "信息不存在")
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,10 +49,10 @@ func (ct *Group) Detail(c *gin.Context) {
|
|||||||
func (ct *Group) Create(c *gin.Context) {
|
func (ct *Group) Create(c *gin.Context) {
|
||||||
f := &admin.GroupForm{}
|
f := &admin.GroupForm{}
|
||||||
if err := c.ShouldBindJSON(f); err != nil {
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
response.Fail(c, 101, "参数错误")
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
errList := global.Validator.ValidStruct(f)
|
errList := global.Validator.ValidStruct(c, f)
|
||||||
if len(errList) > 0 {
|
if len(errList) > 0 {
|
||||||
response.Fail(c, 101, errList[0])
|
response.Fail(c, 101, errList[0])
|
||||||
return
|
return
|
||||||
@@ -60,10 +60,10 @@ func (ct *Group) Create(c *gin.Context) {
|
|||||||
u := f.ToGroup()
|
u := f.ToGroup()
|
||||||
err := service.AllService.GroupService.Create(u)
|
err := service.AllService.GroupService.Create(u)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.Fail(c, 101, "创建失败")
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
response.Success(c, u)
|
response.Success(c, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// List 列表
|
// List 列表
|
||||||
@@ -81,7 +81,7 @@ func (ct *Group) Create(c *gin.Context) {
|
|||||||
func (ct *Group) List(c *gin.Context) {
|
func (ct *Group) List(c *gin.Context) {
|
||||||
query := &admin.PageQuery{}
|
query := &admin.PageQuery{}
|
||||||
if err := c.ShouldBindQuery(query); err != nil {
|
if err := c.ShouldBindQuery(query); err != nil {
|
||||||
response.Fail(c, 101, "参数错误")
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
res := service.AllService.GroupService.List(query.Page, query.PageSize, nil)
|
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) {
|
func (ct *Group) Update(c *gin.Context) {
|
||||||
f := &admin.GroupForm{}
|
f := &admin.GroupForm{}
|
||||||
if err := c.ShouldBindJSON(f); err != nil {
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
response.Fail(c, 101, "参数错误")
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if f.Id == 0 {
|
if f.Id == 0 {
|
||||||
response.Fail(c, 101, "参数错误")
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
errList := global.Validator.ValidStruct(f)
|
errList := global.Validator.ValidStruct(c, f)
|
||||||
if len(errList) > 0 {
|
if len(errList) > 0 {
|
||||||
response.Fail(c, 101, errList[0])
|
response.Fail(c, 101, errList[0])
|
||||||
return
|
return
|
||||||
@@ -117,7 +117,7 @@ func (ct *Group) Update(c *gin.Context) {
|
|||||||
u := f.ToGroup()
|
u := f.ToGroup()
|
||||||
err := service.AllService.GroupService.Update(u)
|
err := service.AllService.GroupService.Update(u)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.Fail(c, 101, "更新失败")
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
response.Success(c, nil)
|
response.Success(c, nil)
|
||||||
@@ -137,11 +137,11 @@ func (ct *Group) Update(c *gin.Context) {
|
|||||||
func (ct *Group) Delete(c *gin.Context) {
|
func (ct *Group) Delete(c *gin.Context) {
|
||||||
f := &admin.GroupForm{}
|
f := &admin.GroupForm{}
|
||||||
if err := c.ShouldBindJSON(f); err != nil {
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
response.Fail(c, 101, "系统错误")
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
id := f.Id
|
id := f.Id
|
||||||
errList := global.Validator.ValidVar(id, "required,gt=0")
|
errList := global.Validator.ValidVar(c, id, "required,gt=0")
|
||||||
if len(errList) > 0 {
|
if len(errList) > 0 {
|
||||||
response.Fail(c, 101, errList[0])
|
response.Fail(c, 101, errList[0])
|
||||||
return
|
return
|
||||||
@@ -153,8 +153,8 @@ func (ct *Group) Delete(c *gin.Context) {
|
|||||||
response.Success(c, nil)
|
response.Success(c, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
response.Fail(c, 101, err.Error())
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
response.Fail(c, 101, "信息不存在")
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,17 @@
|
|||||||
package admin
|
package admin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"Gwen/global"
|
"fmt"
|
||||||
"Gwen/http/request/admin"
|
|
||||||
"Gwen/http/response"
|
|
||||||
adResp "Gwen/http/response/admin"
|
|
||||||
"Gwen/model"
|
|
||||||
"Gwen/service"
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/global"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/http/controller/api"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
|
||||||
|
apiReq "github.com/lejianwen/rustdesk-api/v2/http/request/api"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/http/response"
|
||||||
|
adResp "github.com/lejianwen/rustdesk-api/v2/http/response/admin"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/model"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/service"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Login struct {
|
type Login struct {
|
||||||
@@ -25,39 +29,103 @@ type Login struct {
|
|||||||
// @Router /admin/login [post]
|
// @Router /admin/login [post]
|
||||||
// @Security token
|
// @Security token
|
||||||
func (ct *Login) Login(c *gin.Context) {
|
func (ct *Login) Login(c *gin.Context) {
|
||||||
|
if global.Config.App.DisablePwdLogin {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "PwdLoginDisabled"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查登录限制
|
||||||
|
loginLimiter := global.LoginLimiter
|
||||||
|
clientIp := c.ClientIP()
|
||||||
|
_, needCaptcha := loginLimiter.CheckSecurityStatus(clientIp)
|
||||||
|
|
||||||
f := &admin.Login{}
|
f := &admin.Login{}
|
||||||
err := c.ShouldBindJSON(f)
|
err := c.ShouldBindJSON(f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.Fail(c, 101, "参数错误")
|
loginLimiter.RecordFailedAttempt(clientIp)
|
||||||
|
global.Logger.Warn(fmt.Sprintf("Login Fail: %s %s %s", "ParamsError", c.RemoteIP(), clientIp))
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
errList := global.Validator.ValidStruct(f)
|
errList := global.Validator.ValidStruct(c, f)
|
||||||
if len(errList) > 0 {
|
if len(errList) > 0 {
|
||||||
|
loginLimiter.RecordFailedAttempt(clientIp)
|
||||||
|
global.Logger.Warn(fmt.Sprintf("Login Fail: %s %s %s", "ParamsError", c.RemoteIP(), clientIp))
|
||||||
response.Fail(c, 101, errList[0])
|
response.Fail(c, 101, errList[0])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 检查是否需要验证码
|
||||||
|
if needCaptcha {
|
||||||
|
if f.CaptchaId == "" || f.Captcha == "" || !loginLimiter.VerifyCaptcha(f.CaptchaId, f.Captcha) {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "CaptchaError"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
u := service.AllService.UserService.InfoByUsernamePassword(f.Username, f.Password)
|
u := service.AllService.UserService.InfoByUsernamePassword(f.Username, f.Password)
|
||||||
|
|
||||||
if u.Id == 0 {
|
if u.Id == 0 {
|
||||||
response.Fail(c, 101, "用户名或密码错误")
|
global.Logger.Warn(fmt.Sprintf("Login Fail: %s %s %s", "UsernameOrPasswordError", c.RemoteIP(), clientIp))
|
||||||
|
loginLimiter.RecordFailedAttempt(clientIp)
|
||||||
|
if _, needCaptcha = loginLimiter.CheckSecurityStatus(clientIp); needCaptcha {
|
||||||
|
response.Fail(c, 110, response.TranslateMsg(c, "UsernameOrPasswordError"))
|
||||||
|
} else {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "UsernameOrPasswordError"))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !service.AllService.UserService.CheckUserEnable(u) {
|
||||||
|
if needCaptcha {
|
||||||
|
response.Fail(c, 110, response.TranslateMsg(c, "UserDisabled"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "UserDisabled"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ut := service.AllService.UserService.Login(u, &model.LoginLog{
|
ut := service.AllService.UserService.Login(u, &model.LoginLog{
|
||||||
UserId: u.Id,
|
UserId: u.Id,
|
||||||
Client: "webadmin",
|
Client: model.LoginLogClientWebAdmin,
|
||||||
Uuid: "",
|
Uuid: "", //must be empty
|
||||||
Ip: c.ClientIP(),
|
Ip: clientIp,
|
||||||
Type: "account",
|
Type: model.LoginLogTypeAccount,
|
||||||
Platform: f.Platform,
|
Platform: f.Platform,
|
||||||
})
|
})
|
||||||
|
|
||||||
response.Success(c, &adResp.LoginPayload{
|
// 登录成功,清除登录限制
|
||||||
Token: ut.Token,
|
loginLimiter.RemoveAttempts(clientIp)
|
||||||
Username: u.Username,
|
responseLoginSuccess(c, u, ut.Token)
|
||||||
RouteNames: service.AllService.UserService.RouteNames(u),
|
}
|
||||||
Nickname: u.Nickname,
|
func (ct *Login) Captcha(c *gin.Context) {
|
||||||
|
loginLimiter := global.LoginLimiter
|
||||||
|
clientIp := c.ClientIP()
|
||||||
|
banned, needCaptcha := loginLimiter.CheckSecurityStatus(clientIp)
|
||||||
|
if banned {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "LoginBanned"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !needCaptcha {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "NoCaptchaRequired"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err, captcha := loginLimiter.RequireCaptcha()
|
||||||
|
if err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "CaptchaError")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err, b64 := loginLimiter.DrawCaptcha(captcha.Content)
|
||||||
|
if err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "CaptchaError")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Success(c, gin.H{
|
||||||
|
"captcha": gin.H{
|
||||||
|
"id": captcha.Id,
|
||||||
|
"b64": b64,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,3 +146,97 @@ func (ct *Login) Logout(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
response.Success(c, nil)
|
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) {
|
||||||
|
loginLimiter := global.LoginLimiter
|
||||||
|
clientIp := c.ClientIP()
|
||||||
|
banned, needCaptcha := loginLimiter.CheckSecurityStatus(clientIp)
|
||||||
|
if banned {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "LoginBanned"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ops := service.AllService.OauthService.GetOauthProviders()
|
||||||
|
response.Success(c, gin.H{
|
||||||
|
"ops": ops,
|
||||||
|
"register": global.Config.App.Register,
|
||||||
|
"need_captcha": needCaptcha,
|
||||||
|
"disable_pwd": global.Config.App.DisablePwdLogin,
|
||||||
|
"auto_oidc": global.Config.App.DisablePwdLogin && len(ops) == 1,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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, state, verifier, nonce, url := service.AllService.OauthService.BeginAuth(f.Op)
|
||||||
|
if err != nil {
|
||||||
|
response.Error(c, response.TranslateMsg(c, err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
service.AllService.OauthService.SetOauthCache(state, &service.OauthCacheItem{
|
||||||
|
Action: service.OauthActionTypeLogin,
|
||||||
|
Op: f.Op,
|
||||||
|
Id: f.Id,
|
||||||
|
DeviceType: "webadmin",
|
||||||
|
// DeviceOs: ct.Platform(c),
|
||||||
|
DeviceOs: f.DeviceInfo.Os,
|
||||||
|
Uuid: f.Uuid,
|
||||||
|
Verifier: verifier,
|
||||||
|
Nonce: nonce,
|
||||||
|
}, 5*60)
|
||||||
|
|
||||||
|
response.Success(c, gin.H{
|
||||||
|
"code": state,
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
responseLoginSuccess(c, u, ut.Token)
|
||||||
|
}
|
||||||
|
|
||||||
|
func responseLoginSuccess(c *gin.Context, u *model.User, token string) {
|
||||||
|
lp := &adResp.LoginPayload{}
|
||||||
|
lp.FromUser(u)
|
||||||
|
lp.Token = token
|
||||||
|
lp.RouteNames = service.AllService.UserService.RouteNames(u)
|
||||||
|
response.Success(c, lp)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
package admin
|
package admin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"Gwen/global"
|
|
||||||
"Gwen/http/request/admin"
|
|
||||||
"Gwen/http/response"
|
|
||||||
"Gwen/model"
|
|
||||||
"Gwen/service"
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/global"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/http/response"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/model"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/service"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
@@ -23,7 +23,7 @@ type LoginLog struct {
|
|||||||
// @Param id path int true "ID"
|
// @Param id path int true "ID"
|
||||||
// @Success 200 {object} response.Response{data=model.LoginLog}
|
// @Success 200 {object} response.Response{data=model.LoginLog}
|
||||||
// @Failure 500 {object} response.Response
|
// @Failure 500 {object} response.Response
|
||||||
// @Router /admin/loginLog/detail/{id} [get]
|
// @Router /admin/login_log/detail/{id} [get]
|
||||||
// @Security token
|
// @Security token
|
||||||
func (ct *LoginLog) Detail(c *gin.Context) {
|
func (ct *LoginLog) Detail(c *gin.Context) {
|
||||||
id := c.Param("id")
|
id := c.Param("id")
|
||||||
@@ -33,7 +33,7 @@ func (ct *LoginLog) Detail(c *gin.Context) {
|
|||||||
response.Success(c, u)
|
response.Success(c, u)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
response.Fail(c, 101, "信息不存在")
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,22 +48,19 @@ func (ct *LoginLog) Detail(c *gin.Context) {
|
|||||||
// @Param user_id query int false "用户ID"
|
// @Param user_id query int false "用户ID"
|
||||||
// @Success 200 {object} response.Response{data=model.LoginLogList}
|
// @Success 200 {object} response.Response{data=model.LoginLogList}
|
||||||
// @Failure 500 {object} response.Response
|
// @Failure 500 {object} response.Response
|
||||||
// @Router /admin/loginLog/list [get]
|
// @Router /admin/login_log/list [get]
|
||||||
// @Security token
|
// @Security token
|
||||||
func (ct *LoginLog) List(c *gin.Context) {
|
func (ct *LoginLog) List(c *gin.Context) {
|
||||||
query := &admin.LoginLogQuery{}
|
query := &admin.LoginLogQuery{}
|
||||||
if err := c.ShouldBindQuery(query); err != nil {
|
if err := c.ShouldBindQuery(query); err != nil {
|
||||||
response.Fail(c, 101, "参数错误")
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
u := service.AllService.UserService.CurUser(c)
|
|
||||||
if !service.AllService.UserService.IsAdmin(u) || query.IsMy == 1 {
|
|
||||||
query.UserId = int(u.Id)
|
|
||||||
}
|
|
||||||
res := service.AllService.LoginLogService.List(query.Page, query.PageSize, func(tx *gorm.DB) {
|
res := service.AllService.LoginLogService.List(query.Page, query.PageSize, func(tx *gorm.DB) {
|
||||||
if query.UserId > 0 {
|
if query.UserId > 0 {
|
||||||
tx.Where("user_id = ?", query.UserId)
|
tx.Where("user_id = ?", query.UserId)
|
||||||
}
|
}
|
||||||
|
tx.Order("id desc")
|
||||||
})
|
})
|
||||||
response.Success(c, res)
|
response.Success(c, res)
|
||||||
}
|
}
|
||||||
@@ -77,34 +74,60 @@ func (ct *LoginLog) List(c *gin.Context) {
|
|||||||
// @Param body body model.LoginLog true "登录日志信息"
|
// @Param body body model.LoginLog true "登录日志信息"
|
||||||
// @Success 200 {object} response.Response
|
// @Success 200 {object} response.Response
|
||||||
// @Failure 500 {object} response.Response
|
// @Failure 500 {object} response.Response
|
||||||
// @Router /admin/loginLog/delete [post]
|
// @Router /admin/login_log/delete [post]
|
||||||
// @Security token
|
// @Security token
|
||||||
func (ct *LoginLog) Delete(c *gin.Context) {
|
func (ct *LoginLog) Delete(c *gin.Context) {
|
||||||
f := &model.LoginLog{}
|
f := &model.LoginLog{}
|
||||||
if err := c.ShouldBindJSON(f); err != nil {
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
response.Fail(c, 101, "系统错误")
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
id := f.Id
|
id := f.Id
|
||||||
errList := global.Validator.ValidVar(id, "required,gt=0")
|
errList := global.Validator.ValidVar(c, id, "required,gt=0")
|
||||||
if len(errList) > 0 {
|
if len(errList) > 0 {
|
||||||
response.Fail(c, 101, errList[0])
|
response.Fail(c, 101, errList[0])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
l := service.AllService.LoginLogService.InfoById(f.Id)
|
l := service.AllService.LoginLogService.InfoById(f.Id)
|
||||||
u := service.AllService.UserService.CurUser(c)
|
if l.Id == 0 {
|
||||||
if !service.AllService.UserService.IsAdmin(u) && l.UserId != u.Id {
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
response.Fail(c, 101, "无权限")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if l.Id > 0 {
|
err := service.AllService.LoginLogService.Delete(l)
|
||||||
err := service.AllService.LoginLogService.Delete(l)
|
if err == nil {
|
||||||
if err == nil {
|
response.Success(c, nil)
|
||||||
response.Success(c, nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
response.Fail(c, 101, err.Error())
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
response.Fail(c, 101, "信息不存在")
|
response.Fail(c, 101, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// BatchDelete 删除
|
||||||
|
// @Tags 登录日志
|
||||||
|
// @Summary 登录日志批量删除
|
||||||
|
// @Description 登录日志批量删除
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param body body admin.LoginLogIds true "登录日志"
|
||||||
|
// @Success 200 {object} response.Response
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/login_log/batchDelete [post]
|
||||||
|
// @Security token
|
||||||
|
func (ct *LoginLog) BatchDelete(c *gin.Context) {
|
||||||
|
f := &admin.LoginLogIds{}
|
||||||
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(f.Ids) == 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err := service.AllService.LoginLogService.BatchDelete(f.Ids)
|
||||||
|
if err == nil {
|
||||||
|
response.Success(c, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Fail(c, 101, err.Error())
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
271
http/controller/admin/my/addressBook.go
Normal file
@@ -0,0 +1,271 @@
|
|||||||
|
package my
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/global"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/http/response"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/service"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AddressBook struct{}
|
||||||
|
|
||||||
|
// List 列表
|
||||||
|
// @Tags 我的地址簿
|
||||||
|
// @Summary 地址簿列表
|
||||||
|
// @Description 地址簿列表
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param page query int false "页码"
|
||||||
|
// @Param page_size query int false "页大小"
|
||||||
|
// @Param user_id query int false "用户id"
|
||||||
|
// @Success 200 {object} response.Response{data=model.AddressBookList}
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/my/address_book/list [get]
|
||||||
|
// @Security token
|
||||||
|
func (ct *AddressBook) List(c *gin.Context) {
|
||||||
|
query := &admin.AddressBookQuery{}
|
||||||
|
if err := c.ShouldBindQuery(query); err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
u := service.AllService.UserService.CurUser(c)
|
||||||
|
query.UserId = int(u.Id)
|
||||||
|
res := service.AllService.AddressBookService.List(query.Page, query.PageSize, func(tx *gorm.DB) {
|
||||||
|
//预加载地址簿名称
|
||||||
|
tx.Preload("Collection", func(txc *gorm.DB) *gorm.DB {
|
||||||
|
return txc.Select("id,name")
|
||||||
|
})
|
||||||
|
if query.Id != "" {
|
||||||
|
tx.Where("id like ?", "%"+query.Id+"%")
|
||||||
|
}
|
||||||
|
tx.Where("user_id = ?", query.UserId)
|
||||||
|
if query.Username != "" {
|
||||||
|
tx.Where("username like ?", "%"+query.Username+"%")
|
||||||
|
}
|
||||||
|
if query.Hostname != "" {
|
||||||
|
tx.Where("hostname like ?", "%"+query.Hostname+"%")
|
||||||
|
}
|
||||||
|
if query.CollectionId != nil && *query.CollectionId >= 0 {
|
||||||
|
tx.Where("collection_id = ?", query.CollectionId)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
abCIds := make([]uint, 0)
|
||||||
|
for _, ab := range res.AddressBooks {
|
||||||
|
abCIds = append(abCIds, ab.CollectionId)
|
||||||
|
}
|
||||||
|
response.Success(c, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create 创建地址簿
|
||||||
|
// @Tags 我的地址簿
|
||||||
|
// @Summary 创建地址簿
|
||||||
|
// @Description 创建地址簿
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param body body admin.AddressBookForm true "地址簿信息"
|
||||||
|
// @Success 200 {object} response.Response{data=model.AddressBook}
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/my/address_book/create [post]
|
||||||
|
// @Security token
|
||||||
|
func (ct *AddressBook) Create(c *gin.Context) {
|
||||||
|
f := &admin.AddressBookForm{}
|
||||||
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
errList := global.Validator.ValidStruct(c, f)
|
||||||
|
if len(errList) > 0 {
|
||||||
|
response.Fail(c, 101, errList[0])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t := f.ToAddressBook()
|
||||||
|
u := service.AllService.UserService.CurUser(c)
|
||||||
|
t.UserId = u.Id
|
||||||
|
if t.CollectionId > 0 && !service.AllService.AddressBookService.CheckCollectionOwner(t.UserId, t.CollectionId) {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ex := service.AllService.AddressBookService.InfoByUserIdAndIdAndCid(t.UserId, t.Id, t.CollectionId)
|
||||||
|
if ex.RowId > 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemExists"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err := service.AllService.AddressBookService.Create(t)
|
||||||
|
if err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Success(c, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update 编辑
|
||||||
|
// @Tags 我的地址簿
|
||||||
|
// @Summary 地址簿编辑
|
||||||
|
// @Description 地址簿编辑
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param body body admin.AddressBookForm true "地址簿信息"
|
||||||
|
// @Success 200 {object} response.Response{data=model.AddressBook}
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/my/address_book/update [post]
|
||||||
|
// @Security token
|
||||||
|
func (ct *AddressBook) Update(c *gin.Context) {
|
||||||
|
f := &admin.AddressBookForm{}
|
||||||
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
errList := global.Validator.ValidStruct(c, f)
|
||||||
|
if len(errList) > 0 {
|
||||||
|
response.Fail(c, 101, errList[0])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if f.RowId == 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
u := service.AllService.UserService.CurUser(c)
|
||||||
|
if f.UserId != u.Id {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ex := service.AllService.AddressBookService.InfoByRowId(f.RowId)
|
||||||
|
if ex.RowId == 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ex.UserId != u.Id {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t := f.ToAddressBook()
|
||||||
|
if t.CollectionId > 0 && !service.AllService.AddressBookService.CheckCollectionOwner(t.UserId, t.CollectionId) {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err := service.AllService.AddressBookService.UpdateAll(t)
|
||||||
|
if err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Success(c, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete 删除
|
||||||
|
// @Tags 我的地址簿
|
||||||
|
// @Summary 地址簿删除
|
||||||
|
// @Description 地址簿删除
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param body body admin.AddressBookForm true "地址簿信息"
|
||||||
|
// @Success 200 {object} response.Response
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/my/address_book/delete [post]
|
||||||
|
// @Security token
|
||||||
|
func (ct *AddressBook) Delete(c *gin.Context) {
|
||||||
|
f := &admin.AddressBookForm{}
|
||||||
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
id := f.RowId
|
||||||
|
errList := global.Validator.ValidVar(c, id, "required,gt=0")
|
||||||
|
if len(errList) > 0 {
|
||||||
|
response.Fail(c, 101, errList[0])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ex := service.AllService.AddressBookService.InfoByRowId(f.RowId)
|
||||||
|
if ex.RowId == 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
u := service.AllService.UserService.CurUser(c)
|
||||||
|
if ex.UserId != u.Id {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err := service.AllService.AddressBookService.Delete(ex)
|
||||||
|
if err == nil {
|
||||||
|
response.Success(c, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (ct *AddressBook) BatchCreateFromPeers(c *gin.Context) {
|
||||||
|
f := &admin.BatchCreateFromPeersForm{}
|
||||||
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
u := service.AllService.UserService.CurUser(c)
|
||||||
|
|
||||||
|
if f.CollectionId != 0 {
|
||||||
|
collection := service.AllService.AddressBookService.CollectionInfoById(f.CollectionId)
|
||||||
|
if collection.Id == 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if collection.UserId != u.Id {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(f.PeerIds) == 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pl := int64(len(f.PeerIds))
|
||||||
|
peers := service.AllService.PeerService.List(1, uint(pl), func(tx *gorm.DB) {
|
||||||
|
tx.Where("row_id in ?", f.PeerIds)
|
||||||
|
tx.Where("user_id = ?", u.Id)
|
||||||
|
})
|
||||||
|
if peers.Total == 0 || pl != peers.Total {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tags, _ := json.Marshal(f.Tags)
|
||||||
|
for _, peer := range peers.Peers {
|
||||||
|
ab := service.AllService.AddressBookService.FromPeer(peer)
|
||||||
|
ab.Tags = tags
|
||||||
|
ab.CollectionId = f.CollectionId
|
||||||
|
ex := service.AllService.AddressBookService.InfoByUserIdAndIdAndCid(u.Id, ab.Id, ab.CollectionId)
|
||||||
|
if ex.RowId != 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
service.AllService.AddressBookService.Create(ab)
|
||||||
|
}
|
||||||
|
response.Success(c, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ct *AddressBook) BatchUpdateTags(c *gin.Context) {
|
||||||
|
f := &admin.BatchUpdateTagsForm{}
|
||||||
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
u := service.AllService.UserService.CurUser(c)
|
||||||
|
|
||||||
|
abs := service.AllService.AddressBookService.List(1, 999, func(tx *gorm.DB) {
|
||||||
|
tx.Where("row_id in ?", f.RowIds)
|
||||||
|
tx.Where("user_id = ?", u.Id)
|
||||||
|
})
|
||||||
|
if abs.Total == 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err := service.AllService.AddressBookService.BatchUpdateTags(abs.AddressBooks, f.Tags)
|
||||||
|
if err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Success(c, nil)
|
||||||
|
}
|
||||||
162
http/controller/admin/my/addressBookCollection.go
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
package my
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/global"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/http/response"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/model"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/service"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AddressBookCollection struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create 创建地址簿名称
|
||||||
|
// @Tags 我的地址簿名称
|
||||||
|
// @Summary 创建地址簿名称
|
||||||
|
// @Description 创建地址簿名称
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param body body model.AddressBookCollection true "地址簿名称信息"
|
||||||
|
// @Success 200 {object} response.Response{data=model.AddressBookCollection}
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/my/address_book_collection/create [post]
|
||||||
|
// @Security token
|
||||||
|
func (abc *AddressBookCollection) Create(c *gin.Context) {
|
||||||
|
f := &model.AddressBookCollection{}
|
||||||
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
errList := global.Validator.ValidStruct(c, f)
|
||||||
|
if len(errList) > 0 {
|
||||||
|
response.Fail(c, 101, errList[0])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
u := service.AllService.UserService.CurUser(c)
|
||||||
|
f.UserId = u.Id
|
||||||
|
err := service.AllService.AddressBookService.CreateCollection(f)
|
||||||
|
if err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Success(c, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// List 列表
|
||||||
|
// @Tags 我的地址簿名称
|
||||||
|
// @Summary 地址簿名称列表
|
||||||
|
// @Description 地址簿名称列表
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param page query int false "页码"
|
||||||
|
// @Param page_size query int false "页大小"
|
||||||
|
// @Success 200 {object} response.Response{data=model.AddressBookCollectionList}
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/my/address_book_collection/list [get]
|
||||||
|
// @Security token
|
||||||
|
func (abc *AddressBookCollection) List(c *gin.Context) {
|
||||||
|
query := &admin.AddressBookCollectionQuery{}
|
||||||
|
if err := c.ShouldBindQuery(query); err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
u := service.AllService.UserService.CurUser(c)
|
||||||
|
query.UserId = int(u.Id)
|
||||||
|
res := service.AllService.AddressBookService.ListCollection(query.Page, query.PageSize, func(tx *gorm.DB) {
|
||||||
|
tx.Where("user_id = ?", query.UserId)
|
||||||
|
})
|
||||||
|
response.Success(c, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update 编辑
|
||||||
|
// @Tags 我的地址簿名称
|
||||||
|
// @Summary 地址簿名称编辑
|
||||||
|
// @Description 地址簿名称编辑
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param body body model.AddressBookCollection true "地址簿名称信息"
|
||||||
|
// @Success 200 {object} response.Response{data=model.AddressBookCollection}
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/my/address_book_collection/update [post]
|
||||||
|
// @Security token
|
||||||
|
func (abc *AddressBookCollection) Update(c *gin.Context) {
|
||||||
|
f := &model.AddressBookCollection{}
|
||||||
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
errList := global.Validator.ValidStruct(c, f)
|
||||||
|
if len(errList) > 0 {
|
||||||
|
response.Fail(c, 101, errList[0])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if f.Id == 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
u := service.AllService.UserService.CurUser(c)
|
||||||
|
//if f.UserId != u.Id {
|
||||||
|
// response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
|
||||||
|
// return
|
||||||
|
//}
|
||||||
|
ex := service.AllService.AddressBookService.CollectionInfoById(f.Id)
|
||||||
|
if ex.Id == 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ex.UserId != u.Id {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err := service.AllService.AddressBookService.UpdateCollection(f)
|
||||||
|
if err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Success(c, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete 删除
|
||||||
|
// @Tags 我的地址簿名称
|
||||||
|
// @Summary 地址簿名称删除
|
||||||
|
// @Description 地址簿名称删除
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param body body model.AddressBookCollection true "地址簿名称信息"
|
||||||
|
// @Success 200 {object} response.Response
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/my/address_book_collection/delete [post]
|
||||||
|
// @Security token
|
||||||
|
func (abc *AddressBookCollection) Delete(c *gin.Context) {
|
||||||
|
f := &model.AddressBookCollection{}
|
||||||
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
id := f.Id
|
||||||
|
errList := global.Validator.ValidVar(c, id, "required,gt=0")
|
||||||
|
if len(errList) > 0 {
|
||||||
|
response.Fail(c, 101, errList[0])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ex := service.AllService.AddressBookService.CollectionInfoById(f.Id)
|
||||||
|
if ex.Id == 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
u := service.AllService.UserService.CurUser(c)
|
||||||
|
if ex.UserId != u.Id {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err := service.AllService.AddressBookService.DeleteCollection(ex)
|
||||||
|
if err == nil {
|
||||||
|
response.Success(c, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
|
}
|
||||||
228
http/controller/admin/my/addressBookCollectionRule.go
Normal file
@@ -0,0 +1,228 @@
|
|||||||
|
package my
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/global"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/http/response"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/model"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/service"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AddressBookCollectionRule struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// List 列表
|
||||||
|
// @Tags 我的地址簿规则
|
||||||
|
// @Summary 地址簿规则列表
|
||||||
|
// @Description 地址簿规则列表
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param page query int false "页码"
|
||||||
|
// @Param page_size query int false "页大小"
|
||||||
|
// @Param is_my query int false "是否是我的"
|
||||||
|
// @Param user_id query int false "用户id"
|
||||||
|
// @Param collection_id query int false "地址簿集合id"
|
||||||
|
// @Success 200 {object} response.Response{data=model.AddressBookCollectionList}
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/my/address_book_collection_rule/list [get]
|
||||||
|
// @Security token
|
||||||
|
func (abcr *AddressBookCollectionRule) List(c *gin.Context) {
|
||||||
|
query := &admin.AddressBookCollectionRuleQuery{}
|
||||||
|
if err := c.ShouldBindQuery(query); err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
u := service.AllService.UserService.CurUser(c)
|
||||||
|
query.UserId = int(u.Id)
|
||||||
|
|
||||||
|
res := service.AllService.AddressBookService.ListRules(query.Page, query.PageSize, func(tx *gorm.DB) {
|
||||||
|
tx.Where("user_id = ?", query.UserId)
|
||||||
|
if query.CollectionId > 0 {
|
||||||
|
tx.Where("collection_id = ?", query.CollectionId)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
response.Success(c, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create 创建地址簿规则
|
||||||
|
// @Tags 我的地址簿规则
|
||||||
|
// @Summary 创建地址簿规则
|
||||||
|
// @Description 创建地址簿规则
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param body body model.AddressBookCollectionRule true "地址簿规则信息"
|
||||||
|
// @Success 200 {object} response.Response{data=model.AddressBookCollection}
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/my/address_book_collection_rule/create [post]
|
||||||
|
// @Security token
|
||||||
|
func (abcr *AddressBookCollectionRule) Create(c *gin.Context) {
|
||||||
|
f := &model.AddressBookCollectionRule{}
|
||||||
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
errList := global.Validator.ValidStruct(c, f)
|
||||||
|
if len(errList) > 0 {
|
||||||
|
response.Fail(c, 101, errList[0])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if f.Type != model.ShareAddressBookRuleTypePersonal && f.Type != model.ShareAddressBookRuleTypeGroup {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//t := f.ToAddressBookCollection()
|
||||||
|
t := f
|
||||||
|
u := service.AllService.UserService.CurUser(c)
|
||||||
|
t.UserId = u.Id
|
||||||
|
msg, res := abcr.CheckForm(u, t)
|
||||||
|
if !res {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, msg))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err := service.AllService.AddressBookService.CreateRule(t)
|
||||||
|
if err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Success(c, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (abcr *AddressBookCollectionRule) CheckForm(u *model.User, t *model.AddressBookCollectionRule) (string, bool) {
|
||||||
|
if t.UserId != u.Id {
|
||||||
|
return "NoAccess", false
|
||||||
|
}
|
||||||
|
if t.CollectionId > 0 && !service.AllService.AddressBookService.CheckCollectionOwner(t.UserId, t.CollectionId) {
|
||||||
|
return "ParamsError", false
|
||||||
|
}
|
||||||
|
|
||||||
|
//check to_id
|
||||||
|
if t.Type == model.ShareAddressBookRuleTypePersonal {
|
||||||
|
if t.ToId == t.UserId {
|
||||||
|
return "CannotShareToSelf", false
|
||||||
|
}
|
||||||
|
tou := service.AllService.UserService.InfoById(t.ToId)
|
||||||
|
if tou.Id == 0 {
|
||||||
|
return "ItemNotFound", false
|
||||||
|
}
|
||||||
|
//非管理员不能分享给非本组织用户
|
||||||
|
//if tou.GroupId != u.GroupId {
|
||||||
|
// return "NoAccess", false
|
||||||
|
//}
|
||||||
|
} else if t.Type == model.ShareAddressBookRuleTypeGroup {
|
||||||
|
//非管理员不能分享给其他组
|
||||||
|
//if t.ToId != u.GroupId {
|
||||||
|
// return "NoAccess", false
|
||||||
|
//}
|
||||||
|
|
||||||
|
tog := service.AllService.GroupService.InfoById(t.ToId)
|
||||||
|
if tog.Id == 0 {
|
||||||
|
return "ItemNotFound", false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return "ParamsError", false
|
||||||
|
}
|
||||||
|
// 重复检查
|
||||||
|
ex := service.AllService.AddressBookService.RuleInfoByToIdAndCid(t.Type, t.ToId, t.CollectionId)
|
||||||
|
if t.Id == 0 && ex.Id > 0 {
|
||||||
|
return "ItemExists", false
|
||||||
|
}
|
||||||
|
if t.Id > 0 && ex.Id > 0 && t.Id != ex.Id {
|
||||||
|
return "ItemExists", false
|
||||||
|
}
|
||||||
|
return "", true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update 编辑
|
||||||
|
// @Tags 我的地址簿规则
|
||||||
|
// @Summary 地址簿规则编辑
|
||||||
|
// @Description 地址簿规则编辑
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param body body model.AddressBookCollectionRule true "地址簿规则信息"
|
||||||
|
// @Success 200 {object} response.Response{data=model.AddressBookCollection}
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/my/address_book_collection_rule/update [post]
|
||||||
|
// @Security token
|
||||||
|
func (abcr *AddressBookCollectionRule) Update(c *gin.Context) {
|
||||||
|
f := &model.AddressBookCollectionRule{}
|
||||||
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
errList := global.Validator.ValidStruct(c, f)
|
||||||
|
if len(errList) > 0 {
|
||||||
|
response.Fail(c, 101, errList[0])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if f.Id == 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
u := service.AllService.UserService.CurUser(c)
|
||||||
|
|
||||||
|
ex := service.AllService.AddressBookService.RuleInfoById(f.Id)
|
||||||
|
if ex.Id == 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ex.UserId != u.Id {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t := f
|
||||||
|
msg, res := abcr.CheckForm(u, t)
|
||||||
|
if !res {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, msg))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err := service.AllService.AddressBookService.UpdateRule(t)
|
||||||
|
if err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Success(c, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete 删除
|
||||||
|
// @Tags 我的地址簿规则
|
||||||
|
// @Summary 地址簿规则删除
|
||||||
|
// @Description 地址簿规则删除
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param body body model.AddressBookCollectionRule true "地址簿规则信息"
|
||||||
|
// @Success 200 {object} response.Response
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/my/address_book_collection_rule/delete [post]
|
||||||
|
// @Security token
|
||||||
|
func (abcr *AddressBookCollectionRule) Delete(c *gin.Context) {
|
||||||
|
f := &model.AddressBookCollectionRule{}
|
||||||
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
id := f.Id
|
||||||
|
errList := global.Validator.ValidVar(c, id, "required,gt=0")
|
||||||
|
if len(errList) > 0 {
|
||||||
|
response.Fail(c, 101, errList[0])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ex := service.AllService.AddressBookService.RuleInfoById(f.Id)
|
||||||
|
if ex.Id == 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
u := service.AllService.UserService.CurUser(c)
|
||||||
|
if ex.UserId != u.Id {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err := service.AllService.AddressBookService.DeleteRule(ex)
|
||||||
|
if err == nil {
|
||||||
|
response.Success(c, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
|
}
|
||||||
113
http/controller/admin/my/loginLog.go
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
package my
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/global"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/http/response"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/model"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/service"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LoginLog struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// List 列表
|
||||||
|
// @Tags 我的登录日志
|
||||||
|
// @Summary 登录日志列表
|
||||||
|
// @Description 登录日志列表
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param page query int false "页码"
|
||||||
|
// @Param page_size query int false "页大小"
|
||||||
|
// @Param user_id query int false "用户ID"
|
||||||
|
// @Success 200 {object} response.Response{data=model.LoginLogList}
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/my/login_log/list [get]
|
||||||
|
// @Security token
|
||||||
|
func (ct *LoginLog) List(c *gin.Context) {
|
||||||
|
query := &admin.LoginLogQuery{}
|
||||||
|
if err := c.ShouldBindQuery(query); err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
u := service.AllService.UserService.CurUser(c)
|
||||||
|
res := service.AllService.LoginLogService.List(query.Page, query.PageSize, func(tx *gorm.DB) {
|
||||||
|
tx.Where("user_id = ? and is_deleted = ?", u.Id, model.IsDeletedNo)
|
||||||
|
tx.Order("id desc")
|
||||||
|
})
|
||||||
|
response.Success(c, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete 删除
|
||||||
|
// @Tags 我的登录日志
|
||||||
|
// @Summary 登录日志删除
|
||||||
|
// @Description 登录日志删除
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param body body model.LoginLog true "登录日志信息"
|
||||||
|
// @Success 200 {object} response.Response
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/my/login_log/delete [post]
|
||||||
|
// @Security token
|
||||||
|
func (ct *LoginLog) Delete(c *gin.Context) {
|
||||||
|
f := &model.LoginLog{}
|
||||||
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
id := f.Id
|
||||||
|
errList := global.Validator.ValidVar(c, id, "required,gt=0")
|
||||||
|
if len(errList) > 0 {
|
||||||
|
response.Fail(c, 101, errList[0])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
l := service.AllService.LoginLogService.InfoById(f.Id)
|
||||||
|
if l.Id == 0 || l.IsDeleted == model.IsDeletedYes {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
u := service.AllService.UserService.CurUser(c)
|
||||||
|
if l.UserId != u.Id {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err := service.AllService.LoginLogService.SoftDelete(l)
|
||||||
|
if err == nil {
|
||||||
|
response.Success(c, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Fail(c, 101, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// BatchDelete 删除
|
||||||
|
// @Tags 我的登录日志
|
||||||
|
// @Summary 登录日志批量删除
|
||||||
|
// @Description 登录日志批量删除
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param body body admin.LoginLogIds true "登录日志"
|
||||||
|
// @Success 200 {object} response.Response
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/my/login_log/batchDelete [post]
|
||||||
|
// @Security token
|
||||||
|
func (ct *LoginLog) BatchDelete(c *gin.Context) {
|
||||||
|
f := &admin.LoginLogIds{}
|
||||||
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(f.Ids) == 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
u := service.AllService.UserService.CurUser(c)
|
||||||
|
err := service.AllService.LoginLogService.BatchSoftDelete(u.Id, f.Ids)
|
||||||
|
if err == nil {
|
||||||
|
response.Success(c, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Fail(c, 101, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
59
http/controller/admin/my/peer.go
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
package my
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/http/response"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/service"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Peer struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// List 列表
|
||||||
|
// @Tags 我的设备
|
||||||
|
// @Summary 设备列表
|
||||||
|
// @Description 设备列表
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param page query int false "页码"
|
||||||
|
// @Param page_size query int false "页大小"
|
||||||
|
// @Param time_ago query int false "时间"
|
||||||
|
// @Param id query string false "ID"
|
||||||
|
// @Param hostname query string false "主机名"
|
||||||
|
// @Param uuids query string false "uuids 用逗号分隔"
|
||||||
|
// @Success 200 {object} response.Response{data=model.PeerList}
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/my/peer/list [get]
|
||||||
|
// @Security token
|
||||||
|
func (ct *Peer) List(c *gin.Context) {
|
||||||
|
query := &admin.PeerQuery{}
|
||||||
|
if err := c.ShouldBindQuery(query); err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
u := service.AllService.UserService.CurUser(c)
|
||||||
|
res := service.AllService.PeerService.List(query.Page, query.PageSize, func(tx *gorm.DB) {
|
||||||
|
tx.Where("user_id = ?", u.Id)
|
||||||
|
if query.TimeAgo > 0 {
|
||||||
|
lt := time.Now().Unix() - int64(query.TimeAgo)
|
||||||
|
tx.Where("last_online_time < ?", lt)
|
||||||
|
}
|
||||||
|
if query.TimeAgo < 0 {
|
||||||
|
lt := time.Now().Unix() + int64(query.TimeAgo)
|
||||||
|
tx.Where("last_online_time > ?", lt)
|
||||||
|
}
|
||||||
|
if query.Id != "" {
|
||||||
|
tx.Where("id like ?", "%"+query.Id+"%")
|
||||||
|
}
|
||||||
|
if query.Hostname != "" {
|
||||||
|
tx.Where("hostname like ?", "%"+query.Hostname+"%")
|
||||||
|
}
|
||||||
|
if query.Uuids != "" {
|
||||||
|
tx.Where("uuid in (?)", query.Uuids)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
response.Success(c, res)
|
||||||
|
}
|
||||||
119
http/controller/admin/my/shareRecord.go
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
package my
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/global"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/http/response"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/service"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ShareRecord struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// List 分享记录列表
|
||||||
|
// @Tags 我的分享记录
|
||||||
|
// @Summary 分享记录列表
|
||||||
|
// @Description 分享记录列表
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param page query int false "页码"
|
||||||
|
// @Param page_size query int false "页大小"
|
||||||
|
// @Success 200 {object} response.Response
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/my/share_record/list [get]
|
||||||
|
// @Security token
|
||||||
|
func (sr *ShareRecord) 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
|
||||||
|
}
|
||||||
|
u := service.AllService.UserService.CurUser(c)
|
||||||
|
res := service.AllService.ShareRecordService.List(query.Page, query.PageSize, func(tx *gorm.DB) {
|
||||||
|
tx.Where("user_id = ?", u.Id)
|
||||||
|
})
|
||||||
|
response.Success(c, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete 分享记录删除
|
||||||
|
// @Tags 我的分享记录
|
||||||
|
// @Summary 分享记录删除
|
||||||
|
// @Description 分享记录删除
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param body body admin.ShareRecordForm true "分享记录信息"
|
||||||
|
// @Success 200 {object} response.Response
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/my/share_record/delete [post]
|
||||||
|
// @Security token
|
||||||
|
func (sr *ShareRecord) Delete(c *gin.Context) {
|
||||||
|
f := &admin.ShareRecordForm{}
|
||||||
|
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.UserService.CurUser(c)
|
||||||
|
i := service.AllService.ShareRecordService.InfoById(f.Id)
|
||||||
|
if i.UserId != u.Id {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if i.Id == 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err := service.AllService.ShareRecordService.Delete(i)
|
||||||
|
if err == nil {
|
||||||
|
response.Success(c, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// BatchDelete 批量删除我的分享记录
|
||||||
|
// @Tags 我的
|
||||||
|
// @Summary 批量删除我的分享记录
|
||||||
|
// @Description 批量删除我的分享记录
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param body body admin.PeerShareRecordBatchDeleteForm true "id"
|
||||||
|
// @Success 200 {object} response.Response
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/my/share_record/batchDelete [post]
|
||||||
|
// @Security token
|
||||||
|
func (sr *ShareRecord) BatchDelete(c *gin.Context) {
|
||||||
|
f := &admin.PeerShareRecordBatchDeleteForm{}
|
||||||
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(f.Ids) == 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
u := service.AllService.UserService.CurUser(c)
|
||||||
|
var l int64
|
||||||
|
l = int64(len(f.Ids))
|
||||||
|
res := service.AllService.ShareRecordService.List(1, uint(l), func(tx *gorm.DB) {
|
||||||
|
tx.Where("user_id = ?", u.Id)
|
||||||
|
tx.Where("id in ?", f.Ids)
|
||||||
|
})
|
||||||
|
if res.Total != l {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err := service.AllService.ShareRecordService.BatchDelete(f.Ids)
|
||||||
|
if err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Success(c, nil)
|
||||||
|
}
|
||||||
176
http/controller/admin/my/tag.go
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
package my
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/global"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/http/response"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/service"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Tag struct{}
|
||||||
|
|
||||||
|
// List 列表
|
||||||
|
// @Tags 我的标签
|
||||||
|
// @Summary 标签列表
|
||||||
|
// @Description 标签列表
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param page query int false "页码"
|
||||||
|
// @Param page_size query int false "页大小"
|
||||||
|
// @Param is_my query int false "是否是我的"
|
||||||
|
// @Param user_id query int false "用户id"
|
||||||
|
// @Success 200 {object} response.Response{data=model.TagList}
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/my/tag/list [get]
|
||||||
|
// @Security token
|
||||||
|
func (ct *Tag) List(c *gin.Context) {
|
||||||
|
query := &admin.TagQuery{}
|
||||||
|
if err := c.ShouldBindQuery(query); err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
u := service.AllService.UserService.CurUser(c)
|
||||||
|
query.UserId = int(u.Id)
|
||||||
|
res := service.AllService.TagService.List(query.Page, query.PageSize, func(tx *gorm.DB) {
|
||||||
|
tx.Preload("Collection", func(txc *gorm.DB) *gorm.DB {
|
||||||
|
return txc.Select("id,name")
|
||||||
|
})
|
||||||
|
tx.Where("user_id = ?", query.UserId)
|
||||||
|
if query.CollectionId != nil && *query.CollectionId >= 0 {
|
||||||
|
tx.Where("collection_id = ?", query.CollectionId)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
response.Success(c, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create 创建标签
|
||||||
|
// @Tags 我的标签
|
||||||
|
// @Summary 创建标签
|
||||||
|
// @Description 创建标签
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param body body admin.TagForm true "标签信息"
|
||||||
|
// @Success 200 {object} response.Response{data=model.Tag}
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/my/tag/create [post]
|
||||||
|
// @Security token
|
||||||
|
func (ct *Tag) Create(c *gin.Context) {
|
||||||
|
f := &admin.TagForm{}
|
||||||
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
errList := global.Validator.ValidStruct(c, f)
|
||||||
|
if len(errList) > 0 {
|
||||||
|
response.Fail(c, 101, errList[0])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t := f.ToTag()
|
||||||
|
u := service.AllService.UserService.CurUser(c)
|
||||||
|
t.UserId = u.Id
|
||||||
|
err := service.AllService.TagService.Create(t)
|
||||||
|
if err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Success(c, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update 编辑
|
||||||
|
// @Tags 我的标签
|
||||||
|
// @Summary 标签编辑
|
||||||
|
// @Description 标签编辑
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param body body admin.TagForm true "标签信息"
|
||||||
|
// @Success 200 {object} response.Response{data=model.Tag}
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/my/tag/update [post]
|
||||||
|
// @Security token
|
||||||
|
func (ct *Tag) Update(c *gin.Context) {
|
||||||
|
f := &admin.TagForm{}
|
||||||
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
errList := global.Validator.ValidStruct(c, f)
|
||||||
|
if len(errList) > 0 {
|
||||||
|
response.Fail(c, 101, errList[0])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if f.Id == 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
u := service.AllService.UserService.CurUser(c)
|
||||||
|
if f.UserId != u.Id {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ex := service.AllService.TagService.InfoById(f.Id)
|
||||||
|
if ex.Id == 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ex.UserId != u.Id {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
t := f.ToTag()
|
||||||
|
if t.CollectionId > 0 && !service.AllService.AddressBookService.CheckCollectionOwner(t.UserId, t.CollectionId) {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err := service.AllService.TagService.Update(t)
|
||||||
|
if err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Success(c, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete 删除
|
||||||
|
// @Tags 标签
|
||||||
|
// @Summary 标签删除
|
||||||
|
// @Description 标签删除
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param body body admin.TagForm true "标签信息"
|
||||||
|
// @Success 200 {object} response.Response
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/my/tag/delete [post]
|
||||||
|
// @Security token
|
||||||
|
func (ct *Tag) Delete(c *gin.Context) {
|
||||||
|
f := &admin.TagForm{}
|
||||||
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
id := f.Id
|
||||||
|
errList := global.Validator.ValidVar(c, id, "required,gt=0")
|
||||||
|
if len(errList) > 0 {
|
||||||
|
response.Fail(c, 101, errList[0])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ex := service.AllService.TagService.InfoById(f.Id)
|
||||||
|
if ex.Id == 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
u := service.AllService.UserService.CurUser(c)
|
||||||
|
if ex.UserId != u.Id {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err := service.AllService.TagService.Delete(ex)
|
||||||
|
if err == nil {
|
||||||
|
response.Success(c, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Fail(c, 101, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -1,14 +1,14 @@
|
|||||||
package admin
|
package admin
|
||||||
|
|
||||||
import (
|
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"
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/global"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
|
||||||
|
adminReq "github.com/lejianwen/rustdesk-api/v2/http/request/admin"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/http/response"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/service"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Oauth struct {
|
type Oauth struct {
|
||||||
@@ -18,12 +18,12 @@ type Oauth struct {
|
|||||||
func (o *Oauth) Info(c *gin.Context) {
|
func (o *Oauth) Info(c *gin.Context) {
|
||||||
code := c.Query("code")
|
code := c.Query("code")
|
||||||
if code == "" {
|
if code == "" {
|
||||||
response.Fail(c, 101, "参数错误")
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
v := service.AllService.OauthService.GetOauthCache(code)
|
v := service.AllService.OauthService.GetOauthCache(code)
|
||||||
if v == nil {
|
if v == nil {
|
||||||
response.Fail(c, 101, "信息不存在")
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
response.Success(c, v)
|
response.Success(c, v)
|
||||||
@@ -33,31 +33,33 @@ func (o *Oauth) ToBind(c *gin.Context) {
|
|||||||
f := &adminReq.BindOauthForm{}
|
f := &adminReq.BindOauthForm{}
|
||||||
err := c.ShouldBindJSON(f)
|
err := c.ShouldBindJSON(f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.Fail(c, 101, "参数错误")
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
u := service.AllService.UserService.CurUser(c)
|
u := service.AllService.UserService.CurUser(c)
|
||||||
|
|
||||||
utr := service.AllService.UserService.UserThirdInfo(u.Id, f.Op)
|
utr := service.AllService.UserService.UserThirdInfo(u.Id, f.Op)
|
||||||
if utr.Id > 0 {
|
if utr.Id > 0 {
|
||||||
response.Fail(c, 101, "已绑定过了")
|
response.Fail(c, 101, response.TranslateMsg(c, "OauthHasBindOtherUser"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err, code, url := service.AllService.OauthService.BeginAuth(f.Op)
|
err, state, verifier, nonce, url := service.AllService.OauthService.BeginAuth(f.Op)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.Error(c, err.Error())
|
response.Error(c, response.TranslateMsg(c, err.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
service.AllService.OauthService.SetOauthCache(code, &service.OauthCacheItem{
|
service.AllService.OauthService.SetOauthCache(state, &service.OauthCacheItem{
|
||||||
Action: service.OauthActionTypeBind,
|
Action: service.OauthActionTypeBind,
|
||||||
Op: f.Op,
|
Op: f.Op,
|
||||||
UserId: u.Id,
|
UserId: u.Id,
|
||||||
|
Verifier: verifier,
|
||||||
|
Nonce: nonce,
|
||||||
}, 5*60)
|
}, 5*60)
|
||||||
|
|
||||||
response.Success(c, gin.H{
|
response.Success(c, gin.H{
|
||||||
"code": code,
|
"code": state,
|
||||||
"url": url,
|
"url": url,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -67,16 +69,16 @@ func (o *Oauth) Confirm(c *gin.Context) {
|
|||||||
j := &adminReq.OauthConfirmForm{}
|
j := &adminReq.OauthConfirmForm{}
|
||||||
err := c.ShouldBindJSON(j)
|
err := c.ShouldBindJSON(j)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.Fail(c, 101, "参数错误"+err.Error())
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if j.Code == "" {
|
if j.Code == "" {
|
||||||
response.Fail(c, 101, "参数错误: code 不存在")
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
v := service.AllService.OauthService.GetOauthCache(j.Code)
|
v := service.AllService.OauthService.GetOauthCache(j.Code)
|
||||||
if v == nil {
|
if v == nil {
|
||||||
response.Fail(c, 101, "授权已过期")
|
response.Fail(c, 101, response.TranslateMsg(c, "OauthExpired"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
u := service.AllService.UserService.CurUser(c)
|
u := service.AllService.UserService.CurUser(c)
|
||||||
@@ -89,49 +91,49 @@ func (o *Oauth) BindConfirm(c *gin.Context) {
|
|||||||
j := &adminReq.OauthConfirmForm{}
|
j := &adminReq.OauthConfirmForm{}
|
||||||
err := c.ShouldBindJSON(j)
|
err := c.ShouldBindJSON(j)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.Fail(c, 101, "参数错误"+err.Error())
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if j.Code == "" {
|
if j.Code == "" {
|
||||||
response.Fail(c, 101, "参数错误: code 不存在")
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
v := service.AllService.OauthService.GetOauthCache(j.Code)
|
oauthService := service.AllService.OauthService
|
||||||
if v == nil {
|
oauthCache := oauthService.GetOauthCache(j.Code)
|
||||||
response.Fail(c, 101, "授权已过期")
|
if oauthCache == nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "OauthExpired"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
u := service.AllService.UserService.CurUser(c)
|
oauthUser := oauthCache.ToOauthUser()
|
||||||
err = service.AllService.OauthService.BindGithubUser(v.ThirdOpenId, v.ThirdOpenId, u.Id)
|
user := service.AllService.UserService.CurUser(c)
|
||||||
|
err = oauthService.BindOauthUser(user.Id, oauthUser, oauthCache.Op)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.Fail(c, 101, "绑定失败,请重试")
|
response.Fail(c, 101, response.TranslateMsg(c, "BindFail"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
v.UserId = u.Id
|
oauthCache.UserId = user.Id
|
||||||
service.AllService.OauthService.SetOauthCache(j.Code, v, 0)
|
oauthService.SetOauthCache(j.Code, oauthCache, 0)
|
||||||
response.Success(c, v)
|
response.Success(c, oauthCache)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *Oauth) Unbind(c *gin.Context) {
|
func (o *Oauth) Unbind(c *gin.Context) {
|
||||||
f := &adminReq.UnBindOauthForm{}
|
f := &adminReq.UnBindOauthForm{}
|
||||||
err := c.ShouldBindJSON(f)
|
err := c.ShouldBindJSON(f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.Fail(c, 101, "参数错误")
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
u := service.AllService.UserService.CurUser(c)
|
u := service.AllService.UserService.CurUser(c)
|
||||||
utr := service.AllService.UserService.UserThirdInfo(u.Id, f.Op)
|
utr := service.AllService.UserService.UserThirdInfo(u.Id, f.Op)
|
||||||
if utr.Id == 0 {
|
if utr.Id == 0 {
|
||||||
response.Fail(c, 101, "未绑定")
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if f.Op == model.OauthTypeGithub {
|
err = service.AllService.OauthService.UnBindOauthUser(u.Id, f.Op)
|
||||||
err = service.AllService.OauthService.UnBindGithubUser(u.Id)
|
if err != nil {
|
||||||
if err != nil {
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
response.Fail(c, 101, "解绑失败")
|
return
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
response.Success(c, nil)
|
response.Success(c, nil)
|
||||||
}
|
}
|
||||||
@@ -155,7 +157,7 @@ func (o *Oauth) Detail(c *gin.Context) {
|
|||||||
response.Success(c, u)
|
response.Success(c, u)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
response.Fail(c, 101, "信息不存在")
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,28 +175,31 @@ func (o *Oauth) Detail(c *gin.Context) {
|
|||||||
func (o *Oauth) Create(c *gin.Context) {
|
func (o *Oauth) Create(c *gin.Context) {
|
||||||
f := &admin.OauthForm{}
|
f := &admin.OauthForm{}
|
||||||
if err := c.ShouldBindJSON(f); err != nil {
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
response.Fail(c, 101, "参数错误"+err.Error())
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
errList := global.Validator.ValidStruct(f)
|
errList := global.Validator.ValidStruct(c, f)
|
||||||
if len(errList) > 0 {
|
if len(errList) > 0 {
|
||||||
response.Fail(c, 101, errList[0])
|
response.Fail(c, 101, errList[0])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ex := service.AllService.OauthService.InfoByOp(f.Op)
|
|
||||||
if ex.Id > 0 {
|
|
||||||
response.Fail(c, 101, "已存在"+f.Op)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
u := f.ToOauth()
|
u := f.ToOauth()
|
||||||
err := service.AllService.OauthService.Create(u)
|
err := u.FormatOauthInfo()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.Fail(c, 101, "创建失败")
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
response.Success(c, u)
|
ex := service.AllService.OauthService.InfoByOp(u.Op)
|
||||||
|
if ex.Id > 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemExists"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
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 列表
|
// List 列表
|
||||||
@@ -212,7 +217,7 @@ func (o *Oauth) Create(c *gin.Context) {
|
|||||||
func (o *Oauth) List(c *gin.Context) {
|
func (o *Oauth) List(c *gin.Context) {
|
||||||
query := &admin.PageQuery{}
|
query := &admin.PageQuery{}
|
||||||
if err := c.ShouldBindQuery(query); err != nil {
|
if err := c.ShouldBindQuery(query); err != nil {
|
||||||
response.Fail(c, 101, "参数错误")
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
res := service.AllService.OauthService.List(query.Page, query.PageSize, nil)
|
res := service.AllService.OauthService.List(query.Page, query.PageSize, nil)
|
||||||
@@ -233,14 +238,14 @@ func (o *Oauth) List(c *gin.Context) {
|
|||||||
func (o *Oauth) Update(c *gin.Context) {
|
func (o *Oauth) Update(c *gin.Context) {
|
||||||
f := &admin.OauthForm{}
|
f := &admin.OauthForm{}
|
||||||
if err := c.ShouldBindJSON(f); err != nil {
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
response.Fail(c, 101, "参数错误")
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if f.Id == 0 {
|
if f.Id == 0 {
|
||||||
response.Fail(c, 101, "参数错误")
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
errList := global.Validator.ValidStruct(f)
|
errList := global.Validator.ValidStruct(c, f)
|
||||||
if len(errList) > 0 {
|
if len(errList) > 0 {
|
||||||
response.Fail(c, 101, errList[0])
|
response.Fail(c, 101, errList[0])
|
||||||
return
|
return
|
||||||
@@ -248,7 +253,7 @@ func (o *Oauth) Update(c *gin.Context) {
|
|||||||
u := f.ToOauth()
|
u := f.ToOauth()
|
||||||
err := service.AllService.OauthService.Update(u)
|
err := service.AllService.OauthService.Update(u)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.Fail(c, 101, "更新失败")
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
response.Success(c, nil)
|
response.Success(c, nil)
|
||||||
@@ -268,11 +273,11 @@ func (o *Oauth) Update(c *gin.Context) {
|
|||||||
func (o *Oauth) Delete(c *gin.Context) {
|
func (o *Oauth) Delete(c *gin.Context) {
|
||||||
f := &admin.OauthForm{}
|
f := &admin.OauthForm{}
|
||||||
if err := c.ShouldBindJSON(f); err != nil {
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
response.Fail(c, 101, "系统错误")
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
id := f.Id
|
id := f.Id
|
||||||
errList := global.Validator.ValidVar(id, "required,gt=0")
|
errList := global.Validator.ValidVar(c, id, "required,gt=0")
|
||||||
if len(errList) > 0 {
|
if len(errList) > 0 {
|
||||||
response.Fail(c, 101, errList[0])
|
response.Fail(c, 101, errList[0])
|
||||||
return
|
return
|
||||||
@@ -287,5 +292,5 @@ func (o *Oauth) Delete(c *gin.Context) {
|
|||||||
response.Fail(c, 101, err.Error())
|
response.Fail(c, 101, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
response.Fail(c, 101, "信息不存在")
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,23 @@
|
|||||||
package admin
|
package admin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"Gwen/global"
|
|
||||||
"Gwen/http/request/admin"
|
|
||||||
"Gwen/http/response"
|
|
||||||
"Gwen/service"
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/global"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/http/response"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/service"
|
||||||
|
"gorm.io/gorm"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Peer struct {
|
type Peer struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Detail 机器
|
// Detail 设备
|
||||||
// @Tags 机器
|
// @Tags 设备
|
||||||
// @Summary 机器详情
|
// @Summary 设备详情
|
||||||
// @Description 机器详情
|
// @Description 设备详情
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param id path int true "ID"
|
// @Param id path int true "ID"
|
||||||
@@ -31,17 +33,17 @@ func (ct *Peer) Detail(c *gin.Context) {
|
|||||||
response.Success(c, u)
|
response.Success(c, u)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
response.Fail(c, 101, "信息不存在")
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create 创建机器
|
// Create 创建设备
|
||||||
// @Tags 机器
|
// @Tags 设备
|
||||||
// @Summary 创建机器
|
// @Summary 创建设备
|
||||||
// @Description 创建机器
|
// @Description 创建设备
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param body body admin.PeerForm true "机器信息"
|
// @Param body body admin.PeerForm true "设备信息"
|
||||||
// @Success 200 {object} response.Response{data=model.Peer}
|
// @Success 200 {object} response.Response{data=model.Peer}
|
||||||
// @Failure 500 {object} response.Response
|
// @Failure 500 {object} response.Response
|
||||||
// @Router /admin/peer/create [post]
|
// @Router /admin/peer/create [post]
|
||||||
@@ -49,52 +51,83 @@ func (ct *Peer) Detail(c *gin.Context) {
|
|||||||
func (ct *Peer) Create(c *gin.Context) {
|
func (ct *Peer) Create(c *gin.Context) {
|
||||||
f := &admin.PeerForm{}
|
f := &admin.PeerForm{}
|
||||||
if err := c.ShouldBindJSON(f); err != nil {
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
response.Fail(c, 101, "参数错误")
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
errList := global.Validator.ValidStruct(f)
|
errList := global.Validator.ValidStruct(c, f)
|
||||||
if len(errList) > 0 {
|
if len(errList) > 0 {
|
||||||
response.Fail(c, 101, errList[0])
|
response.Fail(c, 101, errList[0])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
u := f.ToPeer()
|
p := f.ToPeer()
|
||||||
err := service.AllService.PeerService.Create(u)
|
err := service.AllService.PeerService.Create(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.Fail(c, 101, "创建失败")
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
response.Success(c, u)
|
response.Success(c, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// List 列表
|
// List 列表
|
||||||
// @Tags 机器
|
// @Tags 设备
|
||||||
// @Summary 机器列表
|
// @Summary 设备列表
|
||||||
// @Description 机器列表
|
// @Description 设备列表
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param page query int false "页码"
|
// @Param page query int false "页码"
|
||||||
// @Param page_size query int false "页大小"
|
// @Param page_size query int false "页大小"
|
||||||
|
// @Param time_ago query int false "时间"
|
||||||
|
// @Param id query string false "ID"
|
||||||
|
// @Param hostname query string false "主机名"
|
||||||
|
// @Param uuids query string false "uuids 用逗号分隔"
|
||||||
// @Success 200 {object} response.Response{data=model.PeerList}
|
// @Success 200 {object} response.Response{data=model.PeerList}
|
||||||
// @Failure 500 {object} response.Response
|
// @Failure 500 {object} response.Response
|
||||||
// @Router /admin/peer/list [get]
|
// @Router /admin/peer/list [get]
|
||||||
// @Security token
|
// @Security token
|
||||||
func (ct *Peer) List(c *gin.Context) {
|
func (ct *Peer) List(c *gin.Context) {
|
||||||
query := &admin.PageQuery{}
|
query := &admin.PeerQuery{}
|
||||||
if err := c.ShouldBindQuery(query); err != nil {
|
if err := c.ShouldBindQuery(query); err != nil {
|
||||||
response.Fail(c, 101, "参数错误")
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
return
|
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+"%")
|
||||||
|
}
|
||||||
|
if query.Uuids != "" {
|
||||||
|
tx.Where("uuid in (?)", query.Uuids)
|
||||||
|
}
|
||||||
|
if query.Username != "" {
|
||||||
|
tx.Where("username like ?", "%"+query.Username+"%")
|
||||||
|
}
|
||||||
|
if query.Ip != "" {
|
||||||
|
tx.Where("last_online_ip like ?", "%"+query.Ip+"%")
|
||||||
|
}
|
||||||
|
if query.Alias != "" {
|
||||||
|
tx.Where("alias like ?", "%"+query.Alias+"%")
|
||||||
|
}
|
||||||
|
})
|
||||||
response.Success(c, res)
|
response.Success(c, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update 编辑
|
// Update 编辑
|
||||||
// @Tags 机器
|
// @Tags 设备
|
||||||
// @Summary 机器编辑
|
// @Summary 设备编辑
|
||||||
// @Description 机器编辑
|
// @Description 设备编辑
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param body body admin.PeerForm true "机器信息"
|
// @Param body body admin.PeerForm true "设备信息"
|
||||||
// @Success 200 {object} response.Response{data=model.Peer}
|
// @Success 200 {object} response.Response{data=model.Peer}
|
||||||
// @Failure 500 {object} response.Response
|
// @Failure 500 {object} response.Response
|
||||||
// @Router /admin/peer/update [post]
|
// @Router /admin/peer/update [post]
|
||||||
@@ -102,14 +135,14 @@ func (ct *Peer) List(c *gin.Context) {
|
|||||||
func (ct *Peer) Update(c *gin.Context) {
|
func (ct *Peer) Update(c *gin.Context) {
|
||||||
f := &admin.PeerForm{}
|
f := &admin.PeerForm{}
|
||||||
if err := c.ShouldBindJSON(f); err != nil {
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
response.Fail(c, 101, "参数错误")
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if f.RowId == 0 {
|
if f.RowId == 0 {
|
||||||
response.Fail(c, 101, "参数错误")
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
errList := global.Validator.ValidStruct(f)
|
errList := global.Validator.ValidStruct(c, f)
|
||||||
if len(errList) > 0 {
|
if len(errList) > 0 {
|
||||||
response.Fail(c, 101, errList[0])
|
response.Fail(c, 101, errList[0])
|
||||||
return
|
return
|
||||||
@@ -117,19 +150,19 @@ func (ct *Peer) Update(c *gin.Context) {
|
|||||||
u := f.ToPeer()
|
u := f.ToPeer()
|
||||||
err := service.AllService.PeerService.Update(u)
|
err := service.AllService.PeerService.Update(u)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.Fail(c, 101, "更新失败")
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
response.Success(c, nil)
|
response.Success(c, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete 删除
|
// Delete 删除
|
||||||
// @Tags 机器
|
// @Tags 设备
|
||||||
// @Summary 机器删除
|
// @Summary 设备删除
|
||||||
// @Description 机器删除
|
// @Description 设备删除
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param body body admin.PeerForm true "机器信息"
|
// @Param body body admin.PeerForm true "设备信息"
|
||||||
// @Success 200 {object} response.Response
|
// @Success 200 {object} response.Response
|
||||||
// @Failure 500 {object} response.Response
|
// @Failure 500 {object} response.Response
|
||||||
// @Router /admin/peer/delete [post]
|
// @Router /admin/peer/delete [post]
|
||||||
@@ -137,11 +170,11 @@ func (ct *Peer) Update(c *gin.Context) {
|
|||||||
func (ct *Peer) Delete(c *gin.Context) {
|
func (ct *Peer) Delete(c *gin.Context) {
|
||||||
f := &admin.PeerForm{}
|
f := &admin.PeerForm{}
|
||||||
if err := c.ShouldBindJSON(f); err != nil {
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
response.Fail(c, 101, "系统错误")
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
id := f.RowId
|
id := f.RowId
|
||||||
errList := global.Validator.ValidVar(id, "required,gt=0")
|
errList := global.Validator.ValidVar(c, id, "required,gt=0")
|
||||||
if len(errList) > 0 {
|
if len(errList) > 0 {
|
||||||
response.Fail(c, 101, errList[0])
|
response.Fail(c, 101, errList[0])
|
||||||
return
|
return
|
||||||
@@ -153,8 +186,55 @@ func (ct *Peer) Delete(c *gin.Context) {
|
|||||||
response.Success(c, nil)
|
response.Success(c, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
response.Fail(c, 101, err.Error())
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
return
|
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/batchDelete [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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,30 +1,137 @@
|
|||||||
package admin
|
package admin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"Gwen/global"
|
|
||||||
"Gwen/http/response"
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/global"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/http/response"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/model"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/service"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Rustdesk struct {
|
type Rustdesk struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServerConfig 服务配置
|
type RustdeskCmd struct {
|
||||||
// @Tags ADMIN
|
Cmd string `json:"cmd"`
|
||||||
// @Summary 服务配置
|
Option string `json:"option"`
|
||||||
// @Description 服务配置,给webclient提供api-server
|
Target string `json:"target"`
|
||||||
// @Accept json
|
}
|
||||||
// @Produce json
|
|
||||||
// @Success 200 {object} response.Response
|
func (r *Rustdesk) CmdList(c *gin.Context) {
|
||||||
// @Failure 500 {object} response.Response
|
q := &admin.PageQuery{}
|
||||||
// @Router /admin/server-config [get]
|
if err := c.ShouldBindQuery(q); err != nil {
|
||||||
// @Security token
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
func (r *Rustdesk) ServerConfig(c *gin.Context) {
|
return
|
||||||
cf := &response.ServerConfigResponse{
|
}
|
||||||
IdServer: global.Config.Rustdesk.IdServer,
|
res := service.AllService.ServerCmdService.List(q.Page, 9999)
|
||||||
Key: global.Config.Rustdesk.Key,
|
//在列表前添加系统命令
|
||||||
RelayServer: global.Config.Rustdesk.RelayServer,
|
list := make([]*model.ServerCmd, 0)
|
||||||
ApiServer: global.Config.Rustdesk.ApiServer,
|
list = append(list, model.SysIdServerCmds...)
|
||||||
}
|
list = append(list, model.SysRelayServerCmds...)
|
||||||
response.Success(c, cf)
|
list = append(list, res.ServerCmds...)
|
||||||
|
res.ServerCmds = list
|
||||||
|
response.Success(c, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Rustdesk) CmdDelete(c *gin.Context) {
|
||||||
|
f := &model.ServerCmd{}
|
||||||
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if f.Id == 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ex := service.AllService.ServerCmdService.Info(f.Id)
|
||||||
|
if ex.Id == 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err := service.AllService.ServerCmdService.Delete(ex)
|
||||||
|
if err != nil {
|
||||||
|
response.Fail(c, 101, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Success(c, nil)
|
||||||
|
}
|
||||||
|
func (r *Rustdesk) CmdCreate(c *gin.Context) {
|
||||||
|
f := &model.ServerCmd{}
|
||||||
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
errList := global.Validator.ValidStruct(c, f)
|
||||||
|
if len(errList) > 0 {
|
||||||
|
response.Fail(c, 101, errList[0])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err := service.AllService.ServerCmdService.Create(f)
|
||||||
|
if err != nil {
|
||||||
|
response.Fail(c, 101, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Success(c, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Rustdesk) CmdUpdate(c *gin.Context) {
|
||||||
|
f := &model.ServerCmd{}
|
||||||
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
errList := global.Validator.ValidStruct(c, f)
|
||||||
|
if len(errList) > 0 {
|
||||||
|
response.Fail(c, 101, errList[0])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ex := service.AllService.ServerCmdService.Info(f.Id)
|
||||||
|
if ex.Id == 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err := service.AllService.ServerCmdService.Update(f)
|
||||||
|
if err != nil {
|
||||||
|
response.Fail(c, 101, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Success(c, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Rustdesk) SendCmd(c *gin.Context) {
|
||||||
|
rc := &RustdeskCmd{}
|
||||||
|
if err := c.ShouldBindJSON(rc); err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if rc.Cmd == "" {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if rc.Target == "" {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if rc.Target != model.ServerCmdTargetIdServer && rc.Target != model.ServerCmdTargetRelayServer {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
port := 0
|
||||||
|
switch rc.Target {
|
||||||
|
case model.ServerCmdTargetIdServer:
|
||||||
|
port = global.Config.Admin.IdServerPort - 1
|
||||||
|
case model.ServerCmdTargetRelayServer:
|
||||||
|
port = global.Config.Admin.RelayServerPort
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := service.AllService.ServerCmdService.SendCmd(port, rc.Cmd, rc.Option)
|
||||||
|
if err != nil {
|
||||||
|
response.Fail(c, 101, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Success(c, res)
|
||||||
}
|
}
|
||||||
|
|||||||
105
http/controller/admin/shareRecord.go
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
package admin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/global"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/http/response"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/service"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ShareRecord struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// List 列表
|
||||||
|
// @Tags 分享记录
|
||||||
|
// @Summary 分享记录列表
|
||||||
|
// @Description 分享记录列表
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param user_id query int false "用户ID"
|
||||||
|
// @Param page query int false "页码"
|
||||||
|
// @Param page_size query int false "页大小"
|
||||||
|
// @Success 200 {object} response.Response
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/share_record/list [get]
|
||||||
|
// @Security token
|
||||||
|
func (sr *ShareRecord) List(c *gin.Context) {
|
||||||
|
query := &admin.ShareRecordQuery{}
|
||||||
|
if err := c.ShouldBindQuery(query); err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
res := service.AllService.ShareRecordService.List(query.Page, query.PageSize, func(tx *gorm.DB) {
|
||||||
|
if query.UserId > 0 {
|
||||||
|
tx.Where("user_id = ?", query.UserId)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
response.Success(c, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete 删除
|
||||||
|
// @Tags 分享记录
|
||||||
|
// @Summary 分享记录删除
|
||||||
|
// @Description 分享记录删除
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param body body admin.ShareRecordForm true "分享记录信息"
|
||||||
|
// @Success 200 {object} response.Response
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/share_record/delete [post]
|
||||||
|
// @Security token
|
||||||
|
func (sr *ShareRecord) Delete(c *gin.Context) {
|
||||||
|
f := &admin.ShareRecordForm{}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
i := service.AllService.ShareRecordService.InfoById(f.Id)
|
||||||
|
if i.Id > 0 {
|
||||||
|
err := service.AllService.ShareRecordService.Delete(i)
|
||||||
|
if err == nil {
|
||||||
|
response.Success(c, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// BatchDelete 批量删除
|
||||||
|
// @Tags 分享记录
|
||||||
|
// @Summary 批量分享记录
|
||||||
|
// @Description 批量分享记录
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param body body admin.PeerShareRecordBatchDeleteForm true "id"
|
||||||
|
// @Success 200 {object} response.Response
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/share_record/batchDelete [post]
|
||||||
|
// @Security token
|
||||||
|
func (sr *ShareRecord) BatchDelete(c *gin.Context) {
|
||||||
|
f := &admin.PeerShareRecordBatchDeleteForm{}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
err := service.AllService.ShareRecordService.BatchDelete(f.Ids)
|
||||||
|
if err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Success(c, nil)
|
||||||
|
}
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
package admin
|
package admin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"Gwen/global"
|
|
||||||
"Gwen/http/request/admin"
|
|
||||||
"Gwen/http/response"
|
|
||||||
"Gwen/service"
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/global"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/http/response"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/service"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
@@ -30,14 +30,14 @@ func (ct *Tag) Detail(c *gin.Context) {
|
|||||||
t := service.AllService.TagService.InfoById(uint(iid))
|
t := service.AllService.TagService.InfoById(uint(iid))
|
||||||
u := service.AllService.UserService.CurUser(c)
|
u := service.AllService.UserService.CurUser(c)
|
||||||
if !service.AllService.UserService.IsAdmin(u) && t.UserId != u.Id {
|
if !service.AllService.UserService.IsAdmin(u) && t.UserId != u.Id {
|
||||||
response.Fail(c, 101, "无权限")
|
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if t.Id > 0 {
|
if t.Id > 0 {
|
||||||
response.Success(c, t)
|
response.Success(c, t)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
response.Fail(c, 101, "信息不存在")
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,25 +55,25 @@ func (ct *Tag) Detail(c *gin.Context) {
|
|||||||
func (ct *Tag) Create(c *gin.Context) {
|
func (ct *Tag) Create(c *gin.Context) {
|
||||||
f := &admin.TagForm{}
|
f := &admin.TagForm{}
|
||||||
if err := c.ShouldBindJSON(f); err != nil {
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
response.Fail(c, 101, "参数错误")
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
errList := global.Validator.ValidStruct(f)
|
errList := global.Validator.ValidStruct(c, f)
|
||||||
if len(errList) > 0 {
|
if len(errList) > 0 {
|
||||||
response.Fail(c, 101, errList[0])
|
response.Fail(c, 101, errList[0])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
t := f.ToTag()
|
t := f.ToTag()
|
||||||
u := service.AllService.UserService.CurUser(c)
|
if t.UserId == 0 {
|
||||||
if !service.AllService.UserService.IsAdmin(u) || t.UserId == 0 {
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||||
t.UserId = u.Id
|
return
|
||||||
}
|
}
|
||||||
err := service.AllService.TagService.Create(t)
|
err := service.AllService.TagService.Create(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.Fail(c, 101, "创建失败")
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
response.Success(c, u)
|
response.Success(c, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// List 列表
|
// List 列表
|
||||||
@@ -93,17 +93,19 @@ func (ct *Tag) Create(c *gin.Context) {
|
|||||||
func (ct *Tag) List(c *gin.Context) {
|
func (ct *Tag) List(c *gin.Context) {
|
||||||
query := &admin.TagQuery{}
|
query := &admin.TagQuery{}
|
||||||
if err := c.ShouldBindQuery(query); err != nil {
|
if err := c.ShouldBindQuery(query); err != nil {
|
||||||
response.Fail(c, 101, "参数错误")
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
u := service.AllService.UserService.CurUser(c)
|
|
||||||
if !service.AllService.UserService.IsAdmin(u) || query.IsMy == 1 {
|
|
||||||
query.UserId = int(u.Id)
|
|
||||||
}
|
|
||||||
res := service.AllService.TagService.List(query.Page, query.PageSize, func(tx *gorm.DB) {
|
res := service.AllService.TagService.List(query.Page, query.PageSize, func(tx *gorm.DB) {
|
||||||
|
tx.Preload("Collection", func(txc *gorm.DB) *gorm.DB {
|
||||||
|
return txc.Select("id,name")
|
||||||
|
})
|
||||||
if query.UserId > 0 {
|
if query.UserId > 0 {
|
||||||
tx.Where("user_id = ?", query.UserId)
|
tx.Where("user_id = ?", query.UserId)
|
||||||
}
|
}
|
||||||
|
if query.CollectionId != nil && *query.CollectionId >= 0 {
|
||||||
|
tx.Where("collection_id = ?", query.CollectionId)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
response.Success(c, res)
|
response.Success(c, res)
|
||||||
}
|
}
|
||||||
@@ -122,27 +124,27 @@ func (ct *Tag) List(c *gin.Context) {
|
|||||||
func (ct *Tag) Update(c *gin.Context) {
|
func (ct *Tag) Update(c *gin.Context) {
|
||||||
f := &admin.TagForm{}
|
f := &admin.TagForm{}
|
||||||
if err := c.ShouldBindJSON(f); err != nil {
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
response.Fail(c, 101, "参数错误")
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
errList := global.Validator.ValidStruct(f)
|
errList := global.Validator.ValidStruct(c, f)
|
||||||
if len(errList) > 0 {
|
if len(errList) > 0 {
|
||||||
response.Fail(c, 101, errList[0])
|
response.Fail(c, 101, errList[0])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if f.Id == 0 {
|
if f.Id == 0 {
|
||||||
response.Fail(c, 101, "参数错误")
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ex := service.AllService.TagService.InfoById(f.Id)
|
||||||
|
if ex.Id == 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
t := f.ToTag()
|
t := f.ToTag()
|
||||||
u := service.AllService.UserService.CurUser(c)
|
|
||||||
if !service.AllService.UserService.IsAdmin(u) && t.UserId != u.Id {
|
|
||||||
response.Fail(c, 101, "无权限")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err := service.AllService.TagService.Update(t)
|
err := service.AllService.TagService.Update(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.Fail(c, 101, "更新失败")
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
response.Success(c, nil)
|
response.Success(c, nil)
|
||||||
@@ -162,29 +164,24 @@ func (ct *Tag) Update(c *gin.Context) {
|
|||||||
func (ct *Tag) Delete(c *gin.Context) {
|
func (ct *Tag) Delete(c *gin.Context) {
|
||||||
f := &admin.TagForm{}
|
f := &admin.TagForm{}
|
||||||
if err := c.ShouldBindJSON(f); err != nil {
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
response.Fail(c, 101, "系统错误")
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
id := f.Id
|
id := f.Id
|
||||||
errList := global.Validator.ValidVar(id, "required,gt=0")
|
errList := global.Validator.ValidVar(c, id, "required,gt=0")
|
||||||
if len(errList) > 0 {
|
if len(errList) > 0 {
|
||||||
response.Fail(c, 101, errList[0])
|
response.Fail(c, 101, errList[0])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
t := service.AllService.TagService.InfoById(f.Id)
|
ex := service.AllService.TagService.InfoById(f.Id)
|
||||||
u := service.AllService.UserService.CurUser(c)
|
if ex.Id == 0 {
|
||||||
if !service.AllService.UserService.IsAdmin(u) && t.UserId != u.Id {
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
response.Fail(c, 101, "无权限")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if u.Id > 0 {
|
err := service.AllService.TagService.Delete(ex)
|
||||||
err := service.AllService.TagService.Delete(t)
|
if err == nil {
|
||||||
if err == nil {
|
response.Success(c, nil)
|
||||||
response.Success(c, nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
response.Fail(c, 101, err.Error())
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
response.Fail(c, 101, "信息不存在")
|
response.Fail(c, 101, err.Error())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
package admin
|
package admin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"Gwen/global"
|
|
||||||
"Gwen/http/request/admin"
|
|
||||||
"Gwen/http/response"
|
|
||||||
adResp "Gwen/http/response/admin"
|
|
||||||
"Gwen/service"
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/global"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/http/response"
|
||||||
|
adResp "github.com/lejianwen/rustdesk-api/v2/http/response/admin"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/model"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/service"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/utils"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
@@ -33,7 +35,7 @@ func (ct *User) Detail(c *gin.Context) {
|
|||||||
response.Success(c, u)
|
response.Success(c, u)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
response.Fail(c, 101, "信息不存在")
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,10 +53,10 @@ func (ct *User) Detail(c *gin.Context) {
|
|||||||
func (ct *User) Create(c *gin.Context) {
|
func (ct *User) Create(c *gin.Context) {
|
||||||
f := &admin.UserForm{}
|
f := &admin.UserForm{}
|
||||||
if err := c.ShouldBindJSON(f); err != nil {
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
response.Fail(c, 101, "参数错误")
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
errList := global.Validator.ValidStruct(f)
|
errList := global.Validator.ValidStruct(c, f)
|
||||||
if len(errList) > 0 {
|
if len(errList) > 0 {
|
||||||
response.Fail(c, 101, errList[0])
|
response.Fail(c, 101, errList[0])
|
||||||
return
|
return
|
||||||
@@ -62,10 +64,10 @@ func (ct *User) Create(c *gin.Context) {
|
|||||||
u := f.ToUser()
|
u := f.ToUser()
|
||||||
err := service.AllService.UserService.Create(u)
|
err := service.AllService.UserService.Create(u)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.Fail(c, 101, "创建失败")
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
response.Success(c, u)
|
response.Success(c, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// List 列表
|
// List 列表
|
||||||
@@ -84,7 +86,7 @@ func (ct *User) Create(c *gin.Context) {
|
|||||||
func (ct *User) List(c *gin.Context) {
|
func (ct *User) List(c *gin.Context) {
|
||||||
query := &admin.UserQuery{}
|
query := &admin.UserQuery{}
|
||||||
if err := c.ShouldBindQuery(query); err != nil {
|
if err := c.ShouldBindQuery(query); err != nil {
|
||||||
response.Fail(c, 101, "参数错误")
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
res := service.AllService.UserService.List(query.Page, query.PageSize, func(tx *gorm.DB) {
|
res := service.AllService.UserService.List(query.Page, query.PageSize, func(tx *gorm.DB) {
|
||||||
@@ -109,14 +111,14 @@ func (ct *User) List(c *gin.Context) {
|
|||||||
func (ct *User) Update(c *gin.Context) {
|
func (ct *User) Update(c *gin.Context) {
|
||||||
f := &admin.UserForm{}
|
f := &admin.UserForm{}
|
||||||
if err := c.ShouldBindJSON(f); err != nil {
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
response.Fail(c, 101, "参数错误:"+err.Error())
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if f.Id == 0 {
|
if f.Id == 0 {
|
||||||
response.Fail(c, 101, "参数错误")
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
errList := global.Validator.ValidStruct(f)
|
errList := global.Validator.ValidStruct(c, f)
|
||||||
if len(errList) > 0 {
|
if len(errList) > 0 {
|
||||||
response.Fail(c, 101, errList[0])
|
response.Fail(c, 101, errList[0])
|
||||||
return
|
return
|
||||||
@@ -124,7 +126,7 @@ func (ct *User) Update(c *gin.Context) {
|
|||||||
u := f.ToUser()
|
u := f.ToUser()
|
||||||
err := service.AllService.UserService.Update(u)
|
err := service.AllService.UserService.Update(u)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.Fail(c, 101, "更新失败")
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
response.Success(c, nil)
|
response.Success(c, nil)
|
||||||
@@ -144,11 +146,11 @@ func (ct *User) Update(c *gin.Context) {
|
|||||||
func (ct *User) Delete(c *gin.Context) {
|
func (ct *User) Delete(c *gin.Context) {
|
||||||
f := &admin.UserForm{}
|
f := &admin.UserForm{}
|
||||||
if err := c.ShouldBindJSON(f); err != nil {
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
response.Fail(c, 101, "系统错误")
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
id := f.Id
|
id := f.Id
|
||||||
errList := global.Validator.ValidVar(id, "required,gt=0")
|
errList := global.Validator.ValidVar(c, id, "required,gt=0")
|
||||||
if len(errList) > 0 {
|
if len(errList) > 0 {
|
||||||
response.Fail(c, 101, errList[0])
|
response.Fail(c, 101, errList[0])
|
||||||
return
|
return
|
||||||
@@ -163,7 +165,7 @@ func (ct *User) Delete(c *gin.Context) {
|
|||||||
response.Fail(c, 101, err.Error())
|
response.Fail(c, 101, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
response.Fail(c, 101, "信息不存在")
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdatePassword 修改密码
|
// UpdatePassword 修改密码
|
||||||
@@ -180,22 +182,22 @@ func (ct *User) Delete(c *gin.Context) {
|
|||||||
func (ct *User) UpdatePassword(c *gin.Context) {
|
func (ct *User) UpdatePassword(c *gin.Context) {
|
||||||
f := &admin.UserPasswordForm{}
|
f := &admin.UserPasswordForm{}
|
||||||
if err := c.ShouldBindJSON(f); err != nil {
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
response.Fail(c, 101, "参数错误")
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
errList := global.Validator.ValidStruct(f)
|
errList := global.Validator.ValidStruct(c, f)
|
||||||
if len(errList) > 0 {
|
if len(errList) > 0 {
|
||||||
response.Fail(c, 101, errList[0])
|
response.Fail(c, 101, errList[0])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
u := service.AllService.UserService.InfoById(f.Id)
|
u := service.AllService.UserService.InfoById(f.Id)
|
||||||
if u.Id == 0 {
|
if u.Id == 0 {
|
||||||
response.Fail(c, 101, "信息不存在")
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err := service.AllService.UserService.UpdatePassword(u, f.Password)
|
err := service.AllService.UserService.UpdatePassword(u, f.Password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.Fail(c, 101, "更新失败")
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
response.Success(c, nil)
|
response.Success(c, nil)
|
||||||
@@ -215,12 +217,7 @@ func (ct *User) Current(c *gin.Context) {
|
|||||||
u := service.AllService.UserService.CurUser(c)
|
u := service.AllService.UserService.CurUser(c)
|
||||||
token, _ := c.Get("token")
|
token, _ := c.Get("token")
|
||||||
t := token.(string)
|
t := token.(string)
|
||||||
response.Success(c, &adResp.LoginPayload{
|
responseLoginSuccess(c, u, t)
|
||||||
Token: t,
|
|
||||||
Username: u.Username,
|
|
||||||
RouteNames: service.AllService.UserService.RouteNames(u),
|
|
||||||
Nickname: u.Nickname,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChangeCurPwd 修改当前用户密码
|
// ChangeCurPwd 修改当前用户密码
|
||||||
@@ -237,24 +234,27 @@ func (ct *User) Current(c *gin.Context) {
|
|||||||
func (ct *User) ChangeCurPwd(c *gin.Context) {
|
func (ct *User) ChangeCurPwd(c *gin.Context) {
|
||||||
f := &admin.ChangeCurPasswordForm{}
|
f := &admin.ChangeCurPasswordForm{}
|
||||||
if err := c.ShouldBindJSON(f); err != nil {
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
response.Fail(c, 101, "参数错误")
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
errList := global.Validator.ValidStruct(f)
|
errList := global.Validator.ValidStruct(c, f)
|
||||||
if len(errList) > 0 {
|
if len(errList) > 0 {
|
||||||
response.Fail(c, 101, errList[0])
|
response.Fail(c, 101, errList[0])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
u := service.AllService.UserService.CurUser(c)
|
u := service.AllService.UserService.CurUser(c)
|
||||||
oldPwd := service.AllService.UserService.EncryptPassword(f.OldPassword)
|
// Verify the old password only when the account already has one set
|
||||||
if u.Password != oldPwd {
|
if !service.AllService.UserService.IsPasswordEmptyByUser(u) {
|
||||||
response.Fail(c, 101, "旧密码错误")
|
ok, _, err := utils.VerifyPassword(u.Password, f.OldPassword)
|
||||||
return
|
if err != nil || !ok {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "OldPasswordError"))
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
err := service.AllService.UserService.UpdatePassword(u, f.NewPassword)
|
err := service.AllService.UserService.UpdatePassword(u, f.NewPassword)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.Fail(c, 101, "更新失败")
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
response.Success(c, nil)
|
response.Success(c, nil)
|
||||||
@@ -281,10 +281,10 @@ func (ct *User) MyOauth(c *gin.Context) {
|
|||||||
var res []*adResp.UserOauthItem
|
var res []*adResp.UserOauthItem
|
||||||
for _, oa := range oal.Oauths {
|
for _, oa := range oal.Oauths {
|
||||||
item := &adResp.UserOauthItem{
|
item := &adResp.UserOauthItem{
|
||||||
ThirdType: oa.Op,
|
Op: oa.Op,
|
||||||
}
|
}
|
||||||
for _, ut := range uts {
|
for _, ut := range uts {
|
||||||
if ut.ThirdType == oa.Op {
|
if ut.Op == oa.Op {
|
||||||
item.Status = 1
|
item.Status = 1
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -293,3 +293,56 @@ func (ct *User) MyOauth(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
response.Success(c, res)
|
response.Success(c, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// groupUsers
|
||||||
|
func (ct *User) GroupUsers(c *gin.Context) {
|
||||||
|
aG := service.AllService.GroupService.List(1, 999, nil)
|
||||||
|
aU := service.AllService.UserService.List(1, 9999, nil)
|
||||||
|
response.Success(c, gin.H{
|
||||||
|
"groups": aG.Groups,
|
||||||
|
"users": aU.Users,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register
|
||||||
|
func (ct *User) Register(c *gin.Context) {
|
||||||
|
if !global.Config.App.Register {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "RegisterClosed"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
f := &admin.RegisterForm{}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
regStatus := model.StatusCode(global.Config.App.RegisterStatus)
|
||||||
|
// 注册状态可能未配置,默认启用
|
||||||
|
if regStatus != model.COMMON_STATUS_DISABLED && regStatus != model.COMMON_STATUS_ENABLE {
|
||||||
|
regStatus = model.COMMON_STATUS_ENABLE
|
||||||
|
}
|
||||||
|
|
||||||
|
u := service.AllService.UserService.Register(f.Username, f.Email, f.Password, regStatus)
|
||||||
|
if u == nil || u.Id == 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if regStatus == model.COMMON_STATUS_DISABLED {
|
||||||
|
// 需要管理员审核
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "RegisterSuccessWaitAdminConfirm"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 注册成功后自动登录
|
||||||
|
ut := service.AllService.UserService.Login(u, &model.LoginLog{
|
||||||
|
UserId: u.Id,
|
||||||
|
Client: model.LoginLogClientWebAdmin,
|
||||||
|
Uuid: "",
|
||||||
|
Ip: c.ClientIP(),
|
||||||
|
Type: model.LoginLogTypeAccount,
|
||||||
|
})
|
||||||
|
responseLoginSuccess(c, u, ut.Token)
|
||||||
|
}
|
||||||
|
|||||||
113
http/controller/admin/userToken.go
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
package admin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/global"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/http/response"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/model"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/service"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserToken struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// List 列表
|
||||||
|
// @Tags 登录凭证
|
||||||
|
// @Summary 登录凭证列表
|
||||||
|
// @Description 登录凭证列表
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param page query int false "页码"
|
||||||
|
// @Param page_size query int false "页大小"
|
||||||
|
// @Param user_id query int false "用户ID"
|
||||||
|
// @Success 200 {object} response.Response{data=model.UserTokenList}
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/user_token/list [get]
|
||||||
|
// @Security token
|
||||||
|
func (ct *UserToken) List(c *gin.Context) {
|
||||||
|
query := &admin.LoginTokenQuery{}
|
||||||
|
if err := c.ShouldBindQuery(query); err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
res := service.AllService.UserService.TokenList(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.UserToken true "登录凭证信息"
|
||||||
|
// @Success 200 {object} response.Response
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/user_token/delete [post]
|
||||||
|
// @Security token
|
||||||
|
func (ct *UserToken) Delete(c *gin.Context) {
|
||||||
|
f := &model.UserToken{}
|
||||||
|
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.UserService.TokenInfoById(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.UserService.DeleteToken(l)
|
||||||
|
if err == nil {
|
||||||
|
response.Success(c, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Fail(c, 101, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// BatchDelete 批量删除
|
||||||
|
// @Tags 登录凭证
|
||||||
|
// @Summary 登录凭证批量删除
|
||||||
|
// @Description 登录凭证批量删除
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param body body admin.UserTokenBatchDeleteForm true "登录凭证信息"
|
||||||
|
// @Success 200 {object} response.Response
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/user_token/batchDelete [post]
|
||||||
|
// @Security token
|
||||||
|
func (ct *UserToken) BatchDelete(c *gin.Context) {
|
||||||
|
f := &admin.UserTokenBatchDeleteForm{}
|
||||||
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ids := f.Ids
|
||||||
|
if len(ids) == 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err := service.AllService.UserService.BatchDeleteUserToken(ids)
|
||||||
|
if err == nil {
|
||||||
|
response.Success(c, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Fail(c, 101, err.Error())
|
||||||
|
}
|
||||||
@@ -1,14 +1,19 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
requstform "Gwen/http/request/api"
|
|
||||||
"Gwen/http/response"
|
|
||||||
"Gwen/http/response/api"
|
|
||||||
"Gwen/model"
|
|
||||||
"Gwen/service"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/global"
|
||||||
|
requstform "github.com/lejianwen/rustdesk-api/v2/http/request/api"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/http/response"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/http/response/api"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/model"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/service"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/utils"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Ab struct {
|
type Ab struct {
|
||||||
@@ -27,8 +32,8 @@ type Ab struct {
|
|||||||
func (a *Ab) Ab(c *gin.Context) {
|
func (a *Ab) Ab(c *gin.Context) {
|
||||||
user := service.AllService.UserService.CurUser(c)
|
user := service.AllService.UserService.CurUser(c)
|
||||||
|
|
||||||
al := service.AllService.AddressBookService.ListByUserId(user.Id, 1, 1000)
|
al := service.AllService.AddressBookService.ListByUserIdAndCollectionId(user.Id, 0, 1, 1000)
|
||||||
tags := service.AllService.TagService.ListByUserId(user.Id)
|
tags := service.AllService.TagService.ListByUserIdAndCollectionId(user.Id, 0)
|
||||||
|
|
||||||
tagColors := map[string]uint{}
|
tagColors := map[string]uint{}
|
||||||
//将tags中的name转成一个以逗号分割的字符串
|
//将tags中的name转成一个以逗号分割的字符串
|
||||||
@@ -65,82 +70,658 @@ func (a *Ab) UpAb(c *gin.Context) {
|
|||||||
abf := &requstform.AddressBookForm{}
|
abf := &requstform.AddressBookForm{}
|
||||||
err := c.ShouldBindJSON(&abf)
|
err := c.ShouldBindJSON(&abf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.Error(c, "参数错误")
|
response.Error(c, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
abd := &requstform.AddressBookFormData{}
|
abd := &requstform.AddressBookFormData{}
|
||||||
err = json.Unmarshal([]byte(abf.Data), abd)
|
err = json.Unmarshal([]byte(abf.Data), abd)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
//fmt.Println(abd)
|
|
||||||
//for _, peer := range abd.Peers {
|
|
||||||
// fmt.Println(peer)
|
|
||||||
//}
|
|
||||||
|
|
||||||
user := service.AllService.UserService.CurUser(c)
|
user := service.AllService.UserService.CurUser(c)
|
||||||
|
|
||||||
err = service.AllService.AddressBookService.UpdateAddressBook(abd.Peers, user.Id)
|
err = service.AllService.AddressBookService.UpdateAddressBook(abd.Peers, user.Id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Abort()
|
response.Error(c, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
tc := map[string]uint{}
|
service.AllService.TagService.UpdateTags(user.Id, tc)
|
||||||
err = json.Unmarshal([]byte(abd.TagColors), &tc)
|
|
||||||
if err != nil {
|
|
||||||
response.Error(c, "系统错误")
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
service.AllService.TagService.UpdateTags(user.Id, tc)
|
|
||||||
}
|
|
||||||
|
|
||||||
c.JSON(http.StatusOK, nil)
|
c.JSON(http.StatusOK, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tags
|
// PTags
|
||||||
// @Tags 地址
|
// @Tags 地址[Personal]
|
||||||
// @Summary 标签
|
// @Summary 标签
|
||||||
// @Description 标签
|
// @Description 标签
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Success 200 {object} []model.Tag
|
// @Param guid path string true "guid"
|
||||||
|
// @Success 200 {object} model.TagList
|
||||||
// @Failure 500 {object} response.ErrorResponse
|
// @Failure 500 {object} response.ErrorResponse
|
||||||
// @Router /tags [post]
|
// @Router /ab/tags/{guid} [post]
|
||||||
// @Security BearerAuth
|
// @Security BearerAuth
|
||||||
func (a *Ab) Tags(c *gin.Context) {
|
func (a *Ab) PTags(c *gin.Context) {
|
||||||
user := service.AllService.UserService.CurUser(c)
|
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
|
||||||
|
}
|
||||||
|
|
||||||
tags := service.AllService.TagService.ListByUserId(user.Id)
|
//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)
|
c.JSON(http.StatusOK, tags.Tags)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TagAdd
|
// TagAdd
|
||||||
// @Tags 地址
|
// @Tags 地址[Personal]
|
||||||
// @Summary 标签添加
|
// @Summary 标签添加
|
||||||
// @Description 标签
|
// @Description 标签
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
|
// @Param guid path string true "guid"
|
||||||
// @Success 200 {string} string
|
// @Success 200 {string} string
|
||||||
// @Failure 500 {object} response.ErrorResponse
|
// @Failure 500 {object} response.ErrorResponse
|
||||||
// @Router /ab/add [post]
|
// @Router /ab/tag/add/{guid} [post]
|
||||||
// @Security BearerAuth
|
// @Security BearerAuth
|
||||||
func (a *Ab) TagAdd(c *gin.Context) {
|
func (a *Ab) TagAdd(c *gin.Context) {
|
||||||
|
|
||||||
t := &model.Tag{}
|
t := &model.Tag{}
|
||||||
err := c.ShouldBindJSON(t)
|
err := c.ShouldBindJSON(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.Error(c, "参数错误")
|
response.Error(c, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
return
|
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)
|
ids := utils.Keys(allUserIds)
|
||||||
//if err != nil {
|
allUsers := service.AllService.UserService.ListByIds(ids)
|
||||||
// response.Error(c, "操作失败")
|
for _, u := range allUsers {
|
||||||
// return
|
allUserIds[u.Id] = u
|
||||||
//}
|
}
|
||||||
c.JSON(http.StatusOK, "")
|
|
||||||
|
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 (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/gin-gonic/gin/binding"
|
||||||
|
request "github.com/lejianwen/rustdesk-api/v2/http/request/api"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/http/response"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/model"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/service"
|
||||||
|
"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, "")
|
||||||
|
}
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
apiReq "Gwen/http/request/api"
|
|
||||||
"Gwen/http/response"
|
|
||||||
apiResp "Gwen/http/response/api"
|
|
||||||
"Gwen/model"
|
|
||||||
"Gwen/service"
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
apiReq "github.com/lejianwen/rustdesk-api/v2/http/request/api"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/http/response"
|
||||||
|
apiResp "github.com/lejianwen/rustdesk-api/v2/http/response/api"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/model"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/service"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -28,24 +28,24 @@ type Group struct {
|
|||||||
// @Router /users [get]
|
// @Router /users [get]
|
||||||
// @Security BearerAuth
|
// @Security BearerAuth
|
||||||
func (g *Group) Users(c *gin.Context) {
|
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{}
|
q := &apiReq.UserListQuery{}
|
||||||
err := c.ShouldBindQuery(&q)
|
err := c.ShouldBindQuery(&q)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.Error(c, err.Error())
|
response.Error(c, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
userList := service.AllService.UserService.ListByGroupId(u.GroupId, q.Page, q.PageSize)
|
u := service.AllService.UserService.CurUser(c)
|
||||||
var data []*apiResp.UserPayload
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
data := make([]*apiResp.UserPayload, 0, len(userList.Users))
|
||||||
for _, user := range userList.Users {
|
for _, user := range userList.Users {
|
||||||
up := &apiResp.UserPayload{}
|
up := &apiResp.UserPayload{}
|
||||||
up.FromUser(user)
|
up.FromUser(user)
|
||||||
@@ -73,38 +73,45 @@ func (g *Group) Users(c *gin.Context) {
|
|||||||
// @Security BearerAuth
|
// @Security BearerAuth
|
||||||
func (g *Group) Peers(c *gin.Context) {
|
func (g *Group) Peers(c *gin.Context) {
|
||||||
u := service.AllService.UserService.CurUser(c)
|
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{}
|
q := &apiReq.PeerListQuery{}
|
||||||
err := c.ShouldBindQuery(&q)
|
err := c.ShouldBindQuery(&q)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.Error(c, err.Error())
|
response.Error(c, err.Error())
|
||||||
return
|
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, len(users))
|
||||||
namesById := make(map[uint]string)
|
userIds := make([]uint, 0, len(users))
|
||||||
userIds := make([]uint, 0)
|
|
||||||
for _, user := range users {
|
for _, user := range users {
|
||||||
namesById[user.Id] = user.Username
|
namesById[user.Id] = user.Username
|
||||||
userIds = append(userIds, user.Id)
|
userIds = append(userIds, user.Id)
|
||||||
}
|
}
|
||||||
peerList := service.AllService.AddressBookService.ListByUserIds(userIds, q.Page, q.PageSize)
|
dGroupNameById := make(map[uint]string)
|
||||||
var data []*apiResp.GroupPeerPayload
|
allGroup := service.AllService.GroupService.DeviceGroupList(1, 999, nil)
|
||||||
for _, ab := range peerList.AddressBooks {
|
for _, group := range allGroup.DeviceGroups {
|
||||||
uname, ok := namesById[ab.UserId]
|
dGroupNameById[group.Id] = group.Name
|
||||||
|
}
|
||||||
|
peerList := service.AllService.PeerService.ListByUserIds(userIds, q.Page, q.PageSize)
|
||||||
|
data := make([]*apiResp.GroupPeerPayload, 0, len(peerList.Peers))
|
||||||
|
for _, peer := range peerList.Peers {
|
||||||
|
uname, ok := namesById[peer.UserId]
|
||||||
if !ok {
|
if !ok {
|
||||||
uname = ""
|
uname = ""
|
||||||
}
|
}
|
||||||
|
dGroupName, ok2 := dGroupNameById[peer.GroupId]
|
||||||
|
if !ok2 {
|
||||||
|
dGroupName = ""
|
||||||
|
}
|
||||||
pp := &apiResp.GroupPeerPayload{}
|
pp := &apiResp.GroupPeerPayload{}
|
||||||
pp.FromAddressBook(ab, uname)
|
pp.FromPeer(peer, uname, dGroupName)
|
||||||
data = append(data, pp)
|
data = append(data, pp)
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -113,3 +120,31 @@ func (g *Group) Peers(c *gin.Context) {
|
|||||||
Data: data,
|
Data: data,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Device
|
||||||
|
// @Tags 群组
|
||||||
|
// @Summary 设备
|
||||||
|
// @Description 机器
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param page query int false "页码"
|
||||||
|
// @Param pageSize query int false "每页数量"
|
||||||
|
// @Param status query int false "状态"
|
||||||
|
// @Param accessible query string false "accessible"
|
||||||
|
// @Success 200 {object} response.DataResponse
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /device-group/accessible [get]
|
||||||
|
// @Security BearerAuth
|
||||||
|
func (g *Group) Device(c *gin.Context) {
|
||||||
|
u := service.AllService.UserService.CurUser(c)
|
||||||
|
if !service.AllService.UserService.IsAdmin(u) {
|
||||||
|
response.Error(c, "Permission denied")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
allGroup := service.AllService.GroupService.DeviceGroupList(1, 999, nil)
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, response.DataResponse{
|
||||||
|
Total: 0,
|
||||||
|
Data: allGroup.DeviceGroups,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"Gwen/http/response"
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
requstform "github.com/lejianwen/rustdesk-api/v2/http/request/api"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/http/response"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/model"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/service"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Index struct {
|
type Index struct {
|
||||||
@@ -35,5 +39,43 @@ func (i *Index) Index(c *gin.Context) {
|
|||||||
// @Failure 500 {object} response.Response
|
// @Failure 500 {object} response.Response
|
||||||
// @Router /heartbeat [post]
|
// @Router /heartbeat [post]
|
||||||
func (i *Index) Heartbeat(c *gin.Context) {
|
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.FindById(info.Id)
|
||||||
|
if peer == nil || peer.RowId == 0 {
|
||||||
|
c.JSON(http.StatusOK, gin.H{})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//如果在40s以内则不更新
|
||||||
|
if time.Now().Unix()-peer.LastOnlineTime >= 30 {
|
||||||
|
upp := &model.Peer{RowId: peer.RowId, LastOnlineTime: time.Now().Unix(), LastOnlineIp: c.ClientIP()}
|
||||||
|
service.AllService.PeerService.Update(upp)
|
||||||
|
}
|
||||||
c.JSON(http.StatusOK, gin.H{})
|
c.JSON(http.StatusOK, gin.H{})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Version 版本
|
||||||
|
// @Tags 首页
|
||||||
|
// @Summary 版本
|
||||||
|
// @Description 版本
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 {object} response.Response
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /version [get]
|
||||||
|
func (i *Index) Version(c *gin.Context) {
|
||||||
|
//读取resources/version文件
|
||||||
|
v := service.AllService.AppService.GetAppVersion()
|
||||||
|
response.Success(
|
||||||
|
c,
|
||||||
|
v,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"Gwen/global"
|
|
||||||
"Gwen/http/request/api"
|
|
||||||
"Gwen/http/response"
|
|
||||||
apiResp "Gwen/http/response/api"
|
|
||||||
"Gwen/model"
|
|
||||||
"Gwen/service"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/global"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/http/request/api"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/http/response"
|
||||||
|
apiResp "github.com/lejianwen/rustdesk-api/v2/http/response/api"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/model"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/service"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -26,16 +27,29 @@ type Login struct {
|
|||||||
// @Failure 500 {object} response.ErrorResponse
|
// @Failure 500 {object} response.ErrorResponse
|
||||||
// @Router /login [post]
|
// @Router /login [post]
|
||||||
func (l *Login) Login(c *gin.Context) {
|
func (l *Login) Login(c *gin.Context) {
|
||||||
|
if global.Config.App.DisablePwdLogin {
|
||||||
|
response.Error(c, response.TranslateMsg(c, "PwdLoginDisabled"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查登录限制
|
||||||
|
loginLimiter := global.LoginLimiter
|
||||||
|
clientIp := c.ClientIP()
|
||||||
|
|
||||||
f := &api.LoginForm{}
|
f := &api.LoginForm{}
|
||||||
err := c.ShouldBindJSON(f)
|
err := c.ShouldBindJSON(f)
|
||||||
//fmt.Println(f)
|
//fmt.Println(f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.Error(c, "参数错误")
|
loginLimiter.RecordFailedAttempt(clientIp)
|
||||||
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
errList := global.Validator.ValidStruct(f)
|
errList := global.Validator.ValidStruct(c, f)
|
||||||
if len(errList) > 0 {
|
if len(errList) > 0 {
|
||||||
|
loginLimiter.RecordFailedAttempt(clientIp)
|
||||||
|
global.Logger.Warn(fmt.Sprintf("Login Fail: %s %s %s", "ParamsError", c.RemoteIP(), c.ClientIP()))
|
||||||
response.Error(c, errList[0])
|
response.Error(c, errList[0])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -43,19 +57,27 @@ func (l *Login) Login(c *gin.Context) {
|
|||||||
u := service.AllService.UserService.InfoByUsernamePassword(f.Username, f.Password)
|
u := service.AllService.UserService.InfoByUsernamePassword(f.Username, f.Password)
|
||||||
|
|
||||||
if u.Id == 0 {
|
if u.Id == 0 {
|
||||||
response.Error(c, "用户名或密码错误")
|
loginLimiter.RecordFailedAttempt(clientIp)
|
||||||
|
global.Logger.Warn(fmt.Sprintf("Login Fail: %s %s %s", "UsernameOrPasswordError", c.RemoteIP(), c.ClientIP()))
|
||||||
|
response.Error(c, response.TranslateMsg(c, "UsernameOrPasswordError"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !service.AllService.UserService.CheckUserEnable(u) {
|
||||||
|
response.Error(c, response.TranslateMsg(c, "UserDisabled"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
//根据refer判断是webclient还是app
|
//根据refer判断是webclient还是app
|
||||||
ref := c.GetHeader("referer")
|
ref := c.GetHeader("referer")
|
||||||
if ref != "" {
|
if ref != "" {
|
||||||
f.DeviceInfo.Type = "webclient"
|
f.DeviceInfo.Type = model.LoginLogClientWeb
|
||||||
}
|
}
|
||||||
|
|
||||||
ut := service.AllService.UserService.Login(u, &model.LoginLog{
|
ut := service.AllService.UserService.Login(u, &model.LoginLog{
|
||||||
UserId: u.Id,
|
UserId: u.Id,
|
||||||
Client: f.DeviceInfo.Type,
|
Client: f.DeviceInfo.Type,
|
||||||
|
DeviceId: f.Id,
|
||||||
Uuid: f.Uuid,
|
Uuid: f.Uuid,
|
||||||
Ip: c.ClientIP(),
|
Ip: c.ClientIP(),
|
||||||
Type: model.LoginLogTypeAccount,
|
Type: model.LoginLogTypeAccount,
|
||||||
@@ -77,30 +99,24 @@ func (l *Login) Login(c *gin.Context) {
|
|||||||
// @Produce json
|
// @Produce json
|
||||||
// @Success 200 {object} []string
|
// @Success 200 {object} []string
|
||||||
// @Failure 500 {object} response.ErrorResponse
|
// @Failure 500 {object} response.ErrorResponse
|
||||||
// @Router /login-options [post]
|
// @Router /login-options [get]
|
||||||
func (l *Login) LoginOptions(c *gin.Context) {
|
func (l *Login) LoginOptions(c *gin.Context) {
|
||||||
oauthOks := []string{}
|
ops := service.AllService.OauthService.GetOauthProviders()
|
||||||
err, _ := service.AllService.OauthService.GetOauthConfig(model.OauthTypeGithub)
|
if global.Config.App.WebSso {
|
||||||
if err == nil {
|
ops = append(ops, model.OauthTypeWebauth)
|
||||||
oauthOks = append(oauthOks, model.OauthTypeGithub)
|
|
||||||
}
|
}
|
||||||
err, _ = service.AllService.OauthService.GetOauthConfig(model.OauthTypeGoogle)
|
|
||||||
if err == nil {
|
|
||||||
oauthOks = append(oauthOks, model.OauthTypeGoogle)
|
|
||||||
}
|
|
||||||
oauthOks = append(oauthOks, model.OauthTypeWebauth)
|
|
||||||
var oidcItems []map[string]string
|
var oidcItems []map[string]string
|
||||||
for _, v := range oauthOks {
|
for _, v := range ops {
|
||||||
oidcItems = append(oidcItems, map[string]string{"name": v})
|
oidcItems = append(oidcItems, map[string]string{"name": v})
|
||||||
}
|
}
|
||||||
common, err := json.Marshal(oidcItems)
|
common, err := json.Marshal(oidcItems)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.Error(c, "参数错误")
|
response.Error(c, response.TranslateMsg(c, "SystemError")+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var res []string
|
var res []string
|
||||||
res = append(res, "common-oidc/"+string(common))
|
res = append(res, "common-oidc/"+string(common))
|
||||||
for _, v := range oauthOks {
|
for _, v := range ops {
|
||||||
res = append(res, "oidc/"+v)
|
res = append(res, "oidc/"+v)
|
||||||
}
|
}
|
||||||
c.JSON(http.StatusOK, res)
|
c.JSON(http.StatusOK, res)
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
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"
|
"net/http"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/global"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/http/request/api"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/http/response"
|
||||||
|
apiResp "github.com/lejianwen/rustdesk-api/v2/http/response/api"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/model"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/service"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/utils"
|
||||||
|
"github.com/nicksnyder/go-i18n/v2/i18n"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Oauth struct {
|
type Oauth struct {
|
||||||
@@ -29,21 +30,19 @@ func (o *Oauth) OidcAuth(c *gin.Context) {
|
|||||||
f := &api.OidcAuthRequest{}
|
f := &api.OidcAuthRequest{}
|
||||||
err := c.ShouldBindJSON(&f)
|
err := c.ShouldBindJSON(&f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.Error(c, "参数错误")
|
response.Error(c, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
return
|
|
||||||
}
|
|
||||||
if f.Op != model.OauthTypeWebauth && f.Op != model.OauthTypeGoogle && f.Op != model.OauthTypeGithub {
|
|
||||||
response.Error(c, "参数错误")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err, code, url := service.AllService.OauthService.BeginAuth(f.Op)
|
oauthService := service.AllService.OauthService
|
||||||
|
|
||||||
|
err, state, verifier, nonce, url := oauthService.BeginAuth(f.Op)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.Error(c, err.Error())
|
response.Error(c, response.TranslateMsg(c, err.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
service.AllService.OauthService.SetOauthCache(code, &service.OauthCacheItem{
|
service.AllService.OauthService.SetOauthCache(state, &service.OauthCacheItem{
|
||||||
Action: service.OauthActionTypeLogin,
|
Action: service.OauthActionTypeLogin,
|
||||||
Id: f.Id,
|
Id: f.Id,
|
||||||
Op: f.Op,
|
Op: f.Op,
|
||||||
@@ -51,14 +50,71 @@ func (o *Oauth) OidcAuth(c *gin.Context) {
|
|||||||
DeviceName: f.DeviceInfo.Name,
|
DeviceName: f.DeviceInfo.Name,
|
||||||
DeviceOs: f.DeviceInfo.Os,
|
DeviceOs: f.DeviceInfo.Os,
|
||||||
DeviceType: f.DeviceInfo.Type,
|
DeviceType: f.DeviceInfo.Type,
|
||||||
|
Verifier: verifier,
|
||||||
|
Nonce: nonce,
|
||||||
}, 5*60)
|
}, 5*60)
|
||||||
//fmt.Println("code url", code, url)
|
//fmt.Println("code url", code, url)
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
"code": code,
|
"code": state,
|
||||||
"url": url,
|
"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 {
|
||||||
|
//fix: 1.4.2 webclient oidc
|
||||||
|
c.JSON(http.StatusOK, gin.H{"message": "Authorization in progress, please login and bind", "error": "No authed oidc is found"})
|
||||||
|
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,
|
||||||
|
DeviceId: v.Id,
|
||||||
|
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
|
// OidcAuthQuery
|
||||||
// @Tags Oauth
|
// @Tags Oauth
|
||||||
// @Summary OidcAuthQuery
|
// @Summary OidcAuthQuery
|
||||||
@@ -69,42 +125,15 @@ func (o *Oauth) OidcAuth(c *gin.Context) {
|
|||||||
// @Failure 500 {object} response.ErrorResponse
|
// @Failure 500 {object} response.ErrorResponse
|
||||||
// @Router /oidc/auth-query [get]
|
// @Router /oidc/auth-query [get]
|
||||||
func (o *Oauth) OidcAuthQuery(c *gin.Context) {
|
func (o *Oauth) OidcAuthQuery(c *gin.Context) {
|
||||||
q := &api.OidcAuthQuery{}
|
u, ut := o.OidcAuthQueryPre(c)
|
||||||
err := c.ShouldBindQuery(q)
|
if u == nil || ut == nil {
|
||||||
if err != nil {
|
|
||||||
response.Error(c, "参数错误")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
v := service.AllService.OauthService.GetOauthCache(q.Code)
|
c.JSON(http.StatusOK, apiResp.LoginRes{
|
||||||
if v == nil {
|
AccessToken: ut.Token,
|
||||||
response.Error(c, "授权已过期,请重新授权")
|
Type: "access_token",
|
||||||
return
|
User: *(&apiResp.UserPayload{}).FromUser(u),
|
||||||
}
|
})
|
||||||
if v.UserId == 0 {
|
|
||||||
//正在授权
|
|
||||||
c.JSON(http.StatusOK, gin.H{})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
u := service.AllService.UserService.InfoById(v.UserId)
|
|
||||||
//fmt.Println("auth success u", u)
|
|
||||||
if u.Id > 0 {
|
|
||||||
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,
|
|
||||||
})
|
|
||||||
c.JSON(http.StatusOK, apiResp.LoginRes{
|
|
||||||
AccessToken: ut.Token,
|
|
||||||
Type: "access_token",
|
|
||||||
User: *(&apiResp.UserPayload{}).FromUser(u),
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
response.Error(c, "用户不存在")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// OauthCallback 回调
|
// OauthCallback 回调
|
||||||
@@ -115,165 +144,162 @@ func (o *Oauth) OidcAuthQuery(c *gin.Context) {
|
|||||||
// @Produce json
|
// @Produce json
|
||||||
// @Success 200 {object} apiResp.LoginRes
|
// @Success 200 {object} apiResp.LoginRes
|
||||||
// @Failure 500 {object} response.ErrorResponse
|
// @Failure 500 {object} response.ErrorResponse
|
||||||
// @Router /oauth/callback [get]
|
// @Router /oidc/callback [get]
|
||||||
func (o *Oauth) OauthCallback(c *gin.Context) {
|
func (o *Oauth) OauthCallback(c *gin.Context) {
|
||||||
state := c.Query("state")
|
state := c.Query("state")
|
||||||
if state == "" {
|
if state == "" {
|
||||||
c.String(http.StatusInternalServerError, "state为空")
|
c.HTML(http.StatusOK, "oauth_fail.html", gin.H{
|
||||||
|
"message": "ParamIsEmpty",
|
||||||
|
"sub_message": "state",
|
||||||
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
cacheKey := state
|
cacheKey := state
|
||||||
|
oauthService := service.AllService.OauthService
|
||||||
//从缓存中获取
|
//从缓存中获取
|
||||||
v := service.AllService.OauthService.GetOauthCache(cacheKey)
|
oauthCache := oauthService.GetOauthCache(cacheKey)
|
||||||
if v == nil {
|
if oauthCache == nil {
|
||||||
c.String(http.StatusInternalServerError, "授权已过期,请重新授权")
|
c.HTML(http.StatusOK, "oauth_fail.html", gin.H{
|
||||||
|
"message": "OauthExpired",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
nonce := oauthCache.Nonce
|
||||||
|
op := oauthCache.Op
|
||||||
|
action := oauthCache.Action
|
||||||
|
verifier := oauthCache.Verifier
|
||||||
|
var user *model.User
|
||||||
|
// 获取用户信息
|
||||||
|
code := c.Query("code")
|
||||||
|
err, oauthUser := oauthService.Callback(code, verifier, op, nonce)
|
||||||
|
if err != nil {
|
||||||
|
c.HTML(http.StatusOK, "oauth_fail.html", gin.H{
|
||||||
|
"message": "OauthFailed",
|
||||||
|
"sub_message": err.Error(),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
userId := oauthCache.UserId
|
||||||
|
openid := oauthUser.OpenId
|
||||||
|
if action == service.OauthActionTypeBind {
|
||||||
|
|
||||||
|
//fmt.Println("bind", ty, userData)
|
||||||
|
// 检查此openid是否已经绑定过
|
||||||
|
utr := oauthService.UserThirdInfo(op, openid)
|
||||||
|
if utr.UserId > 0 {
|
||||||
|
c.HTML(http.StatusOK, "oauth_fail.html", gin.H{
|
||||||
|
"message": "OauthHasBindOtherUser",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//绑定
|
||||||
|
user = service.AllService.UserService.InfoById(userId)
|
||||||
|
if user == nil {
|
||||||
|
c.HTML(http.StatusOK, "oauth_fail.html", gin.H{
|
||||||
|
"message": "ItemNotFound",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//绑定
|
||||||
|
err := oauthService.BindOauthUser(userId, oauthUser, op)
|
||||||
|
if err != nil {
|
||||||
|
c.HTML(http.StatusOK, "oauth_fail.html", gin.H{
|
||||||
|
"message": "BindFail",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.HTML(http.StatusOK, "oauth_success.html", gin.H{
|
||||||
|
"message": "BindSuccess",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
|
||||||
|
} else if action == service.OauthActionTypeLogin {
|
||||||
|
//登录
|
||||||
|
if userId != 0 {
|
||||||
|
c.HTML(http.StatusOK, "oauth_fail.html", gin.H{
|
||||||
|
"message": "OauthHasBeenSuccess",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
user = service.AllService.UserService.InfoByOauthId(op, openid)
|
||||||
|
if user == nil {
|
||||||
|
oauthConfig := oauthService.InfoByOp(op)
|
||||||
|
if !*oauthConfig.AutoRegister {
|
||||||
|
//c.String(http.StatusInternalServerError, "还未绑定用户,请先绑定")
|
||||||
|
oauthCache.UpdateFromOauthUser(oauthUser)
|
||||||
|
c.Redirect(http.StatusFound, "/_admin/#/oauth/bind/"+cacheKey)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//自动注册
|
||||||
|
err, user = service.AllService.UserService.RegisterByOauth(oauthUser, op)
|
||||||
|
if err != nil {
|
||||||
|
c.HTML(http.StatusOK, "oauth_fail.html", gin.H{
|
||||||
|
"message": err.Error(),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
oauthCache.UserId = user.Id
|
||||||
|
oauthService.SetOauthCache(cacheKey, oauthCache, 0)
|
||||||
|
// 如果是webadmin,登录成功后跳转到webadmin
|
||||||
|
if oauthCache.DeviceType == model.LoginLogClientWebAdmin {
|
||||||
|
/*service.AllService.UserService.Login(u, &model.LoginLog{
|
||||||
|
UserId: u.Id,
|
||||||
|
Client: "webadmin",
|
||||||
|
Uuid: "", //must be empty
|
||||||
|
Ip: c.ClientIP(),
|
||||||
|
Type: model.LoginLogTypeOauth,
|
||||||
|
Platform: oauthService.DeviceOs,
|
||||||
|
})*/
|
||||||
|
c.Redirect(http.StatusFound, "/_admin/#/")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.HTML(http.StatusOK, "oauth_success.html", gin.H{
|
||||||
|
"message": "OauthSuccess",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
c.HTML(http.StatusOK, "oauth_fail.html", gin.H{
|
||||||
|
"message": "ParamsError",
|
||||||
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ty := v.Op
|
|
||||||
ac := v.Action
|
|
||||||
//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, "授权失败:"+err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if ac == service.OauthActionTypeBind {
|
|
||||||
//fmt.Println("bind", ty, userData)
|
|
||||||
utr := service.AllService.OauthService.UserThirdInfo(ty, strconv.Itoa(userData.Id))
|
|
||||||
if utr.UserId > 0 {
|
|
||||||
c.String(http.StatusInternalServerError, "已经绑定其他账号")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
//绑定
|
|
||||||
u := service.AllService.UserService.InfoById(v.UserId)
|
|
||||||
if u == nil {
|
|
||||||
c.String(http.StatusInternalServerError, "用户不存在")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
//绑定github
|
|
||||||
err = service.AllService.OauthService.BindGithubUser(strconv.Itoa(userData.Id), userData.Login, v.UserId)
|
|
||||||
if err != nil {
|
|
||||||
c.String(http.StatusInternalServerError, "绑定失败")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.String(http.StatusOK, "绑定成功")
|
|
||||||
return
|
|
||||||
} else if ac == service.OauthActionTypeLogin {
|
|
||||||
//登录
|
|
||||||
if v.UserId != 0 {
|
|
||||||
c.String(http.StatusInternalServerError, "授权已经成功")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
u := service.AllService.UserService.InfoByGithubId(strconv.Itoa(userData.Id))
|
|
||||||
if u == nil {
|
|
||||||
oa := service.AllService.OauthService.InfoByOp(ty)
|
|
||||||
if !*oa.AutoRegister {
|
|
||||||
//c.String(http.StatusInternalServerError, "还未绑定用户,请先绑定")
|
|
||||||
v.ThirdName = userData.Login
|
|
||||||
v.ThirdOpenId = strconv.Itoa(userData.Id)
|
|
||||||
url := global.Config.Rustdesk.ApiServer + "/_admin/#/oauth/bind/" + cacheKey
|
|
||||||
c.Redirect(http.StatusFound, url)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
//自动注册
|
|
||||||
u = service.AllService.UserService.RegisterByGithub(userData.Login, strconv.Itoa(userData.Id))
|
|
||||||
if u.Id == 0 {
|
|
||||||
c.String(http.StatusInternalServerError, "注册失败")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
v.UserId = u.Id
|
|
||||||
service.AllService.OauthService.SetOauthCache(cacheKey, v, 0)
|
|
||||||
c.String(http.StatusOK, "授权成功")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
//返回js
|
|
||||||
c.Header("Content-Type", "text/html; charset=utf-8")
|
|
||||||
c.String(http.StatusOK, "授权错误")
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if ty == model.OauthTypeGoogle {
|
|
||||||
code := c.Query("code")
|
|
||||||
err, userData := service.AllService.OauthService.GoogleCallback(code)
|
|
||||||
if err != nil {
|
|
||||||
c.String(http.StatusInternalServerError, "授权失败:"+err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
//将空格替换成_
|
|
||||||
googleName := strings.Replace(userData.Name, " ", "_", -1)
|
|
||||||
if ac == service.OauthActionTypeBind {
|
|
||||||
//fmt.Println("bind", ty, userData)
|
|
||||||
utr := service.AllService.OauthService.UserThirdInfo(ty, userData.Email)
|
|
||||||
if utr.UserId > 0 {
|
|
||||||
c.String(http.StatusInternalServerError, "已经绑定其他账号")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
//绑定
|
|
||||||
u := service.AllService.UserService.InfoById(v.UserId)
|
|
||||||
if u == nil {
|
|
||||||
c.String(http.StatusInternalServerError, "用户不存在")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
//绑定
|
|
||||||
err = service.AllService.OauthService.BindGoogleUser(userData.Email, googleName, v.UserId)
|
|
||||||
if err != nil {
|
|
||||||
c.String(http.StatusInternalServerError, "绑定失败")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.String(http.StatusOK, "绑定成功")
|
|
||||||
return
|
|
||||||
} else if ac == service.OauthActionTypeLogin {
|
|
||||||
if v.UserId != 0 {
|
|
||||||
c.String(http.StatusInternalServerError, "授权已经成功")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
u := service.AllService.UserService.InfoByGoogleEmail(userData.Email)
|
|
||||||
if u == nil {
|
|
||||||
oa := service.AllService.OauthService.InfoByOp(ty)
|
|
||||||
if !*oa.AutoRegister {
|
|
||||||
//c.String(http.StatusInternalServerError, "还未绑定用户,请先绑定")
|
|
||||||
|
|
||||||
v.ThirdName = googleName
|
|
||||||
v.ThirdOpenId = userData.Email
|
|
||||||
url := global.Config.Rustdesk.ApiServer + "/_admin/#/oauth/bind/" + cacheKey
|
|
||||||
c.Redirect(http.StatusFound, url)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
//自动注册
|
|
||||||
u = service.AllService.UserService.RegisterByGoogle(googleName, userData.Email)
|
|
||||||
if u.Id == 0 {
|
|
||||||
c.String(http.StatusInternalServerError, "注册失败")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
v.UserId = u.Id
|
|
||||||
service.AllService.OauthService.SetOauthCache(cacheKey, v, 0)
|
|
||||||
c.String(http.StatusOK, "授权成功")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
c.String(http.StatusInternalServerError, "授权配置错误,请联系管理员")
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// WebOauthLogin
|
type MessageParams struct {
|
||||||
// @Tags Oauth
|
Lang string `json:"lang" form:"lang"`
|
||||||
// @Summary WebOauthLogin
|
Title string `json:"title" form:"title"`
|
||||||
// @Description WebOauthLogin
|
Msg string `json:"msg" form:"msg"`
|
||||||
// @Accept json
|
}
|
||||||
// @Produce json
|
|
||||||
// @Success 200 {string} string
|
func (o *Oauth) Message(c *gin.Context) {
|
||||||
// @Failure 500 {string} string
|
mp := &MessageParams{}
|
||||||
// @Router /oauth/login [get]
|
if err := c.ShouldBindQuery(mp); err != nil {
|
||||||
func (o *Oauth) WebOauthLogin(c *gin.Context) {
|
return
|
||||||
|
}
|
||||||
|
localizer := global.Localizer(mp.Lang)
|
||||||
|
res := ""
|
||||||
|
if mp.Title != "" {
|
||||||
|
title, err := localizer.LocalizeMessage(&i18n.Message{
|
||||||
|
ID: mp.Title,
|
||||||
|
})
|
||||||
|
if err == nil {
|
||||||
|
res = utils.StringConcat(";title='", title, "';")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if mp.Msg != "" {
|
||||||
|
msg, err := localizer.LocalizeMessage(&i18n.Message{
|
||||||
|
ID: mp.Msg,
|
||||||
|
})
|
||||||
|
if err == nil {
|
||||||
|
res = utils.StringConcat(res, "msg = '", msg, "';")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//返回js内容
|
||||||
|
c.Header("Content-Type", "application/javascript")
|
||||||
|
c.String(http.StatusOK, res)
|
||||||
}
|
}
|
||||||
|
|||||||