Compare commits
58 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9aad62d1e4 | ||
|
|
867eab40f8 | ||
|
|
eb5c7efc4c | ||
|
|
857abc16e7 | ||
|
|
28b9866c42 | ||
|
|
a27deb0a41 | ||
|
|
8e026de20b | ||
|
|
718ecc2372 | ||
|
|
56d46722f4 | ||
|
|
b6463cd715 | ||
|
|
bd3ae0cbfe | ||
|
|
83c3aa894f | ||
|
|
1b88d26fea | ||
|
|
588287fdb4 | ||
|
|
688e544b07 | ||
|
|
3e3f812e83 | ||
|
|
b551c7abe4 | ||
|
|
6d1e7a4c05 | ||
|
|
3341a4bc8e | ||
|
|
1c84980d36 | ||
|
|
833b25881d | ||
|
|
9dbf58903c | ||
|
|
ad007f0d91 | ||
|
|
4b06973a52 | ||
|
|
159a67f15d | ||
|
|
7c03b9953b | ||
|
|
f90987de8d | ||
|
|
70e4ff7820 | ||
|
|
a99356f54b | ||
|
|
c5bc9534cc | ||
|
|
a40733424f | ||
|
|
a937efc60e | ||
|
|
6adb0e8415 | ||
|
|
ff9ffb2f12 | ||
|
|
9be4f472ae | ||
|
|
8581d74b08 | ||
|
|
dafe9bd6b6 | ||
|
|
3ae5772360 | ||
|
|
4628dbccfb | ||
|
|
572b1d4c14 | ||
|
|
bdb70e9859 | ||
|
|
38bda17271 | ||
|
|
455e1d2e5b | ||
|
|
89cd724bab | ||
|
|
b9109b4d0e | ||
|
|
945958f552 | ||
|
|
78eb0d5c06 | ||
|
|
bc6eae711e | ||
|
|
f0a4bf6164 | ||
|
|
fc3b5e3ac3 | ||
|
|
f7235ac847 | ||
|
|
231f4ddb7f | ||
|
|
3cad3994cb | ||
|
|
8c97cc8686 | ||
|
|
7ae976ee5d | ||
|
|
e91b53eb32 | ||
|
|
90311536a7 | ||
|
|
e951b7f2f9 |
290
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,290 @@
|
|||||||
|
name: Build
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
BASE_IMAGE_NAMESPACE:
|
||||||
|
description: 'Base image namespace (Default: Your Github username)'
|
||||||
|
required: false
|
||||||
|
default: ''
|
||||||
|
DOCKERHUB_IMAGE_NAMESPACE:
|
||||||
|
description: 'Docker Hub image namespace (Default: Your Github username)'
|
||||||
|
required: false
|
||||||
|
default: ''
|
||||||
|
GHCR_IMAGE_NAMESPACE:
|
||||||
|
description: 'GitHub Container Registry image namespace (Default: Your Github username)'
|
||||||
|
required: false
|
||||||
|
default: ''
|
||||||
|
SKIP_DOCKER_HUB:
|
||||||
|
description: 'Set to true to skip pushing to Docker Hub (default: false)'
|
||||||
|
required: false
|
||||||
|
default: 'false'
|
||||||
|
SKIP_GHCR:
|
||||||
|
description: 'Set to true to skip pushing to GHCR (default: false)'
|
||||||
|
required: false
|
||||||
|
default: 'false'
|
||||||
|
WEBCLIENT_SOURCE_LOCATION:
|
||||||
|
description: 'Web Client API Repository'
|
||||||
|
required: true
|
||||||
|
default: 'https://github.com/lejianwen/rustdesk-api-web'
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'v*.*.*' # 当推送带有版本号的 tag(例如 v1.0.0)时触发工作流
|
||||||
|
- 'test*'
|
||||||
|
|
||||||
|
env:
|
||||||
|
LATEST_TAG: latest
|
||||||
|
WEBCLIENT_SOURCE_LOCATION: ${{ github.event.inputs.WEBCLIENT_SOURCE_LOCATION || 'https://github.com/lejianwen/rustdesk-api-web' }}
|
||||||
|
BASE_IMAGE_NAMESPACE: ${{ github.event.inputs.BASE_IMAGE_NAMESPACE || github.actor }}
|
||||||
|
DOCKERHUB_IMAGE_NAMESPACE: ${{ github.event.inputs.DOCKERHUB_IMAGE_NAMESPACE || github.actor }}
|
||||||
|
GHCR_IMAGE_NAMESPACE: ${{ github.event.inputs.GHCR_IMAGE_NAMESPACE || github.actor }}
|
||||||
|
SKIP_DOCKER_HUB: ${{ github.event.inputs.SKIP_DOCKER_HUB || 'false' }}
|
||||||
|
SKIP_GHCR: ${{ github.event.inputs.SKIP_GHCR }}
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
job:
|
||||||
|
- { platform: "amd64", goos: "linux" }
|
||||||
|
- { platform: "arm64", goos: "linux" }
|
||||||
|
- { platform: "amd64", goos: "windows" }
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Go environment
|
||||||
|
uses: actions/setup-go@v4
|
||||||
|
with:
|
||||||
|
go-version: '1.22' # 选择 Go 版本
|
||||||
|
|
||||||
|
- name: Set up npm
|
||||||
|
uses: actions/setup-node@v2
|
||||||
|
with:
|
||||||
|
node-version: '20'
|
||||||
|
|
||||||
|
|
||||||
|
- name: build rustdesk-api-web
|
||||||
|
run: |
|
||||||
|
git clone ${{ env.WEBCLIENT_SOURCE_LOCATION }}
|
||||||
|
cd rustdesk-api-web
|
||||||
|
npm install
|
||||||
|
npm run build
|
||||||
|
mkdir ../resources/admin/ -p
|
||||||
|
cp -ar dist/* ../resources/admin/
|
||||||
|
|
||||||
|
- name: tidy
|
||||||
|
run: go mod tidy
|
||||||
|
|
||||||
|
- name: swag
|
||||||
|
run: |
|
||||||
|
go install github.com/swaggo/swag/cmd/swag@latest
|
||||||
|
swag init -g cmd/apimain.go --output docs/api --instanceName api --exclude http/controller/admin
|
||||||
|
swag init -g cmd/apimain.go --output docs/admin --instanceName admin --exclude http/controller/api
|
||||||
|
|
||||||
|
- name: Build for ${{ matrix.job.goos }}-${{ matrix.job.platform }}
|
||||||
|
run: |
|
||||||
|
mkdir release -p
|
||||||
|
cp -ar resources release/
|
||||||
|
cp -ar docs release/
|
||||||
|
cp -ar conf release/
|
||||||
|
mkdir -p release/data
|
||||||
|
mkdir -p release/runtime
|
||||||
|
if [ "${{ matrix.job.goos }}" = "windows" ]; then
|
||||||
|
sudo apt-get install gcc-mingw-w64-x86-64 zip -y
|
||||||
|
GOOS=${{ matrix.job.goos }} GOARCH=${{ matrix.job.platform }} CC=x86_64-w64-mingw32-gcc CGO_LDFLAGS="-static" CGO_ENABLED=1 go build -ldflags "-s -w" -o ./release/apimain.exe ./cmd/apimain.go
|
||||||
|
zip -r ${{ matrix.job.goos}}-${{ matrix.job.platform }}.zip ./release
|
||||||
|
else
|
||||||
|
if [ "${{ matrix.job.platform }}" = "arm64" ]; then
|
||||||
|
wget https://musl.cc/aarch64-linux-musl-cross.tgz
|
||||||
|
tar -xf aarch64-linux-musl-cross.tgz
|
||||||
|
export PATH=$PATH:$PWD/aarch64-linux-musl-cross/bin
|
||||||
|
GOOS=${{ matrix.job.goos }} GOARCH=${{ matrix.job.platform }} CC=aarch64-linux-musl-gcc CGO_LDFLAGS="-static" CGO_ENABLED=1 go build -ldflags "-s -w" -o ./release/apimain ./cmd/apimain.go
|
||||||
|
else
|
||||||
|
sudo apt-get install musl musl-dev musl-tools -y
|
||||||
|
GOOS=${{ matrix.job.goos }} GOARCH=${{ matrix.job.platform }} CC=musl-gcc CGO_LDFLAGS="-static" CGO_ENABLED=1 go build -ldflags "-s -w" -o ./release/apimain ./cmd/apimain.go
|
||||||
|
fi
|
||||||
|
tar -czf ${{ matrix.job.goos}}-${{ matrix.job.platform }}.tar.gz ./release
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Upload artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: rustdesk-api-${{ matrix.job.goos }}-${{ matrix.job.platform }}
|
||||||
|
path: |
|
||||||
|
${{ matrix.job.goos}}-${{ matrix.job.platform }}.tar.gz
|
||||||
|
${{ matrix.job.goos}}-${{ matrix.job.platform }}.zip
|
||||||
|
- name: Upload to GitHub Release
|
||||||
|
uses: softprops/action-gh-release@v2
|
||||||
|
with:
|
||||||
|
files: |
|
||||||
|
${{ matrix.job.goos}}-${{ matrix.job.platform }}.tar.gz
|
||||||
|
${{ matrix.job.goos}}-${{ matrix.job.platform }}.zip
|
||||||
|
# tag_name: ${{ env.LATEST_TAG }}
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
docker:
|
||||||
|
name: Push Docker Image
|
||||||
|
needs: build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
job:
|
||||||
|
- { platform: "amd64", goos: "linux", docker_platform: "linux/amd64" }
|
||||||
|
- { platform: "arm64", goos: "linux", docker_platform: "linux/arm64" }
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v2
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v2
|
||||||
|
|
||||||
|
- name: Log in to Docker Hub
|
||||||
|
if: ${{ env.SKIP_DOCKER_HUB == 'false' }} # Only log in if SKIP_DOCKER_HUB is false
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
|
||||||
|
|
||||||
|
- name: Log in to GitHub Container Registry
|
||||||
|
if: ${{ env.SKIP_GHCR == 'false' }} # Only log in if GHCR push is enabled
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Extract version from tag
|
||||||
|
id: vars
|
||||||
|
run: |
|
||||||
|
if [[ "${GITHUB_REF}" == refs/tags/* ]]; then
|
||||||
|
echo "TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
|
||||||
|
else
|
||||||
|
echo "TAG=latest" >> $GITHUB_ENV # Default to 'latest' if not a tag
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Extract metadata (tags, labels) for Docker
|
||||||
|
id: meta
|
||||||
|
uses: docker/metadata-action@v4
|
||||||
|
with:
|
||||||
|
images: ${{ env.BASE_IMAGE_NAMESPACE }}/rustdesk-api
|
||||||
|
|
||||||
|
- name: Download binaries
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: rustdesk-api-${{ matrix.job.goos }}-${{ matrix.job.platform }}
|
||||||
|
path: ./
|
||||||
|
|
||||||
|
- name: Unzip binaries
|
||||||
|
run: |
|
||||||
|
mkdir -p ${{ matrix.job.platform }}
|
||||||
|
tar -xzf ${{ matrix.job.goos }}-${{ matrix.job.platform }}.tar.gz -C ${{ matrix.job.platform }}
|
||||||
|
file ${{ matrix.job.platform }}/apimain
|
||||||
|
|
||||||
|
- name: Build and push Docker image to Docker Hub ${{ matrix.job.platform }}
|
||||||
|
if: ${{ env.SKIP_DOCKER_HUB == 'false' }} # Only run this step if SKIP_DOCKER_HUB is false
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
with:
|
||||||
|
context: "."
|
||||||
|
file: ./Dockerfile
|
||||||
|
platforms: ${{ matrix.job.docker_platform }}
|
||||||
|
push: true
|
||||||
|
provenance: false
|
||||||
|
build-args: |
|
||||||
|
BUILDARCH=${{ matrix.job.platform }}
|
||||||
|
tags: |
|
||||||
|
${{ env.DOCKERHUB_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.LATEST_TAG }}-${{ matrix.job.platform }},
|
||||||
|
${{ env.DOCKERHUB_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}-${{ matrix.job.platform }}
|
||||||
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
|
||||||
|
- name: Build and push Docker image to GHCR ${{ matrix.job.platform }}
|
||||||
|
if: ${{ env.SKIP_GHCR == 'false' }} # Only run this step if SKIP_GHCR is false
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
with:
|
||||||
|
context: "."
|
||||||
|
file: ./Dockerfile
|
||||||
|
platforms: ${{ matrix.job.docker_platform }}
|
||||||
|
push: true
|
||||||
|
provenance: false
|
||||||
|
build-args: |
|
||||||
|
BUILDARCH=${{ matrix.job.platform }}
|
||||||
|
tags: |
|
||||||
|
ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.LATEST_TAG }}-${{ matrix.job.platform }},
|
||||||
|
ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}-${{ matrix.job.platform }}
|
||||||
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
|
||||||
|
#
|
||||||
|
docker-manifest:
|
||||||
|
name: Push Docker Manifest
|
||||||
|
needs: docker
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Extract version from tag
|
||||||
|
id: vars
|
||||||
|
run: |
|
||||||
|
if [[ "${GITHUB_REF}" == refs/tags/* ]]; then
|
||||||
|
echo "TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
|
||||||
|
else
|
||||||
|
echo "TAG=latest" >> $GITHUB_ENV # Default to 'latest' if not a tag
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Log in to Docker Hub
|
||||||
|
if: ${{ env.SKIP_DOCKER_HUB == 'false' }} # Only log in if Docker Hub push is enabled
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
|
||||||
|
|
||||||
|
- name: Log in to GitHub Container Registry
|
||||||
|
if: ${{ env.SKIP_GHCR == 'false' }} # Only log in if GHCR push is enabled
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Create and push manifest Docker Hub (:version)
|
||||||
|
if: ${{ env.SKIP_DOCKER_HUB == 'false' }}
|
||||||
|
uses: Noelware/docker-manifest-action@master
|
||||||
|
with:
|
||||||
|
base-image: ${{ env.BASE_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}
|
||||||
|
extra-images: ${{ env.DOCKERHUB_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}-amd64,
|
||||||
|
${{ env.DOCKERHUB_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}-arm64
|
||||||
|
push: true
|
||||||
|
|
||||||
|
- name: Create and push manifest GHCR (:version)
|
||||||
|
if: ${{ env.SKIP_GHCR == 'false' }}
|
||||||
|
uses: Noelware/docker-manifest-action@master
|
||||||
|
with:
|
||||||
|
base-image: ghcr.io/${{ env.BASE_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}
|
||||||
|
extra-images: ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}-amd64,
|
||||||
|
ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}-arm64
|
||||||
|
push: true
|
||||||
|
amend: true
|
||||||
|
|
||||||
|
- name: Create and push manifest Docker Hub (:latest)
|
||||||
|
if: ${{ env.SKIP_DOCKER_HUB == 'false' }}
|
||||||
|
uses: Noelware/docker-manifest-action@master
|
||||||
|
with:
|
||||||
|
base-image: ${{ env.BASE_IMAGE_NAMESPACE }}/rustdesk-api:latest
|
||||||
|
extra-images: ${{ env.DOCKERHUB_IMAGE_NAMESPACE }}/rustdesk-api:latest-amd64,
|
||||||
|
${{ env.DOCKERHUB_IMAGE_NAMESPACE }}/rustdesk-api:latest-arm64
|
||||||
|
push: true
|
||||||
|
|
||||||
|
- name: Create and push manifest GHCR (:latest)
|
||||||
|
if: ${{ env.SKIP_GHCR == 'false' }}
|
||||||
|
uses: Noelware/docker-manifest-action@master
|
||||||
|
with:
|
||||||
|
base-image: ghcr.io/${{ env.BASE_IMAGE_NAMESPACE }}/rustdesk-api:latest
|
||||||
|
extra-images: ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:latest-amd64,
|
||||||
|
ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:latest-arm64
|
||||||
|
push: true
|
||||||
|
amend: true
|
||||||
38
.github/workflows/docker.yml
vendored
@@ -1,38 +0,0 @@
|
|||||||
name: Build and Push Docker Image
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
tags:
|
|
||||||
- 'v*.*.*' # 仅当推送标签(例如 v1.0.0)时触发
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
|
|
||||||
- name: Log in to Docker Hub
|
|
||||||
uses: docker/login-action@v2
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKER_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
- name: Extract version from tag
|
|
||||||
id: vars
|
|
||||||
run: echo "TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Build and push Docker image
|
|
||||||
id: push
|
|
||||||
uses: docker/build-push-action@v6
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
file: ./Dockerfile
|
|
||||||
push: true
|
|
||||||
tags: lejianwen/rustdesk-api:latest, lejianwen/rustdesk-api:${{ env.TAG }}
|
|
||||||
27
.github/workflows/go.yml
vendored
@@ -1,27 +0,0 @@
|
|||||||
# This workflow will build a golang project
|
|
||||||
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go
|
|
||||||
|
|
||||||
name: Go
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
branches: [ "master" ]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Set up Go
|
|
||||||
uses: actions/setup-go@v4
|
|
||||||
with:
|
|
||||||
go-version: '1.22'
|
|
||||||
- name: mod tidy
|
|
||||||
run: go mod tidy
|
|
||||||
- name: Build
|
|
||||||
run: go build -v -o release/apimain cmd/apimain.go
|
|
||||||
|
|
||||||
- name: Test
|
|
||||||
run: go test -v cmd/apimain.go
|
|
||||||
6
.github/workflows/release.yml
vendored
@@ -1,9 +1,9 @@
|
|||||||
name: Build and Release
|
name: Build and Release
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
workflow_dispatch:
|
||||||
tags:
|
# tags:
|
||||||
- 'v*.*.*' # 当推送带有版本号的 tag(例如 v1.0.0)时触发工作流
|
# - 'v*.*.*' # 当推送带有版本号的 tag(例如 v1.0.0)时触发工作流
|
||||||
#on:
|
#on:
|
||||||
# push:
|
# push:
|
||||||
# branches: [ "master" ]
|
# branches: [ "master" ]
|
||||||
|
|||||||
36
Dockerfile
@@ -1,35 +1,11 @@
|
|||||||
FROM golang:1.22-alpine as builder
|
|
||||||
|
|
||||||
RUN set -eux; \
|
|
||||||
apk add --no-cache git gcc build-base sqlite-dev npm nodejs; \
|
|
||||||
git clone https://github.com/lejianwen/rustdesk-api-web; \
|
|
||||||
git clone https://github.com/lejianwen/rustdesk-api; \
|
|
||||||
#先编译后台
|
|
||||||
cd rustdesk-api-web; \
|
|
||||||
npm install; \
|
|
||||||
npm run build; \
|
|
||||||
cd ..; \
|
|
||||||
mkdir -p rustdesk-api/resources/admin; \
|
|
||||||
cp -ar rustdesk-api-web/dist/* rustdesk-api/resources/admin; \
|
|
||||||
cd rustdesk-api; \
|
|
||||||
go mod tidy; \
|
|
||||||
go install github.com/swaggo/swag/cmd/swag@latest; \
|
|
||||||
swag init -g cmd/apimain.go --output docs/api --instanceName api --exclude http/controller/admin; \
|
|
||||||
swag init -g cmd/apimain.go --output docs/admin --instanceName admin --exclude http/controller/api; \
|
|
||||||
go env -w GO111MODULE=on;\
|
|
||||||
CGO_LDFLAGS="-static" CGO_ENABLED=1 go build -ldflags "-s -w" -o ./release/apimain ./cmd/apimain.go; \
|
|
||||||
cp -ar resources release/; \
|
|
||||||
mkdir -p release/resources/public; \
|
|
||||||
cp -ar docs release/; \
|
|
||||||
cp -ar conf release/; \
|
|
||||||
mkdir -p release/data; \
|
|
||||||
mkdir -p release/runtime;
|
|
||||||
|
|
||||||
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 file
|
||||||
COPY --from=builder /go/rustdesk-api/release /app/
|
COPY ./${BUILDARCH}/release /app/
|
||||||
|
RUN file /app/apimain
|
||||||
|
VOLUME /app/data
|
||||||
|
|
||||||
EXPOSE 21114
|
EXPOSE 21114
|
||||||
CMD ["./apimain"]
|
CMD ["./apimain"]
|
||||||
|
|||||||
11
README.md
@@ -9,8 +9,7 @@
|
|||||||
<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://github.com/lejianwen/rustdesk-api/actions/workflows/release.yml/badge.svg"/>
|
<img src="https://github.com/lejianwen/rustdesk-api/actions/workflows/build.yml/badge.svg"/>
|
||||||
<img src="https://github.com/lejianwen/rustdesk-api/actions/workflows/docker.yml/badge.svg"/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
# 特性
|
# 特性
|
||||||
@@ -76,9 +75,7 @@
|
|||||||
|
|
||||||
### Web Admin:
|
### Web Admin:
|
||||||
|
|
||||||
**
|
***使用前后端分离,提供用户友好的管理界面,主要用来管理和展示。前端代码在[rustdesk-api-web](https://github.com/lejianwen/rustdesk-api-web)***
|
||||||
*使用前后端分离,提供用户友好的管理界面,主要用来管理和展示。前端代码在[rustdesk-api-web](https://github.com/lejianwen/rustdesk-api-web)
|
|
||||||
***
|
|
||||||
|
|
||||||
***后台访问地址是`http://<your server>[:port]/_admin/`初次安装管理员为用户名密码为`admin` `admin`,请即时更改密码***
|
***后台访问地址是`http://<your server>[:port]/_admin/`初次安装管理员为用户名密码为`admin` `admin`,请即时更改密码***
|
||||||
|
|
||||||
@@ -118,7 +115,9 @@
|
|||||||
|
|
||||||
### 相关配置
|
### 相关配置
|
||||||
|
|
||||||
* 参考`conf/config.yaml`配置文件,修改相关配置。如果`gorm.type`是`sqlite`,则不需要配置mysql相关配置。
|
* 参考`conf/config.yaml`配置文件,修改相关配置。
|
||||||
|
* 如果`gorm.type`是`sqlite`,则不需要配置mysql相关配置。
|
||||||
|
* 语言如果不设置默认为`zh-CN`
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
lang: "en"
|
lang: "en"
|
||||||
|
|||||||
27
README_EN.md
@@ -8,8 +8,7 @@ desktop software that provides self-hosted solutions.
|
|||||||
<img src="https://img.shields.io/badge/gin-v1.9.0-lightBlue"/>
|
<img src="https://img.shields.io/badge/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://github.com/lejianwen/rustdesk-api/actions/workflows/release.yml/badge.svg"/>
|
<img src="https://github.com/lejianwen/rustdesk-api/actions/workflows/build.yml/badge.svg"/>
|
||||||
<img src="https://github.com/lejianwen/rustdesk-api/actions/workflows/docker.yml/badge.svg"/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
# Features
|
# Features
|
||||||
@@ -65,15 +64,15 @@ desktop software that provides self-hosted solutions.
|
|||||||
configuration section for details.
|
configuration section for details.
|
||||||
- Added authorization login for the web admin panel.
|
- Added authorization login for the web admin panel.
|
||||||
|
|
||||||

|

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

|

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

|

|
||||||
|
|
||||||
### Web Admin
|
### Web Admin
|
||||||
|
|
||||||
@@ -84,19 +83,19 @@ displaying data.Frontend code is available at [rustdesk-api-web](https://github.
|
|||||||
installation are `admin` `admin`, please change the password immediately.***
|
installation are `admin` `admin`, please change the password immediately.***
|
||||||
|
|
||||||
1. Admin interface:
|
1. Admin interface:
|
||||||

|

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

|

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

|

|
||||||
3. Groups can be customized for easy management. Currently, two types are supported: `shared group` and `regular group`.
|
3. Groups can be customized for easy management. Currently, two types are supported: `shared group` and `regular group`.
|
||||||

|

|
||||||
4. You can open the web client directly for convenience:
|
4. You can open the web client directly for convenience:
|
||||||

|

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

|

|
||||||
- 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/oauth/callback`,
|
||||||
@@ -121,8 +120,9 @@ installation are `admin` `admin`, please change the password immediately.***
|
|||||||
|
|
||||||
### Configuration
|
### Configuration
|
||||||
|
|
||||||
* Modify the configuration in `conf/config.yaml`. If `gorm.type` is set to `sqlite`, MySQL-related configurations are
|
* Modify the configuration in `conf/config.yaml`.
|
||||||
not required.
|
* 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
|
```yaml
|
||||||
lang: "en"
|
lang: "en"
|
||||||
@@ -184,6 +184,7 @@ rustdesk:
|
|||||||
```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 RUSTDESK_API_LANG=en \
|
||||||
-e RUSTDESK_API_RUSTDESK_ID_SERVER=192.168.1.66:21116 \
|
-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_RELAY_SERVER=192.168.1.66:21117 \
|
||||||
-e RUSTDESK_API_RUSTDESK_API_SERVER=http://192.168.1.66:21114 \
|
-e RUSTDESK_API_RUSTDESK_API_SERVER=http://192.168.1.66:21114 \
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import (
|
|||||||
"github.com/go-redis/redis/v8"
|
"github.com/go-redis/redis/v8"
|
||||||
"github.com/nicksnyder/go-i18n/v2/i18n"
|
"github.com/nicksnyder/go-i18n/v2/i18n"
|
||||||
"golang.org/x/text/language"
|
"golang.org/x/text/language"
|
||||||
|
nethttp "net/http"
|
||||||
"reflect"
|
"reflect"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -47,6 +48,8 @@ func main() {
|
|||||||
ReportCaller: global.Config.Logger.ReportCaller,
|
ReportCaller: global.Config.Logger.ReportCaller,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
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,
|
||||||
@@ -103,7 +106,6 @@ func main() {
|
|||||||
//locker
|
//locker
|
||||||
global.Lock = lock.NewLocal()
|
global.Lock = lock.NewLocal()
|
||||||
|
|
||||||
InitI18n()
|
|
||||||
//gin
|
//gin
|
||||||
http.ApiInit()
|
http.ApiInit()
|
||||||
|
|
||||||
@@ -198,7 +200,7 @@ func getTranslatorForLang(lang string) ut.Translator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
func DatabaseAutoUpdate() {
|
func DatabaseAutoUpdate() {
|
||||||
version := 126
|
version := 212
|
||||||
|
|
||||||
db := global.DB
|
db := global.DB
|
||||||
|
|
||||||
@@ -268,13 +270,23 @@ func Migrate(version uint) {
|
|||||||
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(&gin.Context{
|
||||||
|
Request: &nethttp.Request{},
|
||||||
|
})
|
||||||
|
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)
|
||||||
@@ -282,7 +294,7 @@ 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,
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ const docTemplateadmin = `{
|
|||||||
"token": []
|
"token": []
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"description": "创建地址簿",
|
"description": "批量创建地址簿",
|
||||||
"consumes": [
|
"consumes": [
|
||||||
"application/json"
|
"application/json"
|
||||||
],
|
],
|
||||||
@@ -32,7 +32,7 @@ const docTemplateadmin = `{
|
|||||||
"tags": [
|
"tags": [
|
||||||
"地址簿"
|
"地址簿"
|
||||||
],
|
],
|
||||||
"summary": "创建地址簿",
|
"summary": "批量创建地址簿",
|
||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
"description": "地址簿信息",
|
"description": "地址簿信息",
|
||||||
@@ -1242,7 +1242,7 @@ const docTemplateadmin = `{
|
|||||||
"token": []
|
"token": []
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"description": "设备删除",
|
"description": "批量设备删除",
|
||||||
"consumes": [
|
"consumes": [
|
||||||
"application/json"
|
"application/json"
|
||||||
],
|
],
|
||||||
@@ -1252,15 +1252,15 @@ const docTemplateadmin = `{
|
|||||||
"tags": [
|
"tags": [
|
||||||
"设备"
|
"设备"
|
||||||
],
|
],
|
||||||
"summary": "设备删除",
|
"summary": "批量设备删除",
|
||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
"description": "设备信息",
|
"description": "设备id",
|
||||||
"name": "body",
|
"name": "body",
|
||||||
"in": "body",
|
"in": "body",
|
||||||
"required": true,
|
"required": true,
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "#/definitions/admin.PeerForm"
|
"$ref": "#/definitions/admin.PeerBatchDeleteForm"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -1365,6 +1365,12 @@ const docTemplateadmin = `{
|
|||||||
"description": "页大小",
|
"description": "页大小",
|
||||||
"name": "page_size",
|
"name": "page_size",
|
||||||
"in": "query"
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "时间",
|
||||||
|
"name": "time_ago",
|
||||||
|
"in": "query"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
@@ -2311,6 +2317,12 @@ const docTemplateadmin = `{
|
|||||||
"user_id": {
|
"user_id": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
|
"user_ids": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
},
|
||||||
"username": {
|
"username": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
@@ -2398,6 +2410,20 @@ const docTemplateadmin = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"admin.PeerBatchDeleteForm": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"row_ids"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"row_ids": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"admin.PeerForm": {
|
"admin.PeerForm": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
@@ -2759,6 +2785,9 @@ const docTemplateadmin = `{
|
|||||||
"id": {
|
"id": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"last_online_time": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
"memory": {
|
"memory": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
"token": []
|
"token": []
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"description": "创建地址簿",
|
"description": "批量创建地址簿",
|
||||||
"consumes": [
|
"consumes": [
|
||||||
"application/json"
|
"application/json"
|
||||||
],
|
],
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
"tags": [
|
"tags": [
|
||||||
"地址簿"
|
"地址簿"
|
||||||
],
|
],
|
||||||
"summary": "创建地址簿",
|
"summary": "批量创建地址簿",
|
||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
"description": "地址簿信息",
|
"description": "地址簿信息",
|
||||||
@@ -1235,7 +1235,7 @@
|
|||||||
"token": []
|
"token": []
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"description": "设备删除",
|
"description": "批量设备删除",
|
||||||
"consumes": [
|
"consumes": [
|
||||||
"application/json"
|
"application/json"
|
||||||
],
|
],
|
||||||
@@ -1245,15 +1245,15 @@
|
|||||||
"tags": [
|
"tags": [
|
||||||
"设备"
|
"设备"
|
||||||
],
|
],
|
||||||
"summary": "设备删除",
|
"summary": "批量设备删除",
|
||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
"description": "设备信息",
|
"description": "设备id",
|
||||||
"name": "body",
|
"name": "body",
|
||||||
"in": "body",
|
"in": "body",
|
||||||
"required": true,
|
"required": true,
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "#/definitions/admin.PeerForm"
|
"$ref": "#/definitions/admin.PeerBatchDeleteForm"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -1358,6 +1358,12 @@
|
|||||||
"description": "页大小",
|
"description": "页大小",
|
||||||
"name": "page_size",
|
"name": "page_size",
|
||||||
"in": "query"
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "时间",
|
||||||
|
"name": "time_ago",
|
||||||
|
"in": "query"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
@@ -2304,6 +2310,12 @@
|
|||||||
"user_id": {
|
"user_id": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
|
"user_ids": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
},
|
||||||
"username": {
|
"username": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
@@ -2391,6 +2403,20 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"admin.PeerBatchDeleteForm": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"row_ids"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"row_ids": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"admin.PeerForm": {
|
"admin.PeerForm": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
@@ -2752,6 +2778,9 @@
|
|||||||
"id": {
|
"id": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"last_online_time": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
"memory": {
|
"memory": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -46,6 +46,10 @@ definitions:
|
|||||||
type: array
|
type: array
|
||||||
user_id:
|
user_id:
|
||||||
type: integer
|
type: integer
|
||||||
|
user_ids:
|
||||||
|
items:
|
||||||
|
type: integer
|
||||||
|
type: array
|
||||||
username:
|
username:
|
||||||
type: string
|
type: string
|
||||||
required:
|
required:
|
||||||
@@ -107,6 +111,15 @@ definitions:
|
|||||||
- op
|
- op
|
||||||
- redirect_url
|
- redirect_url
|
||||||
type: object
|
type: object
|
||||||
|
admin.PeerBatchDeleteForm:
|
||||||
|
properties:
|
||||||
|
row_ids:
|
||||||
|
items:
|
||||||
|
type: integer
|
||||||
|
type: array
|
||||||
|
required:
|
||||||
|
- row_ids
|
||||||
|
type: object
|
||||||
admin.PeerForm:
|
admin.PeerForm:
|
||||||
properties:
|
properties:
|
||||||
cpu:
|
cpu:
|
||||||
@@ -346,6 +359,8 @@ definitions:
|
|||||||
type: string
|
type: string
|
||||||
id:
|
id:
|
||||||
type: string
|
type: string
|
||||||
|
last_online_time:
|
||||||
|
type: integer
|
||||||
memory:
|
memory:
|
||||||
type: string
|
type: string
|
||||||
os:
|
os:
|
||||||
@@ -471,7 +486,7 @@ paths:
|
|||||||
post:
|
post:
|
||||||
consumes:
|
consumes:
|
||||||
- application/json
|
- application/json
|
||||||
description: 创建地址簿
|
description: 批量创建地址簿
|
||||||
parameters:
|
parameters:
|
||||||
- description: 地址簿信息
|
- description: 地址簿信息
|
||||||
in: body
|
in: body
|
||||||
@@ -497,7 +512,7 @@ paths:
|
|||||||
$ref: '#/definitions/response.Response'
|
$ref: '#/definitions/response.Response'
|
||||||
security:
|
security:
|
||||||
- token: []
|
- token: []
|
||||||
summary: 创建地址簿
|
summary: 批量创建地址簿
|
||||||
tags:
|
tags:
|
||||||
- 地址簿
|
- 地址簿
|
||||||
/admin/address_book/delete:
|
/admin/address_book/delete:
|
||||||
@@ -1194,14 +1209,14 @@ paths:
|
|||||||
post:
|
post:
|
||||||
consumes:
|
consumes:
|
||||||
- application/json
|
- application/json
|
||||||
description: 设备删除
|
description: 批量设备删除
|
||||||
parameters:
|
parameters:
|
||||||
- description: 设备信息
|
- description: 设备id
|
||||||
in: body
|
in: body
|
||||||
name: body
|
name: body
|
||||||
required: true
|
required: true
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/admin.PeerForm'
|
$ref: '#/definitions/admin.PeerBatchDeleteForm'
|
||||||
produces:
|
produces:
|
||||||
- application/json
|
- application/json
|
||||||
responses:
|
responses:
|
||||||
@@ -1215,7 +1230,7 @@ paths:
|
|||||||
$ref: '#/definitions/response.Response'
|
$ref: '#/definitions/response.Response'
|
||||||
security:
|
security:
|
||||||
- token: []
|
- token: []
|
||||||
summary: 设备删除
|
summary: 批量设备删除
|
||||||
tags:
|
tags:
|
||||||
- 设备
|
- 设备
|
||||||
/admin/peer/detail/{id}:
|
/admin/peer/detail/{id}:
|
||||||
@@ -1264,6 +1279,10 @@ paths:
|
|||||||
in: query
|
in: query
|
||||||
name: page_size
|
name: page_size
|
||||||
type: integer
|
type: integer
|
||||||
|
- description: 时间
|
||||||
|
in: query
|
||||||
|
name: time_ago
|
||||||
|
type: integer
|
||||||
produces:
|
produces:
|
||||||
- application/json
|
- application/json
|
||||||
responses:
|
responses:
|
||||||
|
|||||||
BIN
docs/en_img/admin_webclient.png
Normal file
|
After Width: | Height: | Size: 64 KiB |
BIN
docs/en_img/pc_ab.png
Normal file
|
After Width: | Height: | Size: 53 KiB |
BIN
docs/en_img/pc_gr.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
docs/en_img/pc_login.png
Normal file
|
After Width: | Height: | Size: 46 KiB |
BIN
docs/en_img/web_admin.png
Normal file
|
After Width: | Height: | Size: 29 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: 12 KiB |
BIN
docs/en_img/web_admin_user.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
docs/en_img/web_resetpwd.png
Normal file
|
After Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 4.3 KiB |
@@ -4,6 +4,7 @@ import (
|
|||||||
"Gwen/global"
|
"Gwen/global"
|
||||||
"Gwen/http/request/admin"
|
"Gwen/http/request/admin"
|
||||||
"Gwen/http/response"
|
"Gwen/http/response"
|
||||||
|
"Gwen/model"
|
||||||
"Gwen/service"
|
"Gwen/service"
|
||||||
_ "encoding/json"
|
_ "encoding/json"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
@@ -69,6 +70,12 @@ func (ct *AddressBook) Create(c *gin.Context) {
|
|||||||
if !service.AllService.UserService.IsAdmin(u) || t.UserId == 0 {
|
if !service.AllService.UserService.IsAdmin(u) || t.UserId == 0 {
|
||||||
t.UserId = u.Id
|
t.UserId = u.Id
|
||||||
}
|
}
|
||||||
|
ex := service.AllService.AddressBookService.InfoByUserIdAndId(t.UserId, t.Id)
|
||||||
|
if ex.RowId > 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemExist"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
err := service.AllService.AddressBookService.Create(t)
|
err := service.AllService.AddressBookService.Create(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
@@ -77,6 +84,58 @@ func (ct *AddressBook) Create(c *gin.Context) {
|
|||||||
response.Success(c, u)
|
response.Success(c, u)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BatchCreate 批量创建地址簿
|
||||||
|
// @Tags 地址簿
|
||||||
|
// @Summary 批量创建地址簿
|
||||||
|
// @Description 批量创建地址簿
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param body body admin.AddressBookForm true "地址簿信息"
|
||||||
|
// @Success 200 {object} response.Response{data=model.AddressBook}
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/address_book/create [post]
|
||||||
|
// @Security token
|
||||||
|
func (ct *AddressBook) BatchCreate(c *gin.Context) {
|
||||||
|
f := &admin.AddressBookForm{}
|
||||||
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
errList := global.Validator.ValidStruct(c, f)
|
||||||
|
if len(errList) > 0 {
|
||||||
|
response.Fail(c, 101, errList[0])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//创建标签
|
||||||
|
for _, fu := range f.UserIds {
|
||||||
|
if fu == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, ft := range f.Tags {
|
||||||
|
exTag := service.AllService.TagService.InfoByUserIdAndName(fu, ft)
|
||||||
|
if exTag.Id == 0 {
|
||||||
|
service.AllService.TagService.Create(&model.Tag{
|
||||||
|
UserId: fu,
|
||||||
|
Name: ft,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ts := f.ToAddressBooks()
|
||||||
|
for _, t := range ts {
|
||||||
|
if t.UserId == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ex := service.AllService.AddressBookService.InfoByUserIdAndId(t.UserId, t.Id)
|
||||||
|
if ex.RowId == 0 {
|
||||||
|
service.AllService.AddressBookService.Create(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
response.Success(c, nil)
|
||||||
|
}
|
||||||
|
|
||||||
// List 列表
|
// List 列表
|
||||||
// @Tags 地址簿
|
// @Tags 地址簿
|
||||||
// @Summary 地址簿列表
|
// @Summary 地址簿列表
|
||||||
@@ -102,9 +161,18 @@ func (ct *AddressBook) List(c *gin.Context) {
|
|||||||
query.UserId = int(u.Id)
|
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) {
|
||||||
|
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+"%")
|
||||||
|
}
|
||||||
})
|
})
|
||||||
response.Success(c, res)
|
response.Success(c, res)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ import (
|
|||||||
"Gwen/http/response"
|
"Gwen/http/response"
|
||||||
"Gwen/service"
|
"Gwen/service"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"gorm.io/gorm"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Peer struct {
|
type Peer struct {
|
||||||
@@ -74,17 +76,27 @@ func (ct *Peer) Create(c *gin.Context) {
|
|||||||
// @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 "时间"
|
||||||
// @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.TranslateMsg(c, "ParamsError")+err.Error())
|
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)
|
||||||
|
}
|
||||||
|
})
|
||||||
response.Success(c, res)
|
response.Success(c, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,3 +170,32 @@ func (ct *Peer) Delete(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BatchDelete 批量删除
|
||||||
|
// @Tags 设备
|
||||||
|
// @Summary 批量设备删除
|
||||||
|
// @Description 批量设备删除
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param body body admin.PeerBatchDeleteForm true "设备id"
|
||||||
|
// @Success 200 {object} response.Response
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/peer/delete [post]
|
||||||
|
// @Security token
|
||||||
|
func (ct *Peer) BatchDelete(c *gin.Context) {
|
||||||
|
f := &admin.PeerBatchDeleteForm{}
|
||||||
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(f.RowIds) == 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err := service.AllService.PeerService.BatchDelete(f.RowIds)
|
||||||
|
if err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Success(c, nil)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
requstform "Gwen/http/request/api"
|
||||||
"Gwen/http/response"
|
"Gwen/http/response"
|
||||||
|
"Gwen/service"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Index struct {
|
type Index struct {
|
||||||
@@ -35,10 +38,22 @@ 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) {
|
||||||
//b := &gin.H{}
|
info := &requstform.PeerInfoInHeartbeat{}
|
||||||
//err := c.BindJSON(b)
|
err := c.ShouldBindJSON(info)
|
||||||
//body : &map[id:xxx modified_at:0 uuid:NGIxZTZjM2YtNmNkMy00YTMwLWFiNjQtMzQ0MTA0NGE5ZDgz ver:1.003e+06]
|
if err != nil {
|
||||||
//fmt.Println(b, err, c.Request.Header)
|
c.JSON(http.StatusOK, gin.H{})
|
||||||
//header : map[Accept:[*/*] Accept-Encoding:[gzip] Content-Length:[105] Content-Type:[application/json]]
|
return
|
||||||
|
}
|
||||||
|
if info.Uuid == "" {
|
||||||
|
c.JSON(http.StatusOK, gin.H{})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
peer := service.AllService.PeerService.FindByUuid(info.Uuid)
|
||||||
|
if peer == nil {
|
||||||
|
c.JSON(http.StatusOK, gin.H{})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
peer.LastOnlineTime = time.Now().Unix()
|
||||||
|
service.AllService.PeerService.Update(peer)
|
||||||
c.JSON(http.StatusOK, gin.H{})
|
c.JSON(http.StatusOK, gin.H{})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ type AddressBookForm struct {
|
|||||||
Tags []string `json:"tags"`
|
Tags []string `json:"tags"`
|
||||||
Hash string `json:"hash"`
|
Hash string `json:"hash"`
|
||||||
UserId uint `json:"user_id"`
|
UserId uint `json:"user_id"`
|
||||||
|
UserIds []uint `json:"user_ids"`
|
||||||
ForceAlwaysRelay bool `json:"forceAlwaysRelay"`
|
ForceAlwaysRelay bool `json:"forceAlwaysRelay"`
|
||||||
RdpPort string `json:"rdpPort"`
|
RdpPort string `json:"rdpPort"`
|
||||||
RdpUsername string `json:"rdpUsername"`
|
RdpUsername string `json:"rdpUsername"`
|
||||||
@@ -48,9 +49,39 @@ func (a AddressBookForm) ToAddressBook() *model.AddressBook {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
func (a AddressBookForm) ToAddressBooks() []*model.AddressBook {
|
||||||
|
//tags转换
|
||||||
|
tags, _ := json.Marshal(a.Tags)
|
||||||
|
|
||||||
|
abs := make([]*model.AddressBook, 0, len(a.UserIds))
|
||||||
|
for _, userId := range a.UserIds {
|
||||||
|
abs = append(abs, &model.AddressBook{
|
||||||
|
RowId: a.RowId,
|
||||||
|
Id: a.Id,
|
||||||
|
Username: a.Username,
|
||||||
|
Password: a.Password,
|
||||||
|
Hostname: a.Hostname,
|
||||||
|
Alias: a.Alias,
|
||||||
|
Platform: a.Platform,
|
||||||
|
Tags: tags,
|
||||||
|
Hash: a.Hash,
|
||||||
|
UserId: userId,
|
||||||
|
ForceAlwaysRelay: a.ForceAlwaysRelay,
|
||||||
|
RdpPort: a.RdpPort,
|
||||||
|
RdpUsername: a.RdpUsername,
|
||||||
|
Online: a.Online,
|
||||||
|
LoginName: a.LoginName,
|
||||||
|
SameServer: a.SameServer,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return abs
|
||||||
|
}
|
||||||
|
|
||||||
type AddressBookQuery struct {
|
type AddressBookQuery struct {
|
||||||
UserId int `form:"user_id"`
|
UserId int `form:"user_id"`
|
||||||
IsMy int `form:"is_my"`
|
IsMy int `form:"is_my"`
|
||||||
|
Username string `form:"username"`
|
||||||
|
Hostname string `form:"hostname"`
|
||||||
|
Id string `form:"id"`
|
||||||
PageQuery
|
PageQuery
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,10 @@ type PeerForm struct {
|
|||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PeerBatchDeleteForm struct {
|
||||||
|
RowIds []uint `json:"row_ids" validate:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
// ToPeer
|
// ToPeer
|
||||||
func (f *PeerForm) ToPeer() *model.Peer {
|
func (f *PeerForm) ToPeer() *model.Peer {
|
||||||
return &model.Peer{
|
return &model.Peer{
|
||||||
@@ -28,3 +32,8 @@ func (f *PeerForm) ToPeer() *model.Peer {
|
|||||||
Version: f.Version,
|
Version: f.Version,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PeerQuery struct {
|
||||||
|
PageQuery
|
||||||
|
TimeAgo int `json:"time_ago" form:"time_ago"`
|
||||||
|
}
|
||||||
|
|||||||
@@ -71,3 +71,9 @@ type TagColorForm struct {
|
|||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Color uint `json:"color"`
|
Color uint `json:"color"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PeerInfoInHeartbeat struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
Uuid string `json:"uuid"`
|
||||||
|
Ver int `json:"ver"`
|
||||||
|
}
|
||||||
|
|||||||
@@ -93,6 +93,9 @@ func AddressBookBind(rg *gin.RouterGroup) {
|
|||||||
aR.POST("/create", cont.Create)
|
aR.POST("/create", cont.Create)
|
||||||
aR.POST("/update", cont.Update)
|
aR.POST("/update", cont.Update)
|
||||||
aR.POST("/delete", cont.Delete)
|
aR.POST("/delete", cont.Delete)
|
||||||
|
|
||||||
|
arp := aR.Use(middleware.AdminPrivilege())
|
||||||
|
arp.POST("/batchCreate", cont.BatchCreate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
func PeerBind(rg *gin.RouterGroup) {
|
func PeerBind(rg *gin.RouterGroup) {
|
||||||
@@ -104,6 +107,9 @@ func PeerBind(rg *gin.RouterGroup) {
|
|||||||
aR.POST("/create", cont.Create)
|
aR.POST("/create", cont.Create)
|
||||||
aR.POST("/update", cont.Update)
|
aR.POST("/update", cont.Update)
|
||||||
aR.POST("/delete", cont.Delete)
|
aR.POST("/delete", cont.Delete)
|
||||||
|
|
||||||
|
arp := aR.Use(middleware.AdminPrivilege())
|
||||||
|
arp.POST("/batchDelete", cont.BatchDelete)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,18 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
type Peer struct {
|
type Peer struct {
|
||||||
RowId uint `json:"row_id" gorm:"primaryKey;"`
|
RowId uint `json:"row_id" gorm:"primaryKey;"`
|
||||||
Id string `json:"id" gorm:"default:'';not null;index"`
|
Id string `json:"id" gorm:"default:'';not null;index"`
|
||||||
Cpu string `json:"cpu" gorm:"default:'';not null;"`
|
Cpu string `json:"cpu" gorm:"default:'';not null;"`
|
||||||
Hostname string `json:"hostname" gorm:"default:'';not null;"`
|
Hostname string `json:"hostname" gorm:"default:'';not null;"`
|
||||||
Memory string `json:"memory" gorm:"default:'';not null;"`
|
Memory string `json:"memory" gorm:"default:'';not null;"`
|
||||||
Os string `json:"os" gorm:"default:'';not null;"`
|
Os string `json:"os" gorm:"default:'';not null;"`
|
||||||
Username string `json:"username" gorm:"default:'';not null;"`
|
Username string `json:"username" gorm:"default:'';not null;"`
|
||||||
Uuid string `json:"uuid" gorm:"default:'';not null;index"`
|
Uuid string `json:"uuid" gorm:"default:'';not null;index"`
|
||||||
Version string `json:"version" gorm:"default:'';not null;"`
|
Version string `json:"version" gorm:"default:'';not null;"`
|
||||||
UserId uint `json:"user_id" gorm:"default:0;not null;index"`
|
UserId uint `json:"user_id" gorm:"default:0;not null;index"`
|
||||||
User User `json:"user,omitempty" gorm:""`
|
User User `json:"user,omitempty" gorm:""`
|
||||||
|
LastOnlineTime int64 `json:"last_online_time" gorm:"default:0;not null;"`
|
||||||
TimeModel
|
TimeModel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -109,3 +109,13 @@ other = "Decode oauth user info error."
|
|||||||
description = "Old password error."
|
description = "Old password error."
|
||||||
one = "Old password error."
|
one = "Old password error."
|
||||||
other = "Old password error."
|
other = "Old password error."
|
||||||
|
|
||||||
|
[DefaultGroup]
|
||||||
|
description = "Default group"
|
||||||
|
one = "Default Group"
|
||||||
|
other = "Default Group"
|
||||||
|
|
||||||
|
[ShareGroup]
|
||||||
|
description = "Share group"
|
||||||
|
one = "Share Group"
|
||||||
|
other = "Share Group"
|
||||||
|
|||||||
@@ -110,3 +110,14 @@ other = "解析授权用户信息失败。"
|
|||||||
description = "Old password error."
|
description = "Old password error."
|
||||||
one = "旧密码错误。"
|
one = "旧密码错误。"
|
||||||
other = "旧密码错误。"
|
other = "旧密码错误。"
|
||||||
|
|
||||||
|
|
||||||
|
[DefaultGroup]
|
||||||
|
description = "Default group."
|
||||||
|
one = "默认组"
|
||||||
|
other = "默认组"
|
||||||
|
|
||||||
|
[ShareGroup]
|
||||||
|
description = "Share group."
|
||||||
|
one = "共享组"
|
||||||
|
other = "共享组"
|
||||||
|
|||||||
@@ -71,6 +71,11 @@ func (ps *PeerService) Delete(u *model.Peer) error {
|
|||||||
return global.DB.Delete(u).Error
|
return global.DB.Delete(u).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BatchDelete
|
||||||
|
func (ps *PeerService) BatchDelete(ids []uint) error {
|
||||||
|
return global.DB.Where("row_id in (?)", ids).Delete(&model.Peer{}).Error
|
||||||
|
}
|
||||||
|
|
||||||
// Update 更新
|
// Update 更新
|
||||||
func (ps *PeerService) Update(u *model.Peer) error {
|
func (ps *PeerService) Update(u *model.Peer) error {
|
||||||
return global.DB.Model(u).Updates(u).Error
|
return global.DB.Model(u).Updates(u).Error
|
||||||
|
|||||||