Compare commits

...

10 Commits
v2.4.5 ... test

Author SHA1 Message Date
ljw
bb8a936ade add build_test.yml 2024-11-08 16:09:50 +08:00
ljw
61044fd30b add build_test.yml 2024-11-08 16:05:43 +08:00
ljw
22a4546d0f add cmd 2024-11-08 15:54:49 +08:00
ljw
07450416ed fix #52 & add auto refresh token #53 2024-11-07 10:46:00 +08:00
0d6db0d2a1 Merge pull request #52 from IamTaoChen/fix/bug
fix: cannot delete user
2024-11-07 10:21:50 +08:00
Tao Chen
ab30b3407b add error information 2024-11-06 15:06:15 +08:00
Tao Chen
7a4c735803 fix: cannot delete user 2024-11-06 14:36:12 +08:00
ljw
654c764019 fix migrate 2024-11-05 21:07:39 +08:00
ljw
7101139250 fix migrate 2024-11-05 21:07:31 +08:00
ljw
793614841a fix migrate 2024-11-05 21:03:32 +08:00
15 changed files with 701 additions and 147 deletions

271
.github/workflows/build_test.yml vendored Normal file
View File

@@ -0,0 +1,271 @@
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
- name: Set up Go environment
uses: actions/setup-go@v4
with:
go-version: '1.22' # 选择 Go 版本
- name: Set up npm
uses: actions/setup-node@v2
with:
node-version: '20'
- name: build rustdesk-api-web
run: |
git clone ${{ env.WEBCLIENT_SOURCE_LOCATION }}
cd rustdesk-api-web
npm install
npm run build
mkdir ../resources/admin/ -p
cp -ar dist/* ../resources/admin/
- name: tidy
run: go mod tidy
- name: swag
run: |
go install github.com/swaggo/swag/cmd/swag@latest
swag init -g cmd/apimain.go --output docs/api --instanceName api --exclude http/controller/admin
swag init -g cmd/apimain.go --output docs/admin --instanceName admin --exclude http/controller/api
- name: Build for ${{ matrix.job.goos }}-${{ matrix.job.platform }}
run: |
mkdir release -p
cp -ar resources release/
cp -ar docs release/
cp -ar conf release/
mkdir -p release/data
mkdir -p release/runtime
if [ "${{ matrix.job.goos }}" = "windows" ]; then
sudo apt-get install gcc-mingw-w64-x86-64 zip -y
GOOS=${{ matrix.job.goos }} GOARCH=${{ matrix.job.platform }} CC=x86_64-w64-mingw32-gcc CGO_LDFLAGS="-static" CGO_ENABLED=1 go build -ldflags "-s -w" -o ./release/apimain.exe ./cmd/apimain.go
zip -r ${{ matrix.job.goos}}-${{ matrix.job.platform }}.${{matrix.job.file_ext}} ./release
else
if [ "${{ matrix.job.platform }}" = "arm64" ]; then
wget https://musl.cc/aarch64-linux-musl-cross.tgz
tar -xf aarch64-linux-musl-cross.tgz
export PATH=$PATH:$PWD/aarch64-linux-musl-cross/bin
GOOS=${{ matrix.job.goos }} GOARCH=${{ matrix.job.platform }} CC=aarch64-linux-musl-gcc CGO_LDFLAGS="-static" CGO_ENABLED=1 go build -ldflags "-s -w" -o ./release/apimain ./cmd/apimain.go
elif [ "${{ matrix.job.platform }}" = "armv7l" ]; then
wget https://musl.cc/armv7l-linux-musleabihf-cross.tgz
tar -xf armv7l-linux-musleabihf-cross.tgz
export PATH=$PATH:$PWD/armv7l-linux-musleabihf-cross/bin
GOOS=${{ matrix.job.goos }} GOARCH=arm GOARM=7 CC=armv7l-linux-musleabihf-gcc CGO_LDFLAGS="-static" CGO_ENABLED=1 go build -ldflags "-s -w" -o ./release/apimain ./cmd/apimain.go
else
sudo apt-get install musl musl-dev musl-tools -y
GOOS=${{ matrix.job.goos }} GOARCH=${{ matrix.job.platform }} CC=musl-gcc CGO_LDFLAGS="-static" CGO_ENABLED=1 go build -ldflags "-s -w" -o ./release/apimain ./cmd/apimain.go
fi
tar -czf ${{ matrix.job.goos}}-${{ matrix.job.platform }}.${{matrix.job.file_ext}} ./release
fi
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: rustdesk-api-${{ matrix.job.goos }}-${{ matrix.job.platform }}
path: |
${{ matrix.job.goos}}-${{ matrix.job.platform }}.${{matrix.job.file_ext}}
- name: Upload to GitHub Release
uses: softprops/action-gh-release@v2
with:
files: |
${{ matrix.job.goos}}-${{ matrix.job.platform }}.${{matrix.job.file_ext}}
tag_name: test
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
docker:
name: Push Docker Image
needs: build
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
job:
- { platform: "amd64", goos: "linux", docker_platform: "linux/amd64" }
- { platform: "arm64", goos: "linux", docker_platform: "linux/arm64" }
- { platform: "armv7l", goos: "linux", docker_platform: "linux/arm/v7" }
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Log in to Docker Hub
if: ${{ env.SKIP_DOCKER_HUB == 'false' }} # Only log in if SKIP_DOCKER_HUB is false
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
- name: Log in to GitHub Container Registry
if: ${{ env.SKIP_GHCR == 'false' }} # Only log in if GHCR push is enabled
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract version from tag
id: vars
run: |
if [[ "${GITHUB_REF}" == refs/tags/* ]]; then
echo "TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
else
echo "TAG=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 }}
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.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@master
with:
base-image: ${{ env.BASE_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}
extra-images: ${{ env.DOCKERHUB_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}-amd64,
${{ env.DOCKERHUB_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}-armv7l,
${{ env.DOCKERHUB_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}-arm64
push: true
- name: Create and push manifest GHCR (:version)
if: ${{ env.SKIP_GHCR == 'false' }}
uses: Noelware/docker-manifest-action@master
with:
base-image: ghcr.io/${{ env.BASE_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}
extra-images: ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}-amd64,
ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}-armv7l,
ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}-arm64
push: true
amend: true

View File

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

View File

@@ -14,6 +14,9 @@ import (
"fmt"
"github.com/go-redis/redis/v8"
"github.com/nicksnyder/go-i18n/v2/i18n"
"github.com/spf13/cobra"
"os"
"strconv"
)
// @title 管理系统API
@@ -26,9 +29,79 @@ import (
// @securitydefinitions.apikey BearerAuth
// @in header
// @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) {
//gin
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)
err := service.AllService.UserService.UpdatePassword(admin, pwd)
if err != nil {
fmt.Printf("reset password fail! %v \n", err)
return
}
fmt.Printf("reset password success! \n")
},
}
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 {
fmt.Printf("userId must be int! \n")
return
}
if uid <= 0 {
fmt.Printf("userId must be greater than 0! \n")
return
}
u := service.AllService.UserService.InfoById(uint(uid))
err = service.AllService.UserService.UpdatePassword(u, pwd)
if err != nil {
fmt.Printf("reset password fail! %v \n", err)
return
}
fmt.Printf("reset password success! \n")
},
}
func init() {
rootCmd.PersistentFlags().StringVarP(&global.ConfigPath, "config", "c", "./conf/config.yaml", "choose config file")
rootCmd.AddCommand(resetPwdCmd, resetUserPwdCmd)
}
func main() {
if err := rootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}
func InitGlobal() {
//配置解析
global.Viper = config.Init(&global.Config)
global.Viper = config.Init(&global.Config, global.ConfigPath)
//从配置文件中加载密钥
config.LoadKeyFile(&global.Config.Rustdesk)
//日志
global.Logger = logger.New(&logger.Config{
@@ -94,14 +167,9 @@ func main() {
//locker
global.Lock = lock.NewLocal()
//gin
http.ApiInit()
}
func DatabaseAutoUpdate() {
version := 245
version := 246
db := global.DB
@@ -150,7 +218,7 @@ func DatabaseAutoUpdate() {
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' and issuer = ''")
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)
@@ -161,6 +229,9 @@ func DatabaseAutoUpdate() {
}
}
}
if v.Version < 246 {
db.Exec("update oauths set issuer = 'https://accounts.google.com' where op = 'google' and issuer is null")
}
}
}

View File

@@ -1,7 +1,6 @@
package config
import (
"flag"
"fmt"
"github.com/fsnotify/fsnotify"
"github.com/spf13/viper"
@@ -35,18 +34,15 @@ type Config struct {
}
// Init 初始化配置
func Init(rowVal interface{}) *viper.Viper {
var config string
flag.StringVar(&config, "c", "", "choose config file.")
flag.Parse()
if config == "" { // 优先级: 命令行 > 默认值
config = DefaultConfig
func Init(rowVal interface{}, path string) *viper.Viper {
if path == "" {
path = DefaultConfig
}
v := viper.New()
v := viper.GetViper()
v.AutomaticEnv()
v.SetEnvKeyReplacer(strings.NewReplacer(".", "_", "-", "_"))
v.SetEnvPrefix("RUSTDESK_API")
v.SetConfigFile(config)
v.SetConfigFile(path)
v.SetConfigType("yaml")
err := v.ReadInConfig()
if err != nil {
@@ -63,7 +59,7 @@ func Init(rowVal interface{}) *viper.Viper {
if err := v.Unmarshal(rowVal); err != nil {
fmt.Println(err)
}
LoadKeyFile(&rowVal.(*Config).Rustdesk)
return v
}

View File

@@ -3067,6 +3067,90 @@ const docTemplateadmin = `{
}
}
},
"/admin/user/myPeer": {
"get": {
"security": [
{
"token": []
}
],
"description": "设备列表",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"设备"
],
"summary": "设备列表",
"parameters": [
{
"type": "integer",
"description": "页码",
"name": "page",
"in": "query"
},
{
"type": "integer",
"description": "页大小",
"name": "page_size",
"in": "query"
},
{
"type": "integer",
"description": "时间",
"name": "time_ago",
"in": "query"
},
{
"type": "string",
"description": "ID",
"name": "id",
"in": "query"
},
{
"type": "string",
"description": "主机名",
"name": "hostname",
"in": "query"
},
{
"type": "string",
"description": "uuids 用逗号分隔",
"name": "uuids",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/response.Response"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/model.PeerList"
}
}
}
]
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/admin/user/update": {
"post": {
"security": [
@@ -3407,6 +3491,12 @@ const docTemplateadmin = `{
"admin.LoginPayload": {
"type": "object",
"properties": {
"avatar": {
"type": "string"
},
"email": {
"type": "string"
},
"nickname": {
"type": "string"
},
@@ -3429,7 +3519,7 @@ const docTemplateadmin = `{
"required": [
"client_id",
"client_secret",
"op",
"oauth_type",
"redirect_url"
],
"properties": {
@@ -3448,6 +3538,9 @@ const docTemplateadmin = `{
"issuer": {
"type": "string"
},
"oauth_type": {
"type": "string"
},
"op": {
"type": "string"
},
@@ -3567,6 +3660,10 @@ const docTemplateadmin = `{
"avatar": {
"type": "string"
},
"email": {
"description": "validate:\"required,email\" email不强制",
"type": "string"
},
"group_id": {
"type": "integer"
},
@@ -3591,18 +3688,18 @@ const docTemplateadmin = `{
"username": {
"type": "string",
"maxLength": 10,
"minLength": 4
"minLength": 2
}
}
},
"admin.UserOauthItem": {
"type": "object",
"properties": {
"op": {
"type": "string"
},
"status": {
"type": "integer"
},
"third_type": {
"type": "string"
}
}
},
@@ -3973,6 +4070,9 @@ const docTemplateadmin = `{
"created_at": {
"type": "string"
},
"device_id": {
"type": "string"
},
"id": {
"type": "integer"
},
@@ -4042,6 +4142,9 @@ const docTemplateadmin = `{
"issuer": {
"type": "string"
},
"oauth_type": {
"type": "string"
},
"op": {
"type": "string"
},
@@ -4220,6 +4323,9 @@ const docTemplateadmin = `{
"created_at": {
"type": "string"
},
"email": {
"type": "string"
},
"group_id": {
"type": "integer"
},
@@ -4269,6 +4375,12 @@ const docTemplateadmin = `{
"created_at": {
"type": "string"
},
"device_id": {
"type": "string"
},
"device_uuid": {
"type": "string"
},
"expired_at": {
"type": "integer"
},

View File

@@ -3060,6 +3060,90 @@
}
}
},
"/admin/user/myPeer": {
"get": {
"security": [
{
"token": []
}
],
"description": "设备列表",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"设备"
],
"summary": "设备列表",
"parameters": [
{
"type": "integer",
"description": "页码",
"name": "page",
"in": "query"
},
{
"type": "integer",
"description": "页大小",
"name": "page_size",
"in": "query"
},
{
"type": "integer",
"description": "时间",
"name": "time_ago",
"in": "query"
},
{
"type": "string",
"description": "ID",
"name": "id",
"in": "query"
},
{
"type": "string",
"description": "主机名",
"name": "hostname",
"in": "query"
},
{
"type": "string",
"description": "uuids 用逗号分隔",
"name": "uuids",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/response.Response"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/model.PeerList"
}
}
}
]
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/admin/user/update": {
"post": {
"security": [
@@ -3400,6 +3484,12 @@
"admin.LoginPayload": {
"type": "object",
"properties": {
"avatar": {
"type": "string"
},
"email": {
"type": "string"
},
"nickname": {
"type": "string"
},
@@ -3422,7 +3512,7 @@
"required": [
"client_id",
"client_secret",
"op",
"oauth_type",
"redirect_url"
],
"properties": {
@@ -3441,6 +3531,9 @@
"issuer": {
"type": "string"
},
"oauth_type": {
"type": "string"
},
"op": {
"type": "string"
},
@@ -3560,6 +3653,10 @@
"avatar": {
"type": "string"
},
"email": {
"description": "validate:\"required,email\" email不强制",
"type": "string"
},
"group_id": {
"type": "integer"
},
@@ -3584,18 +3681,18 @@
"username": {
"type": "string",
"maxLength": 10,
"minLength": 4
"minLength": 2
}
}
},
"admin.UserOauthItem": {
"type": "object",
"properties": {
"op": {
"type": "string"
},
"status": {
"type": "integer"
},
"third_type": {
"type": "string"
}
}
},
@@ -3966,6 +4063,9 @@
"created_at": {
"type": "string"
},
"device_id": {
"type": "string"
},
"id": {
"type": "integer"
},
@@ -4035,6 +4135,9 @@
"issuer": {
"type": "string"
},
"oauth_type": {
"type": "string"
},
"op": {
"type": "string"
},
@@ -4213,6 +4316,9 @@
"created_at": {
"type": "string"
},
"email": {
"type": "string"
},
"group_id": {
"type": "integer"
},
@@ -4262,6 +4368,12 @@
"created_at": {
"type": "string"
},
"device_id": {
"type": "string"
},
"device_uuid": {
"type": "string"
},
"expired_at": {
"type": "integer"
},

View File

@@ -84,6 +84,10 @@ definitions:
type: object
admin.LoginPayload:
properties:
avatar:
type: string
email:
type: string
nickname:
type: string
route_names:
@@ -107,6 +111,8 @@ definitions:
type: integer
issuer:
type: string
oauth_type:
type: string
op:
type: string
redirect_url:
@@ -116,7 +122,7 @@ definitions:
required:
- client_id
- client_secret
- op
- oauth_type
- redirect_url
type: object
admin.PeerBatchDeleteForm:
@@ -188,6 +194,9 @@ definitions:
properties:
avatar:
type: string
email:
description: validate:"required,email" email不强制
type: string
group_id:
type: integer
id:
@@ -203,7 +212,7 @@ definitions:
minimum: 0
username:
maxLength: 10
minLength: 4
minLength: 2
type: string
required:
- group_id
@@ -212,10 +221,10 @@ definitions:
type: object
admin.UserOauthItem:
properties:
op:
type: string
status:
type: integer
third_type:
type: string
type: object
admin.UserPasswordForm:
properties:
@@ -462,6 +471,8 @@ definitions:
type: string
created_at:
type: string
device_id:
type: string
id:
type: integer
ip:
@@ -508,6 +519,8 @@ definitions:
type: integer
issuer:
type: string
oauth_type:
type: string
op:
type: string
redirect_url:
@@ -627,6 +640,8 @@ definitions:
type: string
created_at:
type: string
email:
type: string
group_id:
type: integer
id:
@@ -659,6 +674,10 @@ definitions:
properties:
created_at:
type: string
device_id:
type: string
device_uuid:
type: string
expired_at:
type: integer
id:
@@ -2519,6 +2538,57 @@ paths:
summary: 我的授权
tags:
- 用户
/admin/user/myPeer:
get:
consumes:
- application/json
description: 设备列表
parameters:
- description: 页码
in: query
name: page
type: integer
- description: 页大小
in: query
name: page_size
type: integer
- description: 时间
in: query
name: time_ago
type: integer
- description: ID
in: query
name: id
type: string
- description: 主机名
in: query
name: hostname
type: string
- description: uuids 用逗号分隔
in: query
name: uuids
type: string
produces:
- application/json
responses:
"200":
description: OK
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
data:
$ref: '#/definitions/model.PeerList'
type: object
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.Response'
security:
- token: []
summary: 设备列表
tags:
- 设备
/admin/user/update:
post:
consumes:

View File

@@ -1365,7 +1365,7 @@ const docTemplateapi = `{
"username": {
"type": "string",
"maxLength": 10,
"minLength": 4
"minLength": 2
},
"uuid": {
"type": "string"

View File

@@ -1358,7 +1358,7 @@
"username": {
"type": "string",
"maxLength": 10,
"minLength": 4
"minLength": 2
},
"uuid": {
"type": "string"

View File

@@ -69,7 +69,7 @@ definitions:
type: string
username:
maxLength: 10
minLength: 4
minLength: 2
type: string
uuid:
type: string

View File

@@ -17,13 +17,14 @@ import (
)
var (
DB *gorm.DB
Logger *logrus.Logger
Config config.Config
Viper *viper.Viper
Redis *redis.Client
Cache cache.Handler
Validator struct {
DB *gorm.DB
Logger *logrus.Logger
ConfigPath string = ""
Config config.Config
Viper *viper.Viper
Redis *redis.Client
Cache cache.Handler
Validator struct {
Validate *validator.Validate
UT *ut.UniversalTranslator
VTrans ut.Translator

3
go.mod
View File

@@ -16,6 +16,7 @@ require (
github.com/google/uuid v1.1.2
github.com/nicksnyder/go-i18n/v2 v2.4.0
github.com/sirupsen/logrus v1.8.1
github.com/spf13/cobra v1.8.1
github.com/spf13/viper v1.9.0
github.com/swaggo/files v1.0.1
github.com/swaggo/gin-swagger v1.6.0
@@ -28,7 +29,6 @@ require (
)
require (
cloud.google.com/go/compute/metadata v0.5.1 // indirect
github.com/KyleBanks/depth v1.2.1 // indirect
github.com/PuerkitoBio/purell v1.1.1 // indirect
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
@@ -44,6 +44,7 @@ require (
github.com/go-sql-driver/mysql v1.7.0 // indirect
github.com/goccy/go-json v0.10.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/josharian/intern v1.0.0 // indirect

View File

@@ -17,7 +17,7 @@ func AdminAuth() gin.HandlerFunc {
c.Abort()
return
}
user := service.AllService.UserService.InfoByAccessToken(token)
user, ut := service.AllService.UserService.InfoByAccessToken(token)
if user.Id == 0 {
response.Fail(c, 403, "请先登录")
c.Abort()
@@ -26,6 +26,8 @@ func AdminAuth() gin.HandlerFunc {
c.Set("curUser", user)
c.Set("token", token)
//如果时间小于1天,token自动续期
service.AllService.UserService.AutoRefreshAccessToken(ut)
c.Next()
}

View File

@@ -28,7 +28,7 @@ func RustAuth() gin.HandlerFunc {
//这里只是简单的提取
token = token[7:]
//验证token
user := service.AllService.UserService.InfoByAccessToken(token)
user, ut := service.AllService.UserService.InfoByAccessToken(token)
if user.Id == 0 {
c.JSON(401, gin.H{
"error": "Unauthorized",
@@ -46,6 +46,9 @@ func RustAuth() gin.HandlerFunc {
c.Set("curUser", user)
c.Set("token", token)
service.AllService.UserService.AutoRefreshAccessToken(ut)
c.Next()
}
}

View File

@@ -53,18 +53,18 @@ func (us *UserService) InfoByUsernamePassword(username, password string) *model.
}
// InfoByAccesstoken 根据accesstoken取用户信息
func (us *UserService) InfoByAccessToken(token string) *model.User {
func (us *UserService) InfoByAccessToken(token string) (*model.User, *model.UserToken) {
u := &model.User{}
ut := &model.UserToken{}
global.DB.Where("token = ?", token).First(ut)
if ut.Id == 0 {
return u
return u, ut
}
if ut.ExpiredAt < time.Now().Unix() {
return u
return u, ut
}
global.DB.Where("id = ?", ut.UserId).First(u)
return u
return u, ut
}
// GenerateToken 生成token
@@ -215,12 +215,12 @@ func (us *UserService) Delete(u *model.User) error {
tx.Rollback()
return err
}
tx.Commit()
// 删除关联的peer
if err := AllService.PeerService.EraseUserId(u.Id); err != nil {
tx.Rollback()
return err
global.Logger.Warn("User deleted successfully, but failed to unlink peer.")
return nil
}
tx.Commit()
return nil
}
@@ -448,3 +448,13 @@ func (us *UserService) getAdminUserCount() int64 {
global.DB.Model(&model.User{}).Where("is_admin = ?", true).Count(&count)
return count
}
func (us *UserService) RefreshAccessToken(ut *model.UserToken) {
ut.ExpiredAt = time.Now().Add(time.Hour * 24 * 7).Unix()
global.DB.Model(ut).Update("expired_at", ut.ExpiredAt)
}
func (us *UserService) AutoRefreshAccessToken(ut *model.UserToken) {
if ut.ExpiredAt-time.Now().Unix() < 86400 {
us.RefreshAccessToken(ut)
}
}