Compare commits

...

13 Commits

Author SHA1 Message Date
ljw
8c97cc8686 up README 2024-09-26 13:38:43 +08:00
ljw
7ae976ee5d up README_EN.md 2024-09-26 13:21:17 +08:00
ljw
e91b53eb32 test release_arm64.yml 2024-09-26 11:49:11 +08:00
ljw
90311536a7 test release_arm64.yml 2024-09-26 11:46:17 +08:00
ljw
e951b7f2f9 fix Dockerfile 2024-09-26 09:29:02 +08:00
ljw
e0f94b62cf add i18n 2024-09-25 22:42:36 +08:00
ljw
8cb701ec85 add i18n 2024-09-25 22:41:57 +08:00
ljw
9afd11e3b8 up readme 2024-09-24 21:16:03 +08:00
ljw
a1d495f2db fix group peers 2024-09-24 19:35:20 +08:00
ljw
652fa93910 up readme 2024-09-24 15:22:21 +08:00
ljw
ef414855f0 up docs 2024-09-24 15:16:07 +08:00
ljw
76cf35cdf0 up readme 2024-09-24 15:15:26 +08:00
ljw
9d7aa05032 add personal apis 2024-09-24 14:43:27 +08:00
59 changed files with 2612 additions and 540 deletions

84
.github/workflows/release_arm64.yml vendored Normal file
View File

@@ -0,0 +1,84 @@
name: Build and Release Arm64
on:
push:
tags:
- 'v*.*.*' # 当推送带有版本号的 tag例如 v1.0.0)时触发工作流
#on:
# push:
# branches: [ "master" ]
# pull_request:
# branches: [ "master" ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
goos: [ linux ] # 指定要构建的操作系统
goarch: [ arm64 ] # 指定架构
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
run: |
sudo apt-get install gcc-aarch64-linux-gnu -y
- 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
GOOS=${{ matrix.goos }} GOARCH=${{ matrix.goarch }} CC=aarch64-linux-gnu-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
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: myapp-${{ matrix.goos }}-${{ matrix.goarch }}
path: |
${{ matrix.goos}}-${{ matrix.goarch }}.tar.gz
- name: Upload to GitHub Release
uses: softprops/action-gh-release@v2
with:
files: |
${{ matrix.goos}}-${{ matrix.goarch }}.tar.gz
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

1
.gitignore vendored
View File

@@ -5,5 +5,6 @@ go.sum
resources/*
!resources/public/upload/.gitignore
!resources/web
!resources/i18n
release
data

View File

@@ -25,11 +25,12 @@ RUN set -eux; \
mkdir -p release/data; \
mkdir -p release/runtime;
VOLUME /app/data
FROM alpine
WORKDIR /app
RUN apk add --no-cache tzdata
COPY --from=builder /go/rustdesk-api/release /app/
VOLUME /app/data
EXPOSE 21114
CMD ["./apimain"]

130
README.md
View File

@@ -2,7 +2,7 @@
[English Doc](README_EN.md)
本项目使用 Go 实现了 RustDesk 的 API并包含了 Web UI 和 Web 客户端。RustDesk 是一个远程桌面软件,提供了自托管的解决方案。
本项目使用 Go 实现了 RustDesk 的 API并包含了 Web Admin 和 Web 客户端。RustDesk 是一个远程桌面软件,提供了自托管的解决方案。
<div align=center>
<img src="https://img.shields.io/badge/golang-1.22-blue"/>
@@ -10,8 +10,32 @@
<img src="https://img.shields.io/badge/gorm-v1.25.7-green"/>
<img src="https://img.shields.io/badge/swag-v1.16.3-yellow"/>
<img src="https://github.com/lejianwen/rustdesk-api/actions/workflows/release.yml/badge.svg"/>
<img src="https://github.com/lejianwen/rustdesk-api/actions/workflows/docker.yml/badge.svg"/>
</div>
# 特性
- PC端API
- 个人版API
- 登录
- 地址簿
- 群组
- 授权登录,支持`github``google`登录,支持`web后台`授权登录
- i18n
- Web Admin
- 用户管理
- 设备管理
- 地址簿管理
- 标签管理
- 群组管理
- Oauth 管理
- 快速使用web client
- i18n
- Web Client
- 自动获取API server
- 自动获取ID服务器和KEY
- 自动获取地址簿
## 使用前准备
### [Rustdesk](https://github.com/rustdesk/rustdesk)
@@ -33,7 +57,7 @@
## 功能
### API 服务: 基本实现了PC端基础的接口。
### API 服务: 基本实现了PC端基础的接口。支持Personal版本接口可以通过配置文件`rustdesk.personal`或环境变量`RUSTDESK_API_RUSTDESK_PERSONAL`来控制是否启用
#### 登录
@@ -46,13 +70,13 @@
![pc_ab](docs/pc_ab.png)
#### 群组,群组分为`共享组`和`普通组`,共享组中所有人都能看到小组成员的地址,普通组只有管理员能看到所有小组成员的地址
#### 群组,群组分为`共享组`和`普通组`,共享组中所有人都能看到小组成员的设备,普通组只有管理员能看到所有小组成员的设备
![pc_gr](docs/pc_gr.png)
### **Web UI**: 使用前后端分离,提供用户友好的管理界面,主要用来管理和展示。
### 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`,请即时更改密码***
@@ -60,21 +84,21 @@
![web_admin](docs/web_admin.png)
2. 普通用户界面
![web_user](docs/web_admin_user.png)
3. 右上角也可以更改密码
右上角也可以更改密码
![web_resetpwd](docs/web_resetpwd.png)
4. 分组可以自定义,方便管理,暂时支持两种类型: `共享组` 和 `普通组`
3. 分组可以自定义,方便管理,暂时支持两种类型: `共享组` 和 `普通组`
![web_admin_gr](docs/web_admin_gr.png)
5. 可以直接打开webclient方便使用
4. 可以直接打开webclient方便使用
![web_webclient](docs/admin_webclient.png)
6. Oauth,暂时只支持了`Github`和`Google`, 需要创建一个`OAuth App`,然后配置到后台
5. Oauth,暂时只支持了`Github`和`Google`, 需要创建一个`OAuth App`,然后配置到后台
![web_admin_oauth](docs/web_admin_oauth.png)
- `github oauth app`在`Settings`->`Developer settings`->`OAuth Apps`->`New OAuth App`
中创建,地址 [https://github.com/settings/developers](https://github.com/settings/developers)
- `Authorization callback URL`填写`http://<your server[:port]>/api/oauth/callback`
,比如`http://127.0.0.1:21114/api/oauth/callback`
### **Web Client**:
### Web Client:
1. 如果已经登录了后台web client将自动直接登录
2. 如果没登录后台点击右上角登录即可api server已经自动配置好了
@@ -82,19 +106,22 @@
3. 登录后会自动同步ID服务器和KEY
4. 登录后会将地址簿自动保存到web client中方便使用
### **自动化文档**: 使用 Swag 生成 API 文档,方便开发者理解和使用 API。
### 自动化文档: 使用 Swag 生成 API 文档,方便开发者理解和使用 API。
1. 后台文档 `<youer server>/admin/swagger/index.html`
2. PC端文档 `<youer server>/swagger/index.html`
1. 后台文档 `<youer server[:port]>/admin/swagger/index.html`
2. PC端文档 `<youer server[:port]>/swagger/index.html`
![api_swag](docs/api_swag.png)
## 安装与运行
### 相关配置
* 参考`conf/config.yaml`配置文件,修改相关配置。如果`gorm.type`是`sqlite`则不需要配置mysql相关配置。
* 参考`conf/config.yaml`配置文件,修改相关配置。
* 如果`gorm.type`是`sqlite`则不需要配置mysql相关配置。
* 语言如果不设置默认为`zh-CN`
```yaml
lang: "en"
gin:
api-addr: "0.0.0.0:21114"
mode: "release"
@@ -114,20 +141,23 @@ rustdesk:
relay-server: "192.168.1.66:21117"
api-server: "http://192.168.1.66:21114"
key: "123456789"
personal: 1
```
* 环境变量,变量名前缀是RUSTDESK_API环境变量如果存在将覆盖配置文件中的配置
| 变量名 | 说明 | 示例 |
|:------------------------------------|:-------------------------------------|-----------------------------|
|-------------------------------------|--------------------------------------|-----------------------------|
| TZ | 时区 | Asia/Shanghai |
| RUSTDESK_API_LANG | 语言 | `en`,`zh-CN` |
| -----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_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 |
@@ -241,6 +271,72 @@ lejianwen/rustdesk-api
```
- 如果使用的是S6的镜像会需要修改启动脚本覆盖镜像中的`/etc/s6-overlay/s6-rc.d/hbbr/run`
和`/etc/s6-overlay/s6-rc.d/hbbr/run`
1. 创建`hbbr/run`
```bash
#!/command/with-contenv sh
cd /data
PARAMS=
[ "${ENCRYPTED_ONLY}" = "1" ] && PARAMS="-k ${KEY}"
/usr/bin/hbbr $PARAMS
```
2. 创建`hbbs/run`
```bash
#!/command/with-contenv sh
sleep 2
cd /data
PARAMS=
[ "${ENCRYPTED_ONLY}" = "1" ] && PARAMS="-k ${KEY}"
/usr/bin/hbbs -r $RELAY $PARAMS
```
3. 修改`docker-compose.yml`中的`s6`部分
```
networks:
rustdesk-net:
external: false
services:
rustdesk-server:
container_name: rustdesk-server
ports:
- 21115:21115
- 21116:21116
- 21116:21116/udp
- 21117:21117
- 21118:21118
- 21119:21119
image: rustdesk/rustdesk-server-s6:latest
environment:
- RELAY=192.168.1.66:21117
- ENCRYPTED_ONLY=1
- KEY=abc123456789
volumes:
- ./data:/data
- ./hbbr/run:/etc/s6-overlay/s6-rc.d/hbbr/run
- ./hbbs/run:/etc/s6-overlay/s6-rc.d/hbbs/run
restart: unless-stopped
rustdesk-api:
container_name: rustdesk-api
ports:
- 21114:21114
image: lejianwen/rustdesk-api
environment:
- TZ=Asia/Shanghai
- RUSTDESK_API_RUSTDESK_ID_SERVER=192.168.1.66:21116
- RUSTDESK_API_RUSTDESK_RELAY_SERVER=192.168.1.66:21117
- RUSTDESK_API_RUSTDESK_API_SERVER=http://192.168.1.66:21114
- RUSTDESK_API_RUSTDESK_KEY=abc123456789
volumes:
- /data/rustdesk/api:/app/data #将数据库挂载
networks:
- rustdesk-net
restart: unless-stopped
```
#### 下载release直接运行
下载地址[release](https://github.com/lejianwen/rustdesk-api/releases)
@@ -281,7 +377,7 @@ lejianwen/rustdesk-api
5. 编译,如果想自己编译,先cd到项目根目录然后windows下直接运行`build.bat`,linux下运行`build.sh`,编译后会在`release`
目录下生成对应的可执行文件。直接运行编译后的可执行文件即可。
6. 打开浏览器访问`http://<your server>:21114/_admin/`,默认用户名密码为`admin`,请及时更改密码。
6. 打开浏览器访问`http://<your server[:port]>/_admin/`,默认用户名密码为`admin`,请及时更改密码。
## 其他

View File

@@ -8,8 +8,33 @@ 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/gorm-v1.25.7-green"/>
<img src="https://img.shields.io/badge/swag-v1.16.3-yellow"/>
<img src="https://github.com/lejianwen/rustdesk-api/actions/workflows/release.yml/badge.svg"/>
<img src="https://github.com/lejianwen/rustdesk-api/actions/workflows/docker.yml/badge.svg"/>
</div>
# Features
- PC API
- Personal API
- Login
- Address Book
- Groups
- Authorized login, supports `GitHub` and `Google` login, supports `web admin` authorized login
- i18n
- Web Admin
- User Management
- Device Management
- Address Book Management
- Tag Management
- Group Management
- OAuth Management
- Quick access to web client
- i18n
- Web Client
- Automatically obtain API server
- Automatically obtain ID server and KEY
- Automatically obtain address book
## Prerequisites
### [Rustdesk](https://github.com/rustdesk/rustdesk)
@@ -30,52 +55,54 @@ desktop software that provides self-hosted solutions.
hbbr -k abc1234567
```
## Features
## Overview
### API Service: Basic implementation of the PC client's primary interfaces.
### API Service: Basic implementation of the PC client's primary interfaces.Supports the Personal version api, which can be enabled by configuring the `rustdesk.personal` file or the `RUSTDESK_API_RUSTDESK_PERSONAL` environment variable.
#### Login
- Added `GitHub` and `Google` login, which can be used after configuration in the admin panel. See the OAuth configuration section
for details.
- Added `GitHub` and `Google` login, which can be used after configuration in the admin panel. See the OAuth
configuration section for details.
- Added authorization login for the web admin panel.
![pc_login](docs/pc_login.png)
![pc_login](docs/en_img/pc_login.png)
#### Address Book
![pc_ab](docs/pc_ab.png)
![pc_ab](docs/en_img/pc_ab.png)
#### 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.
#### 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.
![pc_gr](docs/pc_gr.png)
![pc_gr](docs/en_img/pc_gr.png)
### **Web UI**: The frontend and backend are separated to provide a user-friendly management interface, primarily for managing and displaying data.
### Web Admin
***Frontend code is available at [rustdesk-api-web](https://github.com/lejianwen/rustdesk-api-web)***
***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
***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:
![web_admin](docs/web_admin.png)
![web_admin](docs/en_img/web_admin.png)
2. Regular user interface:
![web_user](docs/web_admin_user.png)
3. You can change your password from the top right corner:
![web_resetpwd](docs/web_resetpwd.png)
4. Groups can be customized for easy management. Currently, two types are supported: `shared group` and `regular group`.
![web_admin_gr](docs/web_admin_gr.png)
5. You can open the web client directly for convenience:
![web_webclient](docs/admin_webclient.png)
6. OAuth support: Currently, `GitHub` and `Google` is supported. You need to create an `OAuth App` and configure it in the admin
![web_user](docs/en_img/web_admin_user.png)
You can change your password from the top right corner:
![web_resetpwd](docs/en_img/web_resetpwd.png)
3. Groups can be customized for easy management. Currently, two types are supported: `shared group` and `regular group`.
![web_admin_gr](docs/en_img/web_admin_gr.png)
4. You can open the web client directly for convenience:
![web_webclient](docs/en_img/admin_webclient.png)
5. OAuth support: Currently, `GitHub` and `Google` is supported. You need to create an `OAuth App` and configure it in
the admin
panel.
![web_admin_oauth](docs/web_admin_oauth.png)
![web_admin_oauth](docs/en_img/web_admin_oauth.png)
- Create a `GitHub OAuth App`
at `Settings` -> `Developer settings` -> `OAuth Apps` -> `New OAuth App` [here](https://github.com/settings/developers).
- Set the `Authorization callback URL` to `http://<your server[:port]>/api/oauth/callback`,
e.g., `http://127.0.0.1:21114/api/oauth/callback`.
### **Web Client**:
### Web Client:
1. If you're already logged into the admin panel, the web client will log in automatically.
2. If you're not logged in, simply click the login button at the top right corner, and the API server will be
@@ -84,20 +111,22 @@ installation are `admin` `admin`, please change the password immediately.***
3. After logging in, the ID server and key will be automatically synced.
4. The address book will also be automatically saved to the web client for convenient use.
### **Automated Documentation** : API documentation is generated using Swag, making it easier for developers to understand and use the API.
### 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`
2. PC client docs: `<your server>/swagger/index.html`
1. Admin panel docs: `<your server[:port]>/admin/swagger/index.html`
2. PC client docs: `<your server[:port]>/swagger/index.html`
![api_swag](docs/api_swag.png)
## Installation and Setup
### Configuration
* Modify the configuration in `conf/config.yaml`. If `gorm.type` is set to `sqlite`, MySQL-related configurations are
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
lang: "en"
gin:
api-addr: "0.0.0.0:21114"
mode: "release"
@@ -117,19 +146,24 @@ rustdesk:
relay-server: "192.168.1.66:21117"
api-server: "http://192.168.1.66:21114"
key: "123456789"
personal: 1
```
* Environment variables, with the prefix `RUSTDESK_API`, will override the settings in the configuration file if
* Environment variables, with the prefix `RUSTDESK_API_RUSTDESK_PERSONAL`, will override the settings in the
configuration file if
present.
| Variable Name | Description | Example |
|------------------------------------|-----------------------------------------------------------|--------------------------------|
| TZ | timezone | Asia/Shanghai |
| RUSTDESK_API_LANG | Language | `en`,`zh-CN` |
| ----- GIN Configuration ----- | --------------------------------------- | ------------------------------ |
| RUSTDESK_API_GIN_TRUST_PROXY | Trusted proxy IPs, separated by commas. | 192.168.1.2,192.168.1.3 |
| ----- GORM Configuration ----- | --------------------------------------- | ------------------------------ |
| RUSTDESK_API_GORM_TYPE | Database type (`sqlite` or `mysql`). Default is `sqlite`. | sqlite |
| RUSTDESK_API_GORM_MAX_IDLE_CONNS | Maximum idle connections | 10 |
| RUSTDESK_API_GORM_MAX_OPEN_CONNS | Maximum open connections | 100 |
| RUSTDESK_API_RUSTDESK_PERSONAL | Open Personal Api 1:Enable,0:Disable | 1 |
| ----- MYSQL Configuration ----- | --------------------------------------- | ------------------------------ |
| RUSTDESK_API_MYSQL_USERNAME | MySQL username | root |
| RUSTDESK_API_MYSQL_PASSWORD | MySQL password | 111111 |
@@ -151,6 +185,7 @@ rustdesk:
```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 \
@@ -241,6 +276,70 @@ lejianwen/rustdesk-api
restart: unless-stopped
```
- If you are using an S6 image, you need to modify the startup script `/etc/s6-overlay/s6-rc.d/hbbr/run`
and `/etc/s6-overlay/s6-rc.d/hbbr/run`
1. create `hbbr/run`
```bash
#!/command/with-contenv sh
cd /data
PARAMS=
[ "${ENCRYPTED_ONLY}" = "1" ] && PARAMS="-k ${KEY}"
/usr/bin/hbbr $PARAMS
```
2. create `hbbs/run`
```bash
#!/command/with-contenv sh
sleep 2
cd /data
PARAMS=
[ "${ENCRYPTED_ONLY}" = "1" ] && PARAMS="-k ${KEY}"
/usr/bin/hbbs -r $RELAY $PARAMS
```
3. edit `docker-compose.yml`
```
networks:
rustdesk-net:
external: false
services:
rustdesk-server:
container_name: rustdesk-server
ports:
- 21115:21115
- 21116:21116
- 21116:21116/udp
- 21117:21117
- 21118:21118
- 21119:21119
image: rustdesk/rustdesk-server-s6:latest
environment:
- RELAY=192.168.1.66:21117
- ENCRYPTED_ONLY=1
- KEY=abc123456789
volumes:
- ./data:/data
- ./hbbr/run:/etc/s6-overlay/s6-rc.d/hbbr/run
- ./hbbs/run:/etc/s6-overlay/s6-rc.d/hbbs/run
restart: unless-stopped
rustdesk-api:
container_name: rustdesk-api
ports:
- 21114:21114
image: lejianwen/rustdesk-api
environment:
- TZ=Asia/Shanghai
- RUSTDESK_API_RUSTDESK_ID_SERVER=192.168.1.66:21116
- RUSTDESK_API_RUSTDESK_RELAY_SERVER=192.168.1.66:21117
- RUSTDESK_API_RUSTDESK_API_SERVER=http://192.168.1.66:21114
- RUSTDESK_API_RUSTDESK_KEY=abc123456789
volumes:
- /data/rustdesk/api:/app/data #将数据库挂载
networks:
- rustdesk-net
restart: unless-stopped
```
#### Running from Release
@@ -286,7 +385,7 @@ Download the release from [release](https://github.com/lejianwen/rustdesk-api/re
compiling, the corresponding executables will be generated in the `release` directory. Run the compiled executables
directly.
6. Open your browser and visit `http://<your server>: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.
## Miscellaneous

View File

@@ -12,12 +12,17 @@ import (
"Gwen/model"
"Gwen/service"
"fmt"
"github.com/BurntSushi/toml"
"github.com/gin-gonic/gin"
"github.com/go-playground/locales/en"
"github.com/go-playground/locales/zh_Hans_CN"
ut "github.com/go-playground/universal-translator"
"github.com/go-playground/validator/v10"
en_translations "github.com/go-playground/validator/v10/translations/en"
zh_translations "github.com/go-playground/validator/v10/translations/zh"
"github.com/go-redis/redis/v8"
"github.com/nicksnyder/go-i18n/v2/i18n"
"golang.org/x/text/language"
"reflect"
)
@@ -98,6 +103,7 @@ func main() {
//locker
global.Lock = lock.NewLocal()
InitI18n()
//gin
http.ApiInit()
@@ -105,15 +111,25 @@ func main() {
func ApiInitValidator() {
validate := validator.New()
// 定义不同的语言翻译
enT := en.New()
cn := zh_Hans_CN.New()
uni := ut.New(enT, cn)
trans, _ := uni.GetTranslator("cn")
err := zh_translations.RegisterDefaultTranslations(validate, trans)
enTrans, _ := uni.GetTranslator("en")
zhTrans, _ := uni.GetTranslator("zh_Hans_CN")
err := zh_translations.RegisterDefaultTranslations(validate, zhTrans)
if err != nil {
//退出
panic(err)
}
err = en_translations.RegisterDefaultTranslations(validate, enTrans)
if err != nil {
panic(err)
}
validate.RegisterTagNameFunc(func(field reflect.StructField) string {
label := field.Tag.Get("label")
if label == "" {
@@ -122,10 +138,16 @@ func ApiInitValidator() {
return label
})
global.Validator.Validate = validate
global.Validator.VTrans = trans
global.Validator.UT = uni // 存储 Universal Translator
global.Validator.VTrans = zhTrans
global.Validator.ValidStruct = func(i interface{}) []string {
global.Validator.ValidStruct = func(ctx *gin.Context, i interface{}) []string {
err := global.Validator.Validate.Struct(i)
lang := ctx.GetHeader("Accept-Language")
if lang == "" {
lang = global.Config.Lang
}
trans := getTranslatorForLang(lang)
errList := make([]string, 0, 10)
if err != nil {
if _, ok := err.(*validator.InvalidValidationError); ok {
@@ -133,14 +155,18 @@ func ApiInitValidator() {
return errList
}
for _, err2 := range err.(validator.ValidationErrors) {
errList = append(errList, err2.Translate(global.Validator.VTrans))
errList = append(errList, err2.Translate(trans))
}
}
return errList
}
global.Validator.ValidVar = func(field interface{}, tag string) []string {
global.Validator.ValidVar = func(ctx *gin.Context, field interface{}, tag string) []string {
err := global.Validator.Validate.Var(field, tag)
fmt.Println(err)
lang := ctx.GetHeader("Accept-Language")
if lang == "" {
lang = global.Config.Lang
}
trans := getTranslatorForLang(lang)
errList := make([]string, 0, 10)
if err != nil {
if _, ok := err.(*validator.InvalidValidationError); ok {
@@ -148,16 +174,31 @@ func ApiInitValidator() {
return errList
}
for _, err2 := range err.(validator.ValidationErrors) {
errList = append(errList, err2.Translate(global.Validator.VTrans))
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, _ := global.Validator.UT.GetTranslator("zh_Hans_CN")
return trans
case "en":
fallthrough
default:
trans, _ := global.Validator.UT.GetTranslator("en")
return trans
}
}
func DatabaseAutoUpdate() {
version := 103
version := 126
db := global.DB
@@ -251,3 +292,37 @@ func Migrate(version uint) {
}
}
func InitI18n() {
bundle := i18n.NewBundle(language.English)
bundle.RegisterUnmarshalFunc("toml", toml.Unmarshal)
bundle.LoadMessageFile(global.Config.Gin.ResourcesPath + "/i18n/en.toml")
bundle.LoadMessageFile(global.Config.Gin.ResourcesPath + "/i18n/zh_CN.toml")
global.Localizer = func(ctx *gin.Context) *i18n.Localizer {
lang := ctx.GetHeader("Accept-Language")
if lang == "" {
lang = global.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)
}

View File

@@ -1,3 +1,4 @@
lang: "zh-CN"
gin:
api-addr: "0.0.0.0:21114"
mode: "release" #release,debug,test
@@ -17,6 +18,7 @@ rustdesk:
relay-server: "192.168.1.66:21117"
api-server: "http://192.168.1.66:21114"
key: "123456789"
personal: 1
logger:
path: "./runtime/log.txt"
level: "warn" #trace,debug,info,warn,error,fatal

View File

@@ -15,6 +15,7 @@ const (
)
type Config struct {
Lang string `mapstructure:"lang"`
Gorm Gorm
Mysql Mysql
Gin Gin

View File

@@ -5,4 +5,5 @@ type Rustdesk struct {
RelayServer string `mapstructure:"relay-server"`
ApiServer string `mapstructure:"api-server"`
Key string `mapstructure:"key"`
Personal int `mapstructure:"personal"`
}

View File

@@ -1185,7 +1185,7 @@ const docTemplateadmin = `{
"token": []
}
],
"description": "创建机器",
"description": "创建设备",
"consumes": [
"application/json"
],
@@ -1193,12 +1193,12 @@ const docTemplateadmin = `{
"application/json"
],
"tags": [
"机器"
"设备"
],
"summary": "创建机器",
"summary": "创建设备",
"parameters": [
{
"description": "机器信息",
"description": "设备信息",
"name": "body",
"in": "body",
"required": true,
@@ -1242,7 +1242,7 @@ const docTemplateadmin = `{
"token": []
}
],
"description": "机器删除",
"description": "设备删除",
"consumes": [
"application/json"
],
@@ -1250,12 +1250,12 @@ const docTemplateadmin = `{
"application/json"
],
"tags": [
"机器"
"设备"
],
"summary": "机器删除",
"summary": "设备删除",
"parameters": [
{
"description": "机器信息",
"description": "设备信息",
"name": "body",
"in": "body",
"required": true,
@@ -1287,7 +1287,7 @@ const docTemplateadmin = `{
"token": []
}
],
"description": "机器详情",
"description": "设备详情",
"consumes": [
"application/json"
],
@@ -1295,9 +1295,9 @@ const docTemplateadmin = `{
"application/json"
],
"tags": [
"机器"
"设备"
],
"summary": "机器详情",
"summary": "设备详情",
"parameters": [
{
"type": "integer",
@@ -1342,7 +1342,7 @@ const docTemplateadmin = `{
"token": []
}
],
"description": "机器列表",
"description": "设备列表",
"consumes": [
"application/json"
],
@@ -1350,9 +1350,9 @@ const docTemplateadmin = `{
"application/json"
],
"tags": [
"机器"
"设备"
],
"summary": "机器列表",
"summary": "设备列表",
"parameters": [
{
"type": "integer",
@@ -1402,7 +1402,7 @@ const docTemplateadmin = `{
"token": []
}
],
"description": "机器编辑",
"description": "设备编辑",
"consumes": [
"application/json"
],
@@ -1410,12 +1410,12 @@ const docTemplateadmin = `{
"application/json"
],
"tags": [
"机器"
"设备"
],
"summary": "机器编辑",
"summary": "设备编辑",
"parameters": [
{
"description": "机器信息",
"description": "设备信息",
"name": "body",
"in": "body",
"required": true,
@@ -2266,7 +2266,7 @@ const docTemplateadmin = `{
"alias": {
"type": "string"
},
"force_always_relay": {
"forceAlwaysRelay": {
"type": "boolean"
},
"hash": {
@@ -2278,7 +2278,7 @@ const docTemplateadmin = `{
"id": {
"type": "string"
},
"login_name": {
"loginName": {
"type": "string"
},
"online": {
@@ -2290,16 +2290,16 @@ const docTemplateadmin = `{
"platform": {
"type": "string"
},
"rdp_port": {
"rdpPort": {
"type": "string"
},
"rdp_username": {
"rdpUsername": {
"type": "string"
},
"row_id": {
"type": "integer"
},
"same_server": {
"sameServer": {
"type": "boolean"
},
"tags": {
@@ -2434,8 +2434,7 @@ const docTemplateadmin = `{
"type": "object",
"required": [
"color",
"name",
"user_id"
"name"
],
"properties": {
"color": {
@@ -2456,7 +2455,6 @@ const docTemplateadmin = `{
"type": "object",
"required": [
"group_id",
"nickname",
"status",
"username"
],

View File

@@ -1178,7 +1178,7 @@
"token": []
}
],
"description": "创建机器",
"description": "创建设备",
"consumes": [
"application/json"
],
@@ -1186,12 +1186,12 @@
"application/json"
],
"tags": [
"机器"
"设备"
],
"summary": "创建机器",
"summary": "创建设备",
"parameters": [
{
"description": "机器信息",
"description": "设备信息",
"name": "body",
"in": "body",
"required": true,
@@ -1235,7 +1235,7 @@
"token": []
}
],
"description": "机器删除",
"description": "设备删除",
"consumes": [
"application/json"
],
@@ -1243,12 +1243,12 @@
"application/json"
],
"tags": [
"机器"
"设备"
],
"summary": "机器删除",
"summary": "设备删除",
"parameters": [
{
"description": "机器信息",
"description": "设备信息",
"name": "body",
"in": "body",
"required": true,
@@ -1280,7 +1280,7 @@
"token": []
}
],
"description": "机器详情",
"description": "设备详情",
"consumes": [
"application/json"
],
@@ -1288,9 +1288,9 @@
"application/json"
],
"tags": [
"机器"
"设备"
],
"summary": "机器详情",
"summary": "设备详情",
"parameters": [
{
"type": "integer",
@@ -1335,7 +1335,7 @@
"token": []
}
],
"description": "机器列表",
"description": "设备列表",
"consumes": [
"application/json"
],
@@ -1343,9 +1343,9 @@
"application/json"
],
"tags": [
"机器"
"设备"
],
"summary": "机器列表",
"summary": "设备列表",
"parameters": [
{
"type": "integer",
@@ -1395,7 +1395,7 @@
"token": []
}
],
"description": "机器编辑",
"description": "设备编辑",
"consumes": [
"application/json"
],
@@ -1403,12 +1403,12 @@
"application/json"
],
"tags": [
"机器"
"设备"
],
"summary": "机器编辑",
"summary": "设备编辑",
"parameters": [
{
"description": "机器信息",
"description": "设备信息",
"name": "body",
"in": "body",
"required": true,
@@ -2259,7 +2259,7 @@
"alias": {
"type": "string"
},
"force_always_relay": {
"forceAlwaysRelay": {
"type": "boolean"
},
"hash": {
@@ -2271,7 +2271,7 @@
"id": {
"type": "string"
},
"login_name": {
"loginName": {
"type": "string"
},
"online": {
@@ -2283,16 +2283,16 @@
"platform": {
"type": "string"
},
"rdp_port": {
"rdpPort": {
"type": "string"
},
"rdp_username": {
"rdpUsername": {
"type": "string"
},
"row_id": {
"type": "integer"
},
"same_server": {
"sameServer": {
"type": "boolean"
},
"tags": {
@@ -2427,8 +2427,7 @@
"type": "object",
"required": [
"color",
"name",
"user_id"
"name"
],
"properties": {
"color": {
@@ -2449,7 +2448,6 @@
"type": "object",
"required": [
"group_id",
"nickname",
"status",
"username"
],

View File

@@ -16,7 +16,7 @@ definitions:
properties:
alias:
type: string
force_always_relay:
forceAlwaysRelay:
type: boolean
hash:
type: string
@@ -24,7 +24,7 @@ definitions:
type: string
id:
type: string
login_name:
loginName:
type: string
online:
type: boolean
@@ -32,13 +32,13 @@ definitions:
type: string
platform:
type: string
rdp_port:
rdpPort:
type: string
rdp_username:
rdpUsername:
type: string
row_id:
type: integer
same_server:
sameServer:
type: boolean
tags:
items:
@@ -141,7 +141,6 @@ definitions:
required:
- color
- name
- user_id
type: object
admin.UserForm:
properties:
@@ -166,7 +165,6 @@ definitions:
type: string
required:
- group_id
- nickname
- status
- username
type: object
@@ -1163,9 +1161,9 @@ paths:
post:
consumes:
- application/json
description: 创建机器
description: 创建设备
parameters:
- description: 机器信息
- description: 设备信息
in: body
name: body
required: true
@@ -1189,16 +1187,16 @@ paths:
$ref: '#/definitions/response.Response'
security:
- token: []
summary: 创建机器
summary: 创建设备
tags:
- 机器
- 设备
/admin/peer/delete:
post:
consumes:
- application/json
description: 机器删除
description: 设备删除
parameters:
- description: 机器信息
- description: 设备信息
in: body
name: body
required: true
@@ -1217,14 +1215,14 @@ paths:
$ref: '#/definitions/response.Response'
security:
- token: []
summary: 机器删除
summary: 设备删除
tags:
- 机器
- 设备
/admin/peer/detail/{id}:
get:
consumes:
- application/json
description: 机器详情
description: 设备详情
parameters:
- description: ID
in: path
@@ -1249,14 +1247,14 @@ paths:
$ref: '#/definitions/response.Response'
security:
- token: []
summary: 机器详情
summary: 设备详情
tags:
- 机器
- 设备
/admin/peer/list:
get:
consumes:
- application/json
description: 机器列表
description: 设备列表
parameters:
- description: 页码
in: query
@@ -1284,16 +1282,16 @@ paths:
$ref: '#/definitions/response.Response'
security:
- token: []
summary: 机器列表
summary: 设备列表
tags:
- 机器
- 设备
/admin/peer/update:
post:
consumes:
- application/json
description: 机器编辑
description: 设备编辑
parameters:
- description: 机器信息
- description: 设备信息
in: body
name: body
required: true
@@ -1317,9 +1315,9 @@ paths:
$ref: '#/definitions/response.Response'
security:
- token: []
summary: 机器编辑
summary: 设备编辑
tags:
- 机器
- 设备
/admin/server-config:
get:
consumes:

View File

@@ -136,7 +136,7 @@ const docTemplateapi = `{
"application/json"
],
"tags": [
"地址"
"地址[Personal]"
],
"summary": "标签添加",
"responses": {
@@ -155,14 +155,14 @@ const docTemplateapi = `{
}
}
},
"/ab/personal": {
"/ab/peer/add/{guid}": {
"post": {
"security": [
{
"BearerAuth": []
}
],
"description": "个人信息",
"description": "添加地址",
"consumes": [
"application/json"
],
@@ -170,9 +170,136 @@ const docTemplateapi = `{
"application/json"
],
"tags": [
"用户"
"地址[Personal]"
],
"summary": "个人信息",
"summary": "添加地址",
"parameters": [
{
"type": "string",
"description": "id",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "string"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.ErrorResponse"
}
}
}
},
"delete": {
"security": [
{
"BearerAuth": []
}
],
"description": "删除地址",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"地址[Personal]"
],
"summary": "删除地址",
"parameters": [
{
"type": "string",
"description": "id",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "string"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.ErrorResponse"
}
}
}
}
},
"/ab/peer/update/{guid}": {
"put": {
"security": [
{
"BearerAuth": []
}
],
"description": "更新地址",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"地址[Personal]"
],
"summary": "更新地址",
"parameters": [
{
"type": "string",
"description": "id",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "string"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.ErrorResponse"
}
}
}
}
},
"/ab/peers": {
"post": {
"security": [
{
"BearerAuth": []
}
],
"description": "地址",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"地址[Personal]"
],
"summary": "地址列表",
"parameters": [
{
"description": "string valid",
@@ -199,6 +326,283 @@ const docTemplateapi = `{
}
}
},
"/ab/personal": {
"post": {
"security": [
{
"BearerAuth": []
}
],
"description": "个人地址",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"地址[Personal]"
],
"summary": "个人地址",
"parameters": [
{
"description": "string valid",
"name": "string",
"in": "body",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/ab/settings": {
"post": {
"security": [
{
"BearerAuth": []
}
],
"description": "设置",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"地址[Personal]"
],
"summary": "设置",
"parameters": [
{
"description": "string valid",
"name": "string",
"in": "body",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/ab/shared/profiles": {
"post": {
"security": [
{
"BearerAuth": []
}
],
"description": "共享",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"地址[Personal]"
],
"summary": "共享地址簿",
"parameters": [
{
"description": "string valid",
"name": "string",
"in": "body",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/ab/tag/rename/{guid}": {
"put": {
"security": [
{
"BearerAuth": []
}
],
"description": "标签",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"地址[Personal]"
],
"summary": "标签重命名",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "string"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.ErrorResponse"
}
}
}
}
},
"/ab/tag/update/{guid}": {
"put": {
"security": [
{
"BearerAuth": []
}
],
"description": "标签",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"地址[Personal]"
],
"summary": "标签修改颜色",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "string"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.ErrorResponse"
}
}
}
}
},
"/ab/tag/{guid}": {
"delete": {
"security": [
{
"BearerAuth": []
}
],
"description": "标签",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"地址[Personal]"
],
"summary": "标签删除",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "string"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.ErrorResponse"
}
}
}
}
},
"/ab/tags/{guid}": {
"post": {
"security": [
{
"BearerAuth": []
}
],
"description": "标签",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"地址[Personal]"
],
"summary": "标签",
"parameters": [
{
"type": "string",
"description": "id",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/model.TagList"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.ErrorResponse"
}
}
}
}
},
"/api": {
"get": {
"security": [
@@ -389,35 +793,6 @@ const docTemplateapi = `{
}
}
},
"/oauth/login": {
"get": {
"description": "WebOauthLogin",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Oauth"
],
"summary": "WebOauthLogin",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "string"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"type": "string"
}
}
}
}
},
"/oidc/auth": {
"post": {
"description": "OidcAuth",
@@ -882,6 +1257,26 @@ const docTemplateapi = `{
}
}
},
"model.TagList": {
"type": "object",
"properties": {
"list": {
"type": "array",
"items": {
"$ref": "#/definitions/model.Tag"
}
},
"page": {
"type": "integer"
},
"page_size": {
"type": "integer"
},
"total": {
"type": "integer"
}
}
},
"response.DataResponse": {
"type": "object",
"properties": {

View File

@@ -129,7 +129,7 @@
"application/json"
],
"tags": [
"地址"
"地址[Personal]"
],
"summary": "标签添加",
"responses": {
@@ -148,14 +148,14 @@
}
}
},
"/ab/personal": {
"/ab/peer/add/{guid}": {
"post": {
"security": [
{
"BearerAuth": []
}
],
"description": "个人信息",
"description": "添加地址",
"consumes": [
"application/json"
],
@@ -163,9 +163,136 @@
"application/json"
],
"tags": [
"用户"
"地址[Personal]"
],
"summary": "个人信息",
"summary": "添加地址",
"parameters": [
{
"type": "string",
"description": "id",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "string"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.ErrorResponse"
}
}
}
},
"delete": {
"security": [
{
"BearerAuth": []
}
],
"description": "删除地址",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"地址[Personal]"
],
"summary": "删除地址",
"parameters": [
{
"type": "string",
"description": "id",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "string"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.ErrorResponse"
}
}
}
}
},
"/ab/peer/update/{guid}": {
"put": {
"security": [
{
"BearerAuth": []
}
],
"description": "更新地址",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"地址[Personal]"
],
"summary": "更新地址",
"parameters": [
{
"type": "string",
"description": "id",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "string"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.ErrorResponse"
}
}
}
}
},
"/ab/peers": {
"post": {
"security": [
{
"BearerAuth": []
}
],
"description": "地址",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"地址[Personal]"
],
"summary": "地址列表",
"parameters": [
{
"description": "string valid",
@@ -192,6 +319,283 @@
}
}
},
"/ab/personal": {
"post": {
"security": [
{
"BearerAuth": []
}
],
"description": "个人地址",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"地址[Personal]"
],
"summary": "个人地址",
"parameters": [
{
"description": "string valid",
"name": "string",
"in": "body",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/ab/settings": {
"post": {
"security": [
{
"BearerAuth": []
}
],
"description": "设置",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"地址[Personal]"
],
"summary": "设置",
"parameters": [
{
"description": "string valid",
"name": "string",
"in": "body",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/ab/shared/profiles": {
"post": {
"security": [
{
"BearerAuth": []
}
],
"description": "共享",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"地址[Personal]"
],
"summary": "共享地址簿",
"parameters": [
{
"description": "string valid",
"name": "string",
"in": "body",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/ab/tag/rename/{guid}": {
"put": {
"security": [
{
"BearerAuth": []
}
],
"description": "标签",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"地址[Personal]"
],
"summary": "标签重命名",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "string"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.ErrorResponse"
}
}
}
}
},
"/ab/tag/update/{guid}": {
"put": {
"security": [
{
"BearerAuth": []
}
],
"description": "标签",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"地址[Personal]"
],
"summary": "标签修改颜色",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "string"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.ErrorResponse"
}
}
}
}
},
"/ab/tag/{guid}": {
"delete": {
"security": [
{
"BearerAuth": []
}
],
"description": "标签",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"地址[Personal]"
],
"summary": "标签删除",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "string"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.ErrorResponse"
}
}
}
}
},
"/ab/tags/{guid}": {
"post": {
"security": [
{
"BearerAuth": []
}
],
"description": "标签",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"地址[Personal]"
],
"summary": "标签",
"parameters": [
{
"type": "string",
"description": "id",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/model.TagList"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.ErrorResponse"
}
}
}
}
},
"/api": {
"get": {
"security": [
@@ -382,35 +786,6 @@
}
}
},
"/oauth/login": {
"get": {
"description": "WebOauthLogin",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Oauth"
],
"summary": "WebOauthLogin",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "string"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"type": "string"
}
}
}
}
},
"/oidc/auth": {
"post": {
"description": "OidcAuth",
@@ -875,6 +1250,26 @@
}
}
},
"model.TagList": {
"type": "object",
"properties": {
"list": {
"type": "array",
"items": {
"$ref": "#/definitions/model.Tag"
}
},
"page": {
"type": "integer"
},
"page_size": {
"type": "integer"
},
"total": {
"type": "integer"
}
}
},
"response.DataResponse": {
"type": "object",
"properties": {

View File

@@ -103,6 +103,19 @@ definitions:
user_id:
type: integer
type: object
model.TagList:
properties:
list:
items:
$ref: '#/definitions/model.Tag'
type: array
page:
type: integer
page_size:
type: integer
total:
type: integer
type: object
response.DataResponse:
properties:
data: {}
@@ -215,12 +228,92 @@ paths:
- BearerAuth: []
summary: 标签添加
tags:
- 地址
/ab/personal:
- 地址[Personal]
/ab/peer/add/{guid}:
delete:
consumes:
- application/json
description: 删除地址
parameters:
- description: id
in: path
name: id
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]
post:
consumes:
- application/json
description: 个人信息
description: 添加地址
parameters:
- description: id
in: path
name: id
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: id
in: path
name: id
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: string valid
in: body
@@ -240,9 +333,180 @@ paths:
$ref: '#/definitions/response.Response'
security:
- BearerAuth: []
summary: 个人信息
summary: 地址列表
tags:
- 用户
- 地址[Personal]
/ab/personal:
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/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: 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/tag/{guid}:
delete:
consumes:
- application/json
description: 标签
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: 标签
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: 标签
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: id
in: path
name: id
required: true
type: string
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/model.TagList'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.ErrorResponse'
security:
- BearerAuth: []
summary: 标签
tags:
- 地址[Personal]
/api:
get:
consumes:
@@ -366,25 +630,6 @@ paths:
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:
post:
consumes:

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

BIN
docs/en_img/pc_ab.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

BIN
docs/en_img/pc_gr.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

BIN
docs/en_img/pc_login.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

BIN
docs/en_img/web_admin.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@@ -6,9 +6,11 @@ import (
"Gwen/lib/jwt"
"Gwen/lib/lock"
"Gwen/lib/upload"
"github.com/gin-gonic/gin"
ut "github.com/go-playground/universal-translator"
"github.com/go-playground/validator/v10"
"github.com/go-redis/redis/v8"
"github.com/nicksnyder/go-i18n/v2/i18n"
"github.com/sirupsen/logrus"
"github.com/spf13/viper"
"gorm.io/gorm"
@@ -23,11 +25,13 @@ var (
Cache cache.Handler
Validator struct {
Validate *validator.Validate
UT *ut.UniversalTranslator
VTrans ut.Translator
ValidStruct func(interface{}) []string
ValidVar func(field interface{}, tag string) []string
ValidStruct func(*gin.Context, interface{}) []string
ValidVar func(ctx *gin.Context, field interface{}, tag string) []string
}
Oss *upload.Oss
Jwt *jwt.Jwt
Lock lock.Locker
Oss *upload.Oss
Jwt *jwt.Jwt
Lock lock.Locker
Localizer func(ctx *gin.Context) *i18n.Localizer
)

8
go.mod
View File

@@ -3,6 +3,7 @@ module Gwen
go 1.22
require (
github.com/BurntSushi/toml v1.3.2
github.com/antonfisher/nested-logrus-formatter v1.3.1
github.com/fsnotify/fsnotify v1.5.1
github.com/fvbock/endless v0.0.0-20170109170031-447134032cb6
@@ -12,12 +13,14 @@ require (
github.com/go-playground/validator/v10 v10.11.2
github.com/go-redis/redis/v8 v8.11.4
github.com/golang-jwt/jwt/v5 v5.2.1
github.com/nicksnyder/go-i18n/v2 v2.4.0
github.com/sirupsen/logrus v1.8.1
github.com/spf13/viper v1.9.0
github.com/swaggo/files v1.0.1
github.com/swaggo/gin-swagger v1.6.0
github.com/swaggo/swag v1.16.3
golang.org/x/oauth2 v0.23.0
golang.org/x/text v0.18.0
gorm.io/driver/mysql v1.5.7
gorm.io/driver/sqlite v1.5.6
gorm.io/gorm v1.25.7
@@ -64,10 +67,9 @@ require (
github.com/ugorji/go/codec v1.2.9 // indirect
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
golang.org/x/crypto v0.23.0 // indirect
golang.org/x/net v0.21.0 // indirect
golang.org/x/net v0.25.0 // indirect
golang.org/x/sys v0.25.0 // indirect
golang.org/x/text v0.15.0 // indirect
golang.org/x/tools v0.7.0 // indirect
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/ini.v1 v1.63.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect

View File

@@ -31,14 +31,14 @@ func (ct *AddressBook) Detail(c *gin.Context) {
t := service.AllService.AddressBookService.InfoByRowId(uint(iid))
u := service.AllService.UserService.CurUser(c)
if !service.AllService.UserService.IsAdmin(u) && t.UserId != u.Id {
response.Fail(c, 101, "无权限")
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
return
}
if t.RowId > 0 {
response.Success(c, t)
return
}
response.Fail(c, 101, "信息不存在")
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
return
}
@@ -56,10 +56,10 @@ func (ct *AddressBook) Detail(c *gin.Context) {
func (ct *AddressBook) Create(c *gin.Context) {
f := &admin.AddressBookForm{}
if err := c.ShouldBindJSON(f); err != nil {
response.Fail(c, 101, "参数错误")
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
errList := global.Validator.ValidStruct(f)
errList := global.Validator.ValidStruct(c, f)
if len(errList) > 0 {
response.Fail(c, 101, errList[0])
return
@@ -71,7 +71,7 @@ func (ct *AddressBook) Create(c *gin.Context) {
}
err := service.AllService.AddressBookService.Create(t)
if err != nil {
response.Fail(c, 101, "创建失败")
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
return
}
response.Success(c, u)
@@ -94,7 +94,7 @@ func (ct *AddressBook) Create(c *gin.Context) {
func (ct *AddressBook) List(c *gin.Context) {
query := &admin.AddressBookQuery{}
if err := c.ShouldBindQuery(query); err != nil {
response.Fail(c, 101, "参数错误")
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
u := service.AllService.UserService.CurUser(c)
@@ -123,27 +123,27 @@ func (ct *AddressBook) List(c *gin.Context) {
func (ct *AddressBook) Update(c *gin.Context) {
f := &admin.AddressBookForm{}
if err := c.ShouldBindJSON(f); err != nil {
response.Fail(c, 101, "参数错误")
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
errList := global.Validator.ValidStruct(f)
errList := global.Validator.ValidStruct(c, f)
if len(errList) > 0 {
response.Fail(c, 101, errList[0])
return
}
if f.RowId == 0 {
response.Fail(c, 101, "参数错误")
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
return
}
t := f.ToAddressBook()
u := service.AllService.UserService.CurUser(c)
if !service.AllService.UserService.IsAdmin(u) && t.UserId != u.Id {
response.Fail(c, 101, "无权限")
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
return
}
err := service.AllService.AddressBookService.Update(t)
if err != nil {
response.Fail(c, 101, "更新失败")
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
return
}
response.Success(c, nil)
@@ -163,11 +163,11 @@ func (ct *AddressBook) Update(c *gin.Context) {
func (ct *AddressBook) Delete(c *gin.Context) {
f := &admin.AddressBookForm{}
if err := c.ShouldBindJSON(f); err != nil {
response.Fail(c, 101, "系统错误")
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
id := f.RowId
errList := global.Validator.ValidVar(id, "required,gt=0")
errList := global.Validator.ValidVar(c, id, "required,gt=0")
if len(errList) > 0 {
response.Fail(c, 101, errList[0])
return
@@ -175,7 +175,7 @@ func (ct *AddressBook) Delete(c *gin.Context) {
t := service.AllService.AddressBookService.InfoByRowId(f.RowId)
u := service.AllService.UserService.CurUser(c)
if !service.AllService.UserService.IsAdmin(u) && t.UserId != u.Id {
response.Fail(c, 101, "无权限")
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
return
}
if u.Id > 0 {
@@ -184,8 +184,8 @@ func (ct *AddressBook) Delete(c *gin.Context) {
response.Success(c, nil)
return
}
response.Fail(c, 101, err.Error())
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
return
}
response.Fail(c, 101, "信息不存在")
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
}

View File

@@ -31,7 +31,7 @@ func (ct *Group) Detail(c *gin.Context) {
response.Success(c, u)
return
}
response.Fail(c, 101, "信息不存在")
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
return
}
@@ -49,10 +49,10 @@ func (ct *Group) Detail(c *gin.Context) {
func (ct *Group) Create(c *gin.Context) {
f := &admin.GroupForm{}
if err := c.ShouldBindJSON(f); err != nil {
response.Fail(c, 101, "参数错误")
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
errList := global.Validator.ValidStruct(f)
errList := global.Validator.ValidStruct(c, f)
if len(errList) > 0 {
response.Fail(c, 101, errList[0])
return
@@ -60,7 +60,7 @@ func (ct *Group) Create(c *gin.Context) {
u := f.ToGroup()
err := service.AllService.GroupService.Create(u)
if err != nil {
response.Fail(c, 101, "创建失败")
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
return
}
response.Success(c, u)
@@ -81,7 +81,7 @@ func (ct *Group) Create(c *gin.Context) {
func (ct *Group) List(c *gin.Context) {
query := &admin.PageQuery{}
if err := c.ShouldBindQuery(query); err != nil {
response.Fail(c, 101, "参数错误")
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
res := service.AllService.GroupService.List(query.Page, query.PageSize, nil)
@@ -102,14 +102,14 @@ func (ct *Group) List(c *gin.Context) {
func (ct *Group) Update(c *gin.Context) {
f := &admin.GroupForm{}
if err := c.ShouldBindJSON(f); err != nil {
response.Fail(c, 101, "参数错误")
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
if f.Id == 0 {
response.Fail(c, 101, "参数错误")
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
return
}
errList := global.Validator.ValidStruct(f)
errList := global.Validator.ValidStruct(c, f)
if len(errList) > 0 {
response.Fail(c, 101, errList[0])
return
@@ -117,7 +117,7 @@ func (ct *Group) Update(c *gin.Context) {
u := f.ToGroup()
err := service.AllService.GroupService.Update(u)
if err != nil {
response.Fail(c, 101, "更新失败")
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
return
}
response.Success(c, nil)
@@ -137,11 +137,11 @@ func (ct *Group) Update(c *gin.Context) {
func (ct *Group) Delete(c *gin.Context) {
f := &admin.GroupForm{}
if err := c.ShouldBindJSON(f); err != nil {
response.Fail(c, 101, "系统错误")
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
id := f.Id
errList := global.Validator.ValidVar(id, "required,gt=0")
errList := global.Validator.ValidVar(c, id, "required,gt=0")
if len(errList) > 0 {
response.Fail(c, 101, errList[0])
return
@@ -153,8 +153,8 @@ func (ct *Group) Delete(c *gin.Context) {
response.Success(c, nil)
return
}
response.Fail(c, 101, err.Error())
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
return
}
response.Fail(c, 101, "信息不存在")
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
}

View File

@@ -28,11 +28,11 @@ func (ct *Login) Login(c *gin.Context) {
f := &admin.Login{}
err := c.ShouldBindJSON(f)
if err != nil {
response.Fail(c, 101, "参数错误")
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
errList := global.Validator.ValidStruct(f)
errList := global.Validator.ValidStruct(c, f)
if len(errList) > 0 {
response.Fail(c, 101, errList[0])
return
@@ -40,7 +40,7 @@ func (ct *Login) Login(c *gin.Context) {
u := service.AllService.UserService.InfoByUsernamePassword(f.Username, f.Password)
if u.Id == 0 {
response.Fail(c, 101, "用户名或密码错误")
response.Fail(c, 101, response.TranslateMsg(c, "UsernameOrPasswordError"))
return
}

View File

@@ -33,7 +33,7 @@ func (ct *LoginLog) Detail(c *gin.Context) {
response.Success(c, u)
return
}
response.Fail(c, 101, "信息不存在")
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
return
}
@@ -53,7 +53,7 @@ func (ct *LoginLog) Detail(c *gin.Context) {
func (ct *LoginLog) List(c *gin.Context) {
query := &admin.LoginLogQuery{}
if err := c.ShouldBindQuery(query); err != nil {
response.Fail(c, 101, "参数错误")
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
u := service.AllService.UserService.CurUser(c)
@@ -82,11 +82,11 @@ func (ct *LoginLog) List(c *gin.Context) {
func (ct *LoginLog) Delete(c *gin.Context) {
f := &model.LoginLog{}
if err := c.ShouldBindJSON(f); err != nil {
response.Fail(c, 101, "系统错误")
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
id := f.Id
errList := global.Validator.ValidVar(id, "required,gt=0")
errList := global.Validator.ValidVar(c, id, "required,gt=0")
if len(errList) > 0 {
response.Fail(c, 101, errList[0])
return
@@ -94,7 +94,7 @@ func (ct *LoginLog) Delete(c *gin.Context) {
l := service.AllService.LoginLogService.InfoById(f.Id)
u := service.AllService.UserService.CurUser(c)
if !service.AllService.UserService.IsAdmin(u) && l.UserId != u.Id {
response.Fail(c, 101, "无权限")
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
return
}
if l.Id > 0 {
@@ -106,5 +106,5 @@ func (ct *LoginLog) Delete(c *gin.Context) {
response.Fail(c, 101, err.Error())
return
}
response.Fail(c, 101, "信息不存在")
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
}

View File

@@ -18,12 +18,12 @@ type Oauth struct {
func (o *Oauth) Info(c *gin.Context) {
code := c.Query("code")
if code == "" {
response.Fail(c, 101, "参数错误")
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
return
}
v := service.AllService.OauthService.GetOauthCache(code)
if v == nil {
response.Fail(c, 101, "信息不存在")
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
return
}
response.Success(c, v)
@@ -33,20 +33,20 @@ func (o *Oauth) ToBind(c *gin.Context) {
f := &adminReq.BindOauthForm{}
err := c.ShouldBindJSON(f)
if err != nil {
response.Fail(c, 101, "参数错误")
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
u := service.AllService.UserService.CurUser(c)
utr := service.AllService.UserService.UserThirdInfo(u.Id, f.Op)
if utr.Id > 0 {
response.Fail(c, 101, "已绑定过了")
response.Fail(c, 101, response.TranslateMsg(c, "OauthHasBindOtherUser"))
return
}
err, code, url := service.AllService.OauthService.BeginAuth(f.Op)
if err != nil {
response.Error(c, err.Error())
response.Error(c, response.TranslateMsg(c, err.Error()))
return
}
@@ -89,22 +89,22 @@ func (o *Oauth) BindConfirm(c *gin.Context) {
j := &adminReq.OauthConfirmForm{}
err := c.ShouldBindJSON(j)
if err != nil {
response.Fail(c, 101, "参数错误"+err.Error())
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
if j.Code == "" {
response.Fail(c, 101, "参数错误: code 不存在")
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
return
}
v := service.AllService.OauthService.GetOauthCache(j.Code)
if v == nil {
response.Fail(c, 101, "授权已过期")
response.Fail(c, 101, response.TranslateMsg(c, "OauthExpired"))
return
}
u := service.AllService.UserService.CurUser(c)
err = service.AllService.OauthService.BindGithubUser(v.ThirdOpenId, v.ThirdOpenId, u.Id)
if err != nil {
response.Fail(c, 101, "绑定失败,请重试")
response.Fail(c, 101, response.TranslateMsg(c, "BindFail"))
return
}
@@ -117,22 +117,30 @@ func (o *Oauth) Unbind(c *gin.Context) {
f := &adminReq.UnBindOauthForm{}
err := c.ShouldBindJSON(f)
if err != nil {
response.Fail(c, 101, "参数错误")
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
u := service.AllService.UserService.CurUser(c)
utr := service.AllService.UserService.UserThirdInfo(u.Id, f.Op)
if utr.Id == 0 {
response.Fail(c, 101, "未绑定")
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
return
}
if f.Op == model.OauthTypeGithub {
err = service.AllService.OauthService.UnBindGithubUser(u.Id)
if err != nil {
response.Fail(c, 101, "解绑失败")
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
return
}
}
if f.Op == model.OauthTypeGoogle {
err = service.AllService.OauthService.UnBindGoogleUser(u.Id)
if err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
return
}
}
response.Success(c, nil)
}
@@ -155,7 +163,7 @@ func (o *Oauth) Detail(c *gin.Context) {
response.Success(c, u)
return
}
response.Fail(c, 101, "信息不存在")
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
return
}
@@ -173,10 +181,10 @@ func (o *Oauth) Detail(c *gin.Context) {
func (o *Oauth) Create(c *gin.Context) {
f := &admin.OauthForm{}
if err := c.ShouldBindJSON(f); err != nil {
response.Fail(c, 101, "参数错误"+err.Error())
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
errList := global.Validator.ValidStruct(f)
errList := global.Validator.ValidStruct(c, f)
if len(errList) > 0 {
response.Fail(c, 101, errList[0])
return
@@ -184,14 +192,14 @@ func (o *Oauth) Create(c *gin.Context) {
ex := service.AllService.OauthService.InfoByOp(f.Op)
if ex.Id > 0 {
response.Fail(c, 101, "已存在"+f.Op)
response.Fail(c, 101, response.TranslateMsg(c, "ItemExists"))
return
}
u := f.ToOauth()
err := service.AllService.OauthService.Create(u)
if err != nil {
response.Fail(c, 101, "创建失败")
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
return
}
response.Success(c, u)
@@ -212,7 +220,7 @@ func (o *Oauth) Create(c *gin.Context) {
func (o *Oauth) List(c *gin.Context) {
query := &admin.PageQuery{}
if err := c.ShouldBindQuery(query); err != nil {
response.Fail(c, 101, "参数错误")
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
res := service.AllService.OauthService.List(query.Page, query.PageSize, nil)
@@ -233,14 +241,14 @@ func (o *Oauth) List(c *gin.Context) {
func (o *Oauth) Update(c *gin.Context) {
f := &admin.OauthForm{}
if err := c.ShouldBindJSON(f); err != nil {
response.Fail(c, 101, "参数错误")
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
if f.Id == 0 {
response.Fail(c, 101, "参数错误")
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
return
}
errList := global.Validator.ValidStruct(f)
errList := global.Validator.ValidStruct(c, f)
if len(errList) > 0 {
response.Fail(c, 101, errList[0])
return
@@ -248,7 +256,7 @@ func (o *Oauth) Update(c *gin.Context) {
u := f.ToOauth()
err := service.AllService.OauthService.Update(u)
if err != nil {
response.Fail(c, 101, "更新失败")
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
return
}
response.Success(c, nil)
@@ -268,11 +276,11 @@ func (o *Oauth) Update(c *gin.Context) {
func (o *Oauth) Delete(c *gin.Context) {
f := &admin.OauthForm{}
if err := c.ShouldBindJSON(f); err != nil {
response.Fail(c, 101, "系统错误")
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
id := f.Id
errList := global.Validator.ValidVar(id, "required,gt=0")
errList := global.Validator.ValidVar(c, id, "required,gt=0")
if len(errList) > 0 {
response.Fail(c, 101, errList[0])
return
@@ -287,5 +295,5 @@ func (o *Oauth) Delete(c *gin.Context) {
response.Fail(c, 101, err.Error())
return
}
response.Fail(c, 101, "信息不存在")
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
}

View File

@@ -12,10 +12,10 @@ import (
type Peer struct {
}
// Detail 机器
// @Tags 机器
// @Summary 机器详情
// @Description 机器详情
// Detail 设备
// @Tags 设备
// @Summary 设备详情
// @Description 设备详情
// @Accept json
// @Produce json
// @Param id path int true "ID"
@@ -31,17 +31,17 @@ func (ct *Peer) Detail(c *gin.Context) {
response.Success(c, u)
return
}
response.Fail(c, 101, "信息不存在")
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
return
}
// Create 创建机器
// @Tags 机器
// @Summary 创建机器
// @Description 创建机器
// Create 创建设备
// @Tags 设备
// @Summary 创建设备
// @Description 创建设备
// @Accept json
// @Produce json
// @Param body body admin.PeerForm true "机器信息"
// @Param body body admin.PeerForm true "设备信息"
// @Success 200 {object} response.Response{data=model.Peer}
// @Failure 500 {object} response.Response
// @Router /admin/peer/create [post]
@@ -49,10 +49,10 @@ func (ct *Peer) Detail(c *gin.Context) {
func (ct *Peer) Create(c *gin.Context) {
f := &admin.PeerForm{}
if err := c.ShouldBindJSON(f); err != nil {
response.Fail(c, 101, "参数错误")
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
errList := global.Validator.ValidStruct(f)
errList := global.Validator.ValidStruct(c, f)
if len(errList) > 0 {
response.Fail(c, 101, errList[0])
return
@@ -60,16 +60,16 @@ func (ct *Peer) Create(c *gin.Context) {
u := f.ToPeer()
err := service.AllService.PeerService.Create(u)
if err != nil {
response.Fail(c, 101, "创建失败")
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
return
}
response.Success(c, u)
}
// List 列表
// @Tags 机器
// @Summary 机器列表
// @Description 机器列表
// @Tags 设备
// @Summary 设备列表
// @Description 设备列表
// @Accept json
// @Produce json
// @Param page query int false "页码"
@@ -81,7 +81,7 @@ func (ct *Peer) Create(c *gin.Context) {
func (ct *Peer) List(c *gin.Context) {
query := &admin.PageQuery{}
if err := c.ShouldBindQuery(query); err != nil {
response.Fail(c, 101, "参数错误")
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
res := service.AllService.PeerService.List(query.Page, query.PageSize, nil)
@@ -89,12 +89,12 @@ func (ct *Peer) List(c *gin.Context) {
}
// Update 编辑
// @Tags 机器
// @Summary 机器编辑
// @Description 机器编辑
// @Tags 设备
// @Summary 设备编辑
// @Description 设备编辑
// @Accept json
// @Produce json
// @Param body body admin.PeerForm true "机器信息"
// @Param body body admin.PeerForm true "设备信息"
// @Success 200 {object} response.Response{data=model.Peer}
// @Failure 500 {object} response.Response
// @Router /admin/peer/update [post]
@@ -102,14 +102,14 @@ func (ct *Peer) List(c *gin.Context) {
func (ct *Peer) Update(c *gin.Context) {
f := &admin.PeerForm{}
if err := c.ShouldBindJSON(f); err != nil {
response.Fail(c, 101, "参数错误")
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
if f.RowId == 0 {
response.Fail(c, 101, "参数错误")
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
return
}
errList := global.Validator.ValidStruct(f)
errList := global.Validator.ValidStruct(c, f)
if len(errList) > 0 {
response.Fail(c, 101, errList[0])
return
@@ -117,19 +117,19 @@ func (ct *Peer) Update(c *gin.Context) {
u := f.ToPeer()
err := service.AllService.PeerService.Update(u)
if err != nil {
response.Fail(c, 101, "更新失败")
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
return
}
response.Success(c, nil)
}
// Delete 删除
// @Tags 机器
// @Summary 机器删除
// @Description 机器删除
// @Tags 设备
// @Summary 设备删除
// @Description 设备删除
// @Accept json
// @Produce json
// @Param body body admin.PeerForm true "机器信息"
// @Param body body admin.PeerForm true "设备信息"
// @Success 200 {object} response.Response
// @Failure 500 {object} response.Response
// @Router /admin/peer/delete [post]
@@ -137,11 +137,11 @@ func (ct *Peer) Update(c *gin.Context) {
func (ct *Peer) Delete(c *gin.Context) {
f := &admin.PeerForm{}
if err := c.ShouldBindJSON(f); err != nil {
response.Fail(c, 101, "系统错误")
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
id := f.RowId
errList := global.Validator.ValidVar(id, "required,gt=0")
errList := global.Validator.ValidVar(c, id, "required,gt=0")
if len(errList) > 0 {
response.Fail(c, 101, errList[0])
return
@@ -153,8 +153,8 @@ func (ct *Peer) Delete(c *gin.Context) {
response.Success(c, nil)
return
}
response.Fail(c, 101, err.Error())
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
return
}
response.Fail(c, 101, "信息不存在")
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
}

View File

@@ -30,14 +30,14 @@ func (ct *Tag) Detail(c *gin.Context) {
t := service.AllService.TagService.InfoById(uint(iid))
u := service.AllService.UserService.CurUser(c)
if !service.AllService.UserService.IsAdmin(u) && t.UserId != u.Id {
response.Fail(c, 101, "无权限")
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
return
}
if t.Id > 0 {
response.Success(c, t)
return
}
response.Fail(c, 101, "信息不存在")
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
return
}
@@ -55,10 +55,10 @@ func (ct *Tag) Detail(c *gin.Context) {
func (ct *Tag) Create(c *gin.Context) {
f := &admin.TagForm{}
if err := c.ShouldBindJSON(f); err != nil {
response.Fail(c, 101, "参数错误")
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
errList := global.Validator.ValidStruct(f)
errList := global.Validator.ValidStruct(c, f)
if len(errList) > 0 {
response.Fail(c, 101, errList[0])
return
@@ -70,7 +70,7 @@ func (ct *Tag) Create(c *gin.Context) {
}
err := service.AllService.TagService.Create(t)
if err != nil {
response.Fail(c, 101, "创建失败")
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
return
}
response.Success(c, u)
@@ -93,7 +93,7 @@ func (ct *Tag) Create(c *gin.Context) {
func (ct *Tag) List(c *gin.Context) {
query := &admin.TagQuery{}
if err := c.ShouldBindQuery(query); err != nil {
response.Fail(c, 101, "参数错误")
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
u := service.AllService.UserService.CurUser(c)
@@ -122,27 +122,27 @@ func (ct *Tag) List(c *gin.Context) {
func (ct *Tag) Update(c *gin.Context) {
f := &admin.TagForm{}
if err := c.ShouldBindJSON(f); err != nil {
response.Fail(c, 101, "参数错误")
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
errList := global.Validator.ValidStruct(f)
errList := global.Validator.ValidStruct(c, f)
if len(errList) > 0 {
response.Fail(c, 101, errList[0])
return
}
if f.Id == 0 {
response.Fail(c, 101, "参数错误")
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
return
}
t := f.ToTag()
u := service.AllService.UserService.CurUser(c)
if !service.AllService.UserService.IsAdmin(u) && t.UserId != u.Id {
response.Fail(c, 101, "无权限")
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
return
}
err := service.AllService.TagService.Update(t)
if err != nil {
response.Fail(c, 101, "更新失败")
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
return
}
response.Success(c, nil)
@@ -162,11 +162,11 @@ func (ct *Tag) Update(c *gin.Context) {
func (ct *Tag) Delete(c *gin.Context) {
f := &admin.TagForm{}
if err := c.ShouldBindJSON(f); err != nil {
response.Fail(c, 101, "系统错误")
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
id := f.Id
errList := global.Validator.ValidVar(id, "required,gt=0")
errList := global.Validator.ValidVar(c, id, "required,gt=0")
if len(errList) > 0 {
response.Fail(c, 101, errList[0])
return
@@ -174,7 +174,7 @@ func (ct *Tag) Delete(c *gin.Context) {
t := service.AllService.TagService.InfoById(f.Id)
u := service.AllService.UserService.CurUser(c)
if !service.AllService.UserService.IsAdmin(u) && t.UserId != u.Id {
response.Fail(c, 101, "无权限")
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
return
}
if u.Id > 0 {
@@ -186,5 +186,5 @@ func (ct *Tag) Delete(c *gin.Context) {
response.Fail(c, 101, err.Error())
return
}
response.Fail(c, 101, "信息不存在")
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
}

View File

@@ -33,7 +33,7 @@ func (ct *User) Detail(c *gin.Context) {
response.Success(c, u)
return
}
response.Fail(c, 101, "信息不存在")
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
return
}
@@ -51,10 +51,10 @@ func (ct *User) Detail(c *gin.Context) {
func (ct *User) Create(c *gin.Context) {
f := &admin.UserForm{}
if err := c.ShouldBindJSON(f); err != nil {
response.Fail(c, 101, "参数错误")
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
errList := global.Validator.ValidStruct(f)
errList := global.Validator.ValidStruct(c, f)
if len(errList) > 0 {
response.Fail(c, 101, errList[0])
return
@@ -62,7 +62,7 @@ func (ct *User) Create(c *gin.Context) {
u := f.ToUser()
err := service.AllService.UserService.Create(u)
if err != nil {
response.Fail(c, 101, "创建失败")
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
return
}
response.Success(c, u)
@@ -84,7 +84,7 @@ func (ct *User) Create(c *gin.Context) {
func (ct *User) List(c *gin.Context) {
query := &admin.UserQuery{}
if err := c.ShouldBindQuery(query); err != nil {
response.Fail(c, 101, "参数错误")
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
res := service.AllService.UserService.List(query.Page, query.PageSize, func(tx *gorm.DB) {
@@ -109,14 +109,14 @@ func (ct *User) List(c *gin.Context) {
func (ct *User) Update(c *gin.Context) {
f := &admin.UserForm{}
if err := c.ShouldBindJSON(f); err != nil {
response.Fail(c, 101, "参数错误:"+err.Error())
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
if f.Id == 0 {
response.Fail(c, 101, "参数错误")
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
return
}
errList := global.Validator.ValidStruct(f)
errList := global.Validator.ValidStruct(c, f)
if len(errList) > 0 {
response.Fail(c, 101, errList[0])
return
@@ -124,7 +124,7 @@ func (ct *User) Update(c *gin.Context) {
u := f.ToUser()
err := service.AllService.UserService.Update(u)
if err != nil {
response.Fail(c, 101, "更新失败")
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
return
}
response.Success(c, nil)
@@ -144,11 +144,11 @@ func (ct *User) Update(c *gin.Context) {
func (ct *User) Delete(c *gin.Context) {
f := &admin.UserForm{}
if err := c.ShouldBindJSON(f); err != nil {
response.Fail(c, 101, "系统错误")
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
id := f.Id
errList := global.Validator.ValidVar(id, "required,gt=0")
errList := global.Validator.ValidVar(c, id, "required,gt=0")
if len(errList) > 0 {
response.Fail(c, 101, errList[0])
return
@@ -163,7 +163,7 @@ func (ct *User) Delete(c *gin.Context) {
response.Fail(c, 101, err.Error())
return
}
response.Fail(c, 101, "信息不存在")
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
}
// UpdatePassword 修改密码
@@ -180,22 +180,22 @@ func (ct *User) Delete(c *gin.Context) {
func (ct *User) UpdatePassword(c *gin.Context) {
f := &admin.UserPasswordForm{}
if err := c.ShouldBindJSON(f); err != nil {
response.Fail(c, 101, "参数错误")
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
errList := global.Validator.ValidStruct(f)
errList := global.Validator.ValidStruct(c, f)
if len(errList) > 0 {
response.Fail(c, 101, errList[0])
return
}
u := service.AllService.UserService.InfoById(f.Id)
if u.Id == 0 {
response.Fail(c, 101, "信息不存在")
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
return
}
err := service.AllService.UserService.UpdatePassword(u, f.Password)
if err != nil {
response.Fail(c, 101, "更新失败")
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
return
}
response.Success(c, nil)
@@ -237,11 +237,11 @@ func (ct *User) Current(c *gin.Context) {
func (ct *User) ChangeCurPwd(c *gin.Context) {
f := &admin.ChangeCurPasswordForm{}
if err := c.ShouldBindJSON(f); err != nil {
response.Fail(c, 101, "参数错误")
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
errList := global.Validator.ValidStruct(f)
errList := global.Validator.ValidStruct(c, f)
if len(errList) > 0 {
response.Fail(c, 101, errList[0])
return
@@ -249,12 +249,12 @@ func (ct *User) ChangeCurPwd(c *gin.Context) {
u := service.AllService.UserService.CurUser(c)
oldPwd := service.AllService.UserService.EncryptPassword(f.OldPassword)
if u.Password != oldPwd {
response.Fail(c, 101, "旧密码错误")
response.Fail(c, 101, response.TranslateMsg(c, "OldPasswordError"))
return
}
err := service.AllService.UserService.UpdatePassword(u, f.NewPassword)
if err != nil {
response.Fail(c, 101, "更新失败")
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
return
}
response.Success(c, nil)

View File

@@ -1,14 +1,17 @@
package api
import (
"Gwen/global"
requstform "Gwen/http/request/api"
"Gwen/http/response"
"Gwen/http/response/api"
"Gwen/model"
"Gwen/service"
"encoding/json"
"fmt"
"github.com/gin-gonic/gin"
"net/http"
"strconv"
)
type Ab struct {
@@ -65,37 +68,30 @@ func (a *Ab) UpAb(c *gin.Context) {
abf := &requstform.AddressBookForm{}
err := c.ShouldBindJSON(&abf)
if err != nil {
response.Error(c, "参数错误")
response.Error(c, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
abd := &requstform.AddressBookFormData{}
err = json.Unmarshal([]byte(abf.Data), abd)
if err != nil {
response.Error(c, "系统错误")
response.Error(c, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
tc := map[string]uint{}
err = json.Unmarshal([]byte(abd.TagColors), &tc)
if err != nil {
response.Error(c, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
//fmt.Println(abd)
//for _, peer := range abd.Peers {
// fmt.Println(peer)
//}
user := service.AllService.UserService.CurUser(c)
err = service.AllService.AddressBookService.UpdateAddressBook(abd.Peers, user.Id)
if err != nil {
c.Abort()
response.Error(c, response.TranslateMsg(c, "OperationFailed")+err.Error())
return
}
tc := map[string]uint{}
err = json.Unmarshal([]byte(abd.TagColors), &tc)
if err != nil {
response.Error(c, "系统错误")
return
} else {
service.AllService.TagService.UpdateTags(user.Id, tc)
}
service.AllService.TagService.UpdateTags(user.Id, tc)
c.JSON(http.StatusOK, nil)
}
@@ -118,7 +114,7 @@ func (a *Ab) Tags(c *gin.Context) {
}
// TagAdd
// @Tags 地址
// @Tags 地址[Personal]
// @Summary 标签添加
// @Description 标签
// @Accept json
@@ -131,16 +127,361 @@ func (a *Ab) TagAdd(c *gin.Context) {
t := &model.Tag{}
err := c.ShouldBindJSON(t)
if err != nil {
response.Error(c, "参数错误")
response.Error(c, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
//u := service.AllService.UserService.CurUser(c)
//err = service.AllService.TagService.UpdateTags(t.Name, t.Color, user.Id)
//if err != nil {
// response.Error(c, "操作失败")
// return
//}
c.JSON(http.StatusOK, "")
u := service.AllService.UserService.CurUser(c)
tag := service.AllService.TagService.InfoByUserIdAndName(u.Id, t.Name)
if tag != nil && tag.Id != 0 {
response.Error(c, response.TranslateMsg(c, "ItemExists"))
return
}
t.UserId = u.Id
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
// @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)
tag := service.AllService.TagService.InfoByUserIdAndName(u.Id, t.Old)
if tag == nil || tag.Id == 0 {
response.Error(c, response.TranslateMsg(c, "ItemNotFound"))
return
}
ntag := service.AllService.TagService.InfoByUserIdAndName(u.Id, t.New)
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
// @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)
tag := service.AllService.TagService.InfoByUserIdAndName(u.Id, t.Name)
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
// @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)
for _, name := range *t {
tag := service.AllService.TagService.InfoByUserIdAndName(u.Id, name)
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 := strconv.Itoa(int(user.GroupId)) + "-" + strconv.Itoa(int(user.Id))
//如果返回了guid后面的请求会有变化
c.JSON(http.StatusOK, gin.H{
"guid": guid,
"name": user.Username,
"rule": 0,
})
} 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 string body string false "string valid"
// @Success 200 {object} response.Response
// @Failure 500 {object} response.Response
// @Router /ab/shared/profiles [post]
// @Security BearerAuth
func (a *Ab) SharedProfiles(c *gin.Context) {
//AbProfile.fromJson(Map<String, dynamic> json)
//: guid = json['guid'] ?? '',
// name = json['name'] ?? '',
// owner = json['owner'] ?? '',
// note = json['note'] ?? '',
// rule = json['rule'] ?? 0;
//暂时没必要返回数据,可能是为了共享地址簿
/*item := map[string]interface{}{
"guid": "1",
"name": "admin",
"owner": "admin",
"note": "admin11",
"rule": 0,
}
item2 := map[string]interface{}{
"guid": "2",
"name": "admin2",
"owner": "admin2",
"note": "admin22",
"rule": 0,
}
c.JSON(http.StatusOK, gin.H{
"total": 2,
"data": []interface{}{item, item2},
})*/
c.JSON(http.StatusOK, gin.H{
"total": 0,
"data": nil,
})
}
// Peers
// @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/peers [post]
// @Security BearerAuth
func (a *Ab) Peers(c *gin.Context) {
user := service.AllService.UserService.CurUser(c)
al := service.AllService.AddressBookService.ListByUserId(user.Id, 1, 1000)
c.JSON(http.StatusOK, gin.H{
"total": al.Total,
"data": al.AddressBooks,
"licensed_devices": 99999,
})
}
// PTags
// @Tags 地址[Personal]
// @Summary 标签
// @Description 标签
// @Accept json
// @Produce json
// @Param id path string true "id"
// @Success 200 {object} model.TagList
// @Failure 500 {object} response.ErrorResponse
// @Router /ab/tags/{guid} [post]
// @Security BearerAuth
func (a *Ab) PTags(c *gin.Context) {
user := service.AllService.UserService.CurUser(c)
tags := service.AllService.TagService.ListByUserId(user.Id)
c.JSON(http.StatusOK, tags.Tags)
}
// PeerAdd
// @Tags 地址[Personal]
// @Summary 添加地址
// @Description 添加地址
// @Accept json
// @Produce json
// @Param id path string true "id"
// @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
}
fmt.Println(f)
u := service.AllService.UserService.CurUser(c)
f.UserId = u.Id
ab := f.ToAddressBook()
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 id path string true "id"
// @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)
for _, id := range *f {
ab := service.AllService.AddressBookService.InfoByUserIdAndId(u.Id, id)
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 id path string true "id"
// @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
}
//fmt.Println(f)
//return
u := service.AllService.UserService.CurUser(c)
ab := service.AllService.AddressBookService.InfoByUserIdAndId(u.Id, f.Id)
if ab == nil || ab.RowId == 0 {
response.Error(c, response.TranslateMsg(c, "ItemNotFound"))
return
}
nab := f.ToAddressBook()
nab.RowId = ab.RowId
err = service.AllService.AddressBookService.Update(nab)
if err != nil {
response.Error(c, response.TranslateMsg(c, "OperationFailed")+err.Error())
return
}
c.String(http.StatusOK, "")
}

View File

@@ -33,7 +33,7 @@ func (g *Group) Users(c *gin.Context) {
if !*u.IsAdmin {
gr := service.AllService.GroupService.InfoById(u.GroupId)
if gr.Type != model.GroupTypeShare {
response.Error(c, "不是管理员也不在分享组")
response.Error(c, response.TranslateMsg(c, "NoAccess"))
return
}
}
@@ -77,7 +77,7 @@ func (g *Group) Peers(c *gin.Context) {
if !*u.IsAdmin {
gr := service.AllService.GroupService.InfoById(u.GroupId)
if gr.Type != model.GroupTypeShare {
response.Error(c, "不是管理员也不在分享组")
response.Error(c, response.TranslateMsg(c, "NoAccess"))
return
}
}
@@ -96,15 +96,15 @@ func (g *Group) Peers(c *gin.Context) {
namesById[user.Id] = user.Username
userIds = append(userIds, user.Id)
}
peerList := service.AllService.AddressBookService.ListByUserIds(userIds, q.Page, q.PageSize)
peerList := service.AllService.PeerService.ListByUserIds(userIds, q.Page, q.PageSize)
var data []*apiResp.GroupPeerPayload
for _, ab := range peerList.AddressBooks {
uname, ok := namesById[ab.UserId]
for _, peer := range peerList.Peers {
uname, ok := namesById[peer.UserId]
if !ok {
uname = ""
}
pp := &apiResp.GroupPeerPayload{}
pp.FromAddressBook(ab, uname)
pp.FromPeer(peer, uname)
data = append(data, pp)
}

View File

@@ -35,5 +35,10 @@ func (i *Index) Index(c *gin.Context) {
// @Failure 500 {object} response.Response
// @Router /heartbeat [post]
func (i *Index) Heartbeat(c *gin.Context) {
//b := &gin.H{}
//err := c.BindJSON(b)
//body : &map[id:xxx modified_at:0 uuid:NGIxZTZjM2YtNmNkMy00YTMwLWFiNjQtMzQ0MTA0NGE5ZDgz ver:1.003e+06]
//fmt.Println(b, err, c.Request.Header)
//header : map[Accept:[*/*] Accept-Encoding:[gzip] Content-Length:[105] Content-Type:[application/json]]
c.JSON(http.StatusOK, gin.H{})
}

View File

@@ -30,11 +30,11 @@ func (l *Login) Login(c *gin.Context) {
err := c.ShouldBindJSON(f)
//fmt.Println(f)
if err != nil {
response.Error(c, "参数错误")
response.Error(c, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
errList := global.Validator.ValidStruct(f)
errList := global.Validator.ValidStruct(c, f)
if len(errList) > 0 {
response.Error(c, errList[0])
return
@@ -43,7 +43,7 @@ func (l *Login) Login(c *gin.Context) {
u := service.AllService.UserService.InfoByUsernamePassword(f.Username, f.Password)
if u.Id == 0 {
response.Error(c, "用户名或密码错误")
response.Error(c, response.TranslateMsg(c, "UsernameOrPasswordError"))
return
}
@@ -95,7 +95,7 @@ func (l *Login) LoginOptions(c *gin.Context) {
}
common, err := json.Marshal(oidcItems)
if err != nil {
response.Error(c, "参数错误")
response.Error(c, response.TranslateMsg(c, "SystemError")+err.Error())
return
}
var res []string

View File

@@ -29,17 +29,17 @@ func (o *Oauth) OidcAuth(c *gin.Context) {
f := &api.OidcAuthRequest{}
err := c.ShouldBindJSON(&f)
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, "参数错误")
response.Error(c, response.TranslateMsg(c, "ParamsError"))
return
}
err, code, url := service.AllService.OauthService.BeginAuth(f.Op)
if err != nil {
response.Error(c, err.Error())
response.Error(c, response.TranslateMsg(c, err.Error()))
return
}
@@ -72,12 +72,12 @@ func (o *Oauth) OidcAuthQuery(c *gin.Context) {
q := &api.OidcAuthQuery{}
err := c.ShouldBindQuery(q)
if err != nil {
response.Error(c, "参数错误")
response.Error(c, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
v := service.AllService.OauthService.GetOauthCache(q.Code)
if v == nil {
response.Error(c, "授权已过期,请重新授权")
response.Error(c, response.TranslateMsg(c, "OauthExpired"))
return
}
if v.UserId == 0 {
@@ -87,24 +87,20 @@ func (o *Oauth) OidcAuthQuery(c *gin.Context) {
}
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, "用户不存在")
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),
})
}
// OauthCallback 回调
@@ -119,7 +115,7 @@ func (o *Oauth) OidcAuthQuery(c *gin.Context) {
func (o *Oauth) OauthCallback(c *gin.Context) {
state := c.Query("state")
if state == "" {
c.String(http.StatusInternalServerError, "state为空")
c.String(http.StatusInternalServerError, response.TranslateParamMsg(c, "ParamIsEmpty", "state"))
return
}
@@ -127,7 +123,7 @@ func (o *Oauth) OauthCallback(c *gin.Context) {
//从缓存中获取
v := service.AllService.OauthService.GetOauthCache(cacheKey)
if v == nil {
c.String(http.StatusInternalServerError, "授权已过期,请重新授权")
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "OauthExpired"))
return
}
@@ -138,34 +134,34 @@ func (o *Oauth) OauthCallback(c *gin.Context) {
code := c.Query("code")
err, userData := service.AllService.OauthService.GithubCallback(code)
if err != nil {
c.String(http.StatusInternalServerError, "授权失败:"+err.Error())
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "OauthFailed")+response.TranslateMsg(c, 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, "已经绑定其他账号")
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "OauthHasBindOtherUser"))
return
}
//绑定
u := service.AllService.UserService.InfoById(v.UserId)
if u == nil {
c.String(http.StatusInternalServerError, "用户不存在")
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "ItemNotFound"))
return
}
//绑定github
err = service.AllService.OauthService.BindGithubUser(strconv.Itoa(userData.Id), userData.Login, v.UserId)
if err != nil {
c.String(http.StatusInternalServerError, "绑定失败")
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "BindFail"))
return
}
c.String(http.StatusOK, "绑定成功")
c.String(http.StatusOK, response.TranslateMsg(c, "BindSuccess"))
return
} else if ac == service.OauthActionTypeLogin {
//登录
if v.UserId != 0 {
c.String(http.StatusInternalServerError, "授权已经成功")
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "OauthHasBeenSuccess"))
return
}
u := service.AllService.UserService.InfoByGithubId(strconv.Itoa(userData.Id))
@@ -183,19 +179,16 @@ func (o *Oauth) OauthCallback(c *gin.Context) {
//自动注册
u = service.AllService.UserService.RegisterByGithub(userData.Login, strconv.Itoa(userData.Id))
if u.Id == 0 {
c.String(http.StatusInternalServerError, "注册失败")
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "OauthRegisterFailed"))
return
}
}
v.UserId = u.Id
service.AllService.OauthService.SetOauthCache(cacheKey, v, 0)
c.String(http.StatusOK, "授权成功")
c.String(http.StatusOK, response.TranslateMsg(c, "OauthSuccess"))
return
}
//返回js
c.Header("Content-Type", "text/html; charset=utf-8")
c.String(http.StatusOK, "授权错误")
}
@@ -203,7 +196,7 @@ func (o *Oauth) OauthCallback(c *gin.Context) {
code := c.Query("code")
err, userData := service.AllService.OauthService.GoogleCallback(code)
if err != nil {
c.String(http.StatusInternalServerError, "授权失败:"+err.Error())
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "OauthFailed")+response.TranslateMsg(c, err.Error()))
return
}
//将空格替换成_
@@ -212,26 +205,26 @@ func (o *Oauth) OauthCallback(c *gin.Context) {
//fmt.Println("bind", ty, userData)
utr := service.AllService.OauthService.UserThirdInfo(ty, userData.Email)
if utr.UserId > 0 {
c.String(http.StatusInternalServerError, "已经绑定其他账号")
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "OauthHasBindOtherUser"))
return
}
//绑定
u := service.AllService.UserService.InfoById(v.UserId)
if u == nil {
c.String(http.StatusInternalServerError, "用户不存在")
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "ItemNotFound"))
return
}
//绑定
err = service.AllService.OauthService.BindGoogleUser(userData.Email, googleName, v.UserId)
if err != nil {
c.String(http.StatusInternalServerError, "绑定失败")
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "BindFail"))
return
}
c.String(http.StatusOK, "绑定成功")
c.String(http.StatusOK, response.TranslateMsg(c, "BindSuccess"))
return
} else if ac == service.OauthActionTypeLogin {
if v.UserId != 0 {
c.String(http.StatusInternalServerError, "授权已经成功")
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "OauthHasBeenSuccess"))
return
}
u := service.AllService.UserService.InfoByGoogleEmail(userData.Email)
@@ -250,30 +243,17 @@ func (o *Oauth) OauthCallback(c *gin.Context) {
//自动注册
u = service.AllService.UserService.RegisterByGoogle(googleName, userData.Email)
if u.Id == 0 {
c.String(http.StatusInternalServerError, "注册失败")
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "OauthRegisterFailed"))
return
}
}
v.UserId = u.Id
service.AllService.OauthService.SetOauthCache(cacheKey, v, 0)
c.String(http.StatusOK, "授权成功")
c.String(http.StatusOK, response.TranslateMsg(c, "OauthSuccess"))
return
}
}
c.String(http.StatusInternalServerError, "授权配置错误,请联系管理员")
}
// WebOauthLogin
// @Tags Oauth
// @Summary WebOauthLogin
// @Description WebOauthLogin
// @Accept json
// @Produce json
// @Success 200 {string} string
// @Failure 500 {string} string
// @Router /oauth/login [get]
func (o *Oauth) WebOauthLogin(c *gin.Context) {
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "SystemError"))
}

View File

@@ -27,7 +27,7 @@ func (p *Peer) SysInfo(c *gin.Context) {
f := &requstform.PeerForm{}
err := c.ShouldBindBodyWith(f, binding.JSON)
if err != nil {
response.Error(c, err.Error())
response.Error(c, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
@@ -36,7 +36,7 @@ func (p *Peer) SysInfo(c *gin.Context) {
pe = f.ToPeer()
err = service.AllService.PeerService.Create(pe)
if err != nil {
response.Error(c, err.Error())
response.Error(c, response.TranslateMsg(c, "OperationFailed")+err.Error())
return
}
}

View File

@@ -3,7 +3,6 @@ package api
import (
apiResp "Gwen/http/response/api"
"Gwen/service"
"fmt"
"github.com/gin-gonic/gin"
"net/http"
)
@@ -42,33 +41,3 @@ func (u *User) Info(c *gin.Context) {
up := (&apiResp.UserPayload{}).FromUser(user)
c.JSON(http.StatusOK, up)
}
// Personal
// @Tags 用户
// @Summary 个人信息
// @Description 个人信息
// @Accept json
// @Produce json
// @Param string body string false "string valid"
// @Success 200 {object} response.Response
// @Failure 500 {object} response.Response
// @Router /ab/personal [post]
// @Security BearerAuth
func (u *User) Personal(c *gin.Context) {
//打印全部body
fmt.Println(c.Request.Body)
/**
guid = json['guid'] ?? '',
name = json['name'] ?? '',
owner = json['owner'] ?? '',
note = json['note'] ?? '',
rule = json['rule'] ?? 0;
*/
//如果返回了guid后面的请求会有变化
c.JSON(http.StatusOK, gin.H{
//"guid": "123456",
//"name": "admindddd",
//"rule": 1,
})
}

View File

@@ -8,6 +8,10 @@ import (
type Index struct {
}
func (i *Index) Index(c *gin.Context) {
c.Redirect(302, "/_admin/")
}
func (i *Index) ConfigJs(c *gin.Context) {
apiServer := global.Config.Rustdesk.ApiServer

View File

@@ -7,7 +7,7 @@ import (
func RustAuth() gin.HandlerFunc {
return func(c *gin.Context) {
//fmt.Println(c.Request.Header)
//获取HTTP_AUTHORIZATION
token := c.GetHeader("Authorization")
if token == "" {

View File

@@ -16,12 +16,12 @@ type AddressBookForm struct {
Tags []string `json:"tags"`
Hash string `json:"hash"`
UserId uint `json:"user_id"`
ForceAlwaysRelay bool `json:"force_always_relay"`
RdpPort string `json:"rdp_port"`
RdpUsername string `json:"rdp_username"`
ForceAlwaysRelay bool `json:"forceAlwaysRelay"`
RdpPort string `json:"rdpPort"`
RdpUsername string `json:"rdpUsername"`
Online bool `json:"online"`
LoginName string `json:"login_name" `
SameServer bool `json:"same_server"`
LoginName string `json:"loginName" `
SameServer bool `json:"sameServer"`
}
func (a AddressBookForm) ToAddressBook() *model.AddressBook {

View File

@@ -8,7 +8,7 @@ type UserForm struct {
Id uint `json:"id"`
Username string `json:"username" validate:"required,gte=4,lte=10"`
//Password string `json:"password" validate:"required,gte=4,lte=20"`
Nickname string `json:"nickname" validate:"required"`
Nickname string `json:"nickname"`
Avatar string `json:"avatar"`
GroupId uint `json:"group_id" validate:"required"`
IsAdmin *bool `json:"is_admin" `

View File

@@ -35,3 +35,39 @@ func (pf *PeerForm) ToPeer() *model.Peer {
Version: pf.Version,
}
}
// PersonalAddressBookForm 个人地址簿表单
type PersonalAddressBookForm struct {
model.AddressBook
ForceAlwaysRelay string `json:"forceAlwaysRelay"`
}
func (pabf *PersonalAddressBookForm) ToAddressBook() *model.AddressBook {
return &model.AddressBook{
RowId: pabf.RowId,
Id: pabf.Id,
Username: pabf.Username,
Password: pabf.Password,
Hostname: pabf.Hostname,
Alias: pabf.Alias,
Platform: pabf.Platform,
Tags: pabf.Tags,
Hash: pabf.Hash,
UserId: pabf.UserId,
ForceAlwaysRelay: pabf.ForceAlwaysRelay == "true",
RdpPort: pabf.RdpPort,
RdpUsername: pabf.RdpUsername,
Online: pabf.Online,
LoginName: pabf.LoginName,
SameServer: pabf.SameServer,
}
}
type TagRenameForm struct {
Old string `json:"old"`
New string `json:"new"`
}
type TagColorForm struct {
Name string `json:"name"`
Color uint `json:"color"`
}

View File

@@ -59,16 +59,13 @@ func (gpp *GroupPeerPayload) FromAddressBook(a *model.AddressBook, username stri
gpp.UserName = username
}
//func (gpp *GroupPeerPayload) FromPeer(p *model.Peer) {
// gpp.Id = p.Id
// gpp.Info = &PeerPayloadInfo{
// DeviceName: p.Hostname,
// Os: p.Os,
// Username: p.Username,
// }
// gpp.Note = ""
// if p.User.Id != 0 {
// //gpp.User = p.User.Username
// gpp.UserName = p.User.Username
// }
//}
func (gpp *GroupPeerPayload) FromPeer(p *model.Peer, username string) {
gpp.Id = p.Id
gpp.Info = &PeerPayloadInfo{
DeviceName: p.Hostname,
Os: p.Os,
Username: p.Username,
}
gpp.Note = ""
gpp.UserName = username
}

View File

@@ -1,7 +1,10 @@
package response
import (
"Gwen/global"
"fmt"
"github.com/gin-gonic/gin"
"github.com/nicksnyder/go-i18n/v2/i18n"
"net/http"
)
@@ -51,3 +54,48 @@ type ServerConfigResponse struct {
RelayServer string `json:"relay_server"`
ApiServer string `json:"api_server"`
}
func TranslateMsg(c *gin.Context, messageId string) string {
localizer := global.Localizer(c)
errMsg, err := localizer.LocalizeMessage(&i18n.Message{
ID: messageId,
})
if err != nil {
global.Logger.Warn("LocalizeMessage Error: " + err.Error())
errMsg = messageId
}
return errMsg
}
func TranslateTempMsg(c *gin.Context, messageId string, templateData map[string]interface{}) string {
localizer := global.Localizer(c)
errMsg, err := localizer.Localize(&i18n.LocalizeConfig{
DefaultMessage: &i18n.Message{
ID: messageId,
},
TemplateData: templateData,
})
if err != nil {
global.Logger.Warn("LocalizeMessage Error: " + err.Error())
errMsg = messageId
}
return errMsg
}
func TranslateParamMsg(c *gin.Context, messageId string, params ...string) string {
localizer := global.Localizer(c)
templateData := make(map[string]interface{})
for i, v := range params {
k := fmt.Sprintf("P%d", i)
templateData[k] = v
}
errMsg, err := localizer.Localize(&i18n.LocalizeConfig{
DefaultMessage: &i18n.Message{
ID: messageId,
},
TemplateData: templateData,
})
if err != nil {
global.Logger.Warn("LocalizeMessage Error: " + err.Error())
errMsg = messageId
}
return errMsg
}

View File

@@ -74,7 +74,38 @@ func ApiInit(g *gin.Engine) {
//更新地址
frg.POST("/ab", ab.UpAb)
}
PersonalRoutes(frg)
//访问静态文件
g.StaticFS("/upload", http.Dir(global.Config.Gin.ResourcesPath+"/public/upload"))
}
func PersonalRoutes(frg *gin.RouterGroup) {
{
ab := &api.Ab{}
frg.POST("/ab/personal", ab.Personal)
//[method:POST] [uri:/api/ab/settings] Request
frg.POST("/ab/settings", ab.Settings)
// [method:POST] [uri:/api/ab/shared/profiles?current=1&pageSize=100]
frg.POST("/ab/shared/profiles", ab.SharedProfiles)
//[method:POST] [uri:/api/ab/peers?current=1&pageSize=100&ab=1]
frg.POST("/ab/peers", ab.Peers)
// [method:POST] [uri:/api/ab/tags/1]
frg.POST("/ab/tags/:guid", ab.PTags)
//[method:POST] api/ab/peer/add/1
frg.POST("/ab/peer/add/:guid", ab.PeerAdd)
//[method:DELETE] [uri:/api/ab/peer/1]
frg.DELETE("/ab/peer/:guid", ab.PeerDel)
//[method:PUT] [uri:/api/ab/peer/update/1]
frg.PUT("/ab/peer/update/:guid", ab.PeerUpdate)
//[method:POST] [uri:/api/ab/tag/add/1]
frg.POST("/ab/tag/add/:guid", ab.TagAdd)
//[method:PUT] [uri:/api/ab/tag/rename/1]
frg.PUT("/ab/tag/rename/:guid", ab.TagRename)
//[method:PUT] [uri:/api/ab/tag/update/1]
frg.PUT("/ab/tag/update/:guid", ab.TagUpdate)
//[method:DELETE] [uri:/api/ab/tag/1]
frg.DELETE("/ab/tag/:guid", ab.TagDel)
}
}

View File

@@ -9,6 +9,7 @@ import (
func WebInit(g *gin.Engine) {
i := &web.Index{}
g.GET("/", i.Index)
g.GET("/webclient-config/index.js", i.ConfigJs)
g.StaticFS("/webclient", http.Dir(global.Config.Gin.ResourcesPath+"/web"))
g.StaticFS("/_admin", http.Dir(global.Config.Gin.ResourcesPath+"/admin"))

111
resources/i18n/en.toml Normal file
View File

@@ -0,0 +1,111 @@
[Test]
description = "test"
one = "test1 "
other = "Test2 {{.P0}}"
[ParamsError]
description = "Params validation failed."
one = "Params validation failed."
other = "Params validation failed."
[OperationFailed]
description = "OperationFailed."
one = "the operation failed."
other = "the operation failed."
[OperationSuccess]
description = "OperationSuccess."
one = "the operation success."
other = "the operation success."
[ItemExists]
description = "Item already exists."
one = "Item already exists."
other = "Item already exists."
[ItemNotFound]
description = "Item not found."
one = "Item not found."
other = "Item not found."
[NoAccess]
description = "No access."
one = "No access."
other = "No access."
[UsernameOrPasswordError]
description = "Username or password error."
one = "Username or password error."
other = "Username or password error."
[SystemError]
description = "System error."
one = "System error."
other = "System error."
[ConfigNotFound]
description = "Config not found."
one = "Config not found."
other = "Config not found."
[OauthExpired]
description = "Oauth expired."
one = "Oauth expired, please try again."
other = "Oauth expired,please try again."
[OauthFailed]
description = "Oauth failed."
one = "Oauth failed."
other = "Oauth failed."
[OauthHasBindOtherUser]
description = "Oauth has bind other user."
one = "Oauth has bind other user."
other = "Oauth has bind other user."
[ParamIsEmpty]
description = "Param is empty."
one = "{{.P0}} is empty."
other = "{{.P0}} is empty."
[BindFail]
description = "Bind fail."
one = "Bind fail."
other = "Bind fail."
[BindSuccess]
description = "Bind success."
one = "Bind success."
other = "Bind success."
[OauthHasBeenSuccess]
description = "Oauth has been success."
one = "Oauth has been success."
other = "Oauth has been success."
[OauthSuccess]
description = "Oauth success."
one = "Oauth success."
other = "Oauth success."
[OauthRegisterSuccess]
description = "Oauth register success."
one = "Oauth register success."
other = "Oauth register success."
[OauthRegisterFailed]
description = "Oauth register failed."
one = "Oauth register failed."
other = "Oauth register failed."
[GetOauthTokenError]
description = "Get oauth token error."
one = "Get oauth token error."
other = "Get oauth token error."
[GetOauthUserInfoError]
description = "Get oauth user info error."
one = "Get oauth user info error."
other = "Get oauth user info error."
[DecodeOauthUserInfoError]
description = "Decode oauth user info error."
one = "Decode oauth user info error."
other = "Decode oauth user info error."
[OldPasswordError]
description = "Old password error."
one = "Old password error."
other = "Old password error."

112
resources/i18n/zh_CN.toml Normal file
View File

@@ -0,0 +1,112 @@
[Test]
description = "test"
one = "测试1 {{.P0}}"
other = "测试2 {{.P0}}"
[ParamsError]
description = "Params validation failed."
one = "参数错误。"
other = "参数错误。"
[OperationFailed]
description = "OperationFailed."
one = "操作失败。"
other = "操作失败。"
[OperationSuccess]
description = "OperationSuccess."
one = "操作成功。"
other = "操作成功。"
[ItemExists]
description = "Item already exists."
one = "数据已存在。"
other = "数据已存在。"
[ItemNotFound]
description = "Item not found."
one = "数据不存在。"
other = "数据不存在。"
[NoAccess]
description = "No access."
one = "无权限。"
other = "无权限。"
[UsernameOrPasswordError]
description = "Username or password error."
one = "用户名或密码错误。"
other = "用户名或密码错误。"
[SystemError]
description = "System error."
one = "系统错误。"
other = "系统错误。"
[ConfigNotFound]
description = "Config not found."
one = "配置不存在。"
other = "配置不存在。"
#授权过期
[OauthExpired]
description = "Oauth expired."
one = "授权过期,请重新授权。"
other = "授权过期,请重新授权。"
[OauthFailed]
description = "Oauth failed."
one = "授权失败。"
other = "授权失败。"
[OauthHasBindOtherUser]
description = "Oauth has bind other user."
one = "授权已绑定其他用户。"
other = "授权已绑定其他用户。"
[ParamIsEmpty]
description = "Param is empty."
one = "{{.P0}} 为空。"
other = "{{.P0}} 为空。"
[BindFail]
description = "Bind fail."
one = "绑定失败。"
other = "绑定失败。"
[BindSuccess]
description = "Bind success."
one = "绑定成功。"
other = "绑定成功。"
[OauthHasBeenSuccess]
description = "Oauth has been success."
one = "授权已成功。"
other = "授权已成功。"
[OauthSuccess]
description = "Oauth success."
one = "授权成功。"
other = "授权成功。"
[OauthRegisterSuccess]
description = "Oauth register success."
one = "授权注册成功。"
other = "授权注册成功。"
[OauthRegisterFailed]
description = "Oauth register failed."
one = "授权注册失败。"
other = "授权注册失败。"
[GetOauthTokenError]
description = "Get oauth token error."
one = "获取授权token失败。"
other = "获取授权token失败。"
[GetOauthUserInfoError]
description = "Get oauth user info error."
one = "获取授权用户信息失败。"
other = "获取授权用户信息失败。"
[DecodeOauthUserInfoError]
description = "Decode oauth user info error."
one = "解析授权用户信息失败。"
other = "解析授权用户信息失败。"
[OldPasswordError]
description = "Old password error."
one = "旧密码错误。"
other = "旧密码错误。"

View File

@@ -9,11 +9,17 @@ import (
type AddressBookService struct {
}
func (s *AddressBookService) Info(id uint) *model.AddressBook {
func (s *AddressBookService) Info(id string) *model.AddressBook {
p := &model.AddressBook{}
global.DB.Where("id = ?", id).First(p)
return p
}
func (s *AddressBookService) InfoByUserIdAndId(userid uint, id string) *model.AddressBook {
p := &model.AddressBook{}
global.DB.Where("user_id = ? and id = ?", userid, id).First(p)
return p
}
func (s *AddressBookService) InfoByRowId(id uint) *model.AddressBook {
p := &model.AddressBook{}
global.DB.Where("row_id = ?", id).First(p)

View File

@@ -133,7 +133,7 @@ func (os *OauthService) BeginAuth(op string) (error error, code, url string) {
return err, code, conf.AuthCodeURL(code)
}
return errors.New("op错误"), code, ""
return err, code, ""
}
// GetOauthConfig 获取配置
@@ -141,7 +141,7 @@ func (os *OauthService) GetOauthConfig(op string) (error, *oauth2.Config) {
if op == model.OauthTypeGithub {
g := os.InfoByOp(model.OauthTypeGithub)
if g.Id == 0 || g.ClientId == "" || g.ClientSecret == "" || g.RedirectUrl == "" {
return errors.New("配置不存在"), nil
return errors.New("ConfigNotFound"), nil
}
return nil, &oauth2.Config{
ClientID: g.ClientId,
@@ -154,7 +154,7 @@ func (os *OauthService) GetOauthConfig(op string) (error, *oauth2.Config) {
if op == model.OauthTypeGoogle {
g := os.InfoByOp(model.OauthTypeGoogle)
if g.Id == 0 || g.ClientId == "" || g.ClientSecret == "" || g.RedirectUrl == "" {
return errors.New("配置不存在"), nil
return errors.New("ConfigNotFound"), nil
}
return nil, &oauth2.Config{
ClientID: g.ClientId,
@@ -164,7 +164,7 @@ func (os *OauthService) GetOauthConfig(op string) (error, *oauth2.Config) {
Scopes: []string{"https://www.googleapis.com/auth/userinfo.profile", "https://www.googleapis.com/auth/userinfo.email"},
}
}
return errors.New("op错误"), nil
return errors.New("ConfigNotFound"), nil
}
func (os *OauthService) GithubCallback(code string) (error error, userData *GithubUserdata) {
@@ -175,7 +175,7 @@ func (os *OauthService) GithubCallback(code string) (error error, userData *Gith
token, err := oauthConfig.Exchange(context.Background(), code)
if err != nil {
global.Logger.Warn(fmt.Printf("oauthConfig.Exchange() failed: %s\n", err))
error = errors.New("获取token失败")
error = errors.New("GetOauthTokenError")
return
}
@@ -184,7 +184,7 @@ func (os *OauthService) GithubCallback(code string) (error error, userData *Gith
resp, err := client.Get("https://api.github.com/user")
if err != nil {
global.Logger.Warn("failed getting user info: %s\n", err)
error = errors.New("获取user info失败")
error = errors.New("GetOauthUserInfoError")
return
}
defer func(Body io.ReadCloser) {
@@ -197,7 +197,7 @@ func (os *OauthService) GithubCallback(code string) (error error, userData *Gith
// 在这里处理 GitHub 用户信息
if err = json.NewDecoder(resp.Body).Decode(&userData); err != nil {
global.Logger.Warn("failed decoding user info: %s\n", err)
error = errors.New("解析user info失败")
error = errors.New("DecodeOauthUserInfoError")
return
}
return
@@ -208,7 +208,7 @@ func (os *OauthService) GoogleCallback(code string) (error error, userData *Goog
token, err := oauthConfig.Exchange(context.Background(), code)
if err != nil {
global.Logger.Warn(fmt.Printf("oauthConfig.Exchange() failed: %s\n", err))
error = errors.New("获取token失败")
error = errors.New("GetOauthTokenError")
return
}
// 创建 HTTP 客户端,并将 access_token 添加到 Authorization 头中
@@ -216,7 +216,7 @@ func (os *OauthService) GoogleCallback(code string) (error error, userData *Goog
resp, err := client.Get("https://www.googleapis.com/oauth2/v2/userinfo")
if err != nil {
global.Logger.Warn("failed getting user info: %s\n", err)
error = errors.New("获取user info失败: " + err.Error())
error = errors.New("GetOauthUserInfoError")
return
}
defer func(Body io.ReadCloser) {
@@ -228,7 +228,7 @@ func (os *OauthService) GoogleCallback(code string) (error error, userData *Goog
if err = json.NewDecoder(resp.Body).Decode(&userData); err != nil {
global.Logger.Warn("failed decoding user info: %s\n", err)
error = errors.New("解析user info失败:" + err.Error())
error = errors.New("DecodeOauthUserInfoError")
return
}
return
@@ -258,7 +258,13 @@ func (os *OauthService) BindOauthUser(thirdType, openid, username string, userId
}
func (os *OauthService) UnBindGithubUser(userid uint) error {
return global.DB.Where("user_id = ? and third_type = ?", userid, model.OauthTypeGithub).Delete(&model.UserThird{}).Error
return os.UnBindThird(model.OauthTypeGithub, userid)
}
func (os *OauthService) UnBindGoogleUser(userid uint) error {
return os.UnBindThird(model.OauthTypeGoogle, userid)
}
func (os *OauthService) UnBindThird(thirdType string, userid uint) error {
return global.DB.Where("user_id = ? and third_type = ?", userid, thirdType).Delete(&model.UserThird{}).Error
}
// InfoById 根据id取用户信息

View File

@@ -15,24 +15,38 @@ func (ps *PeerService) FindById(id string) *model.Peer {
global.DB.Where("id = ?", id).First(p)
return p
}
func (ps *PeerService) FindByUuid(uuid string) *model.Peer {
p := &model.Peer{}
global.DB.Where("uuid = ?", uuid).First(p)
return p
}
func (ps *PeerService) InfoByRowId(id uint) *model.Peer {
p := &model.Peer{}
global.DB.Where("row_id = ?", id).First(p)
return p
}
//// ListByUserIds 根据用户id取列表
//func (ps *PeerService) ListByUserIds(userIds []uint, page, pageSize uint) (res *model.PeerList) {
// res = &model.PeerList{}
// res.Page = int64(page)
// res.PageSize = int64(pageSize)
// tx := global.DB.Model(&model.Peer{}).Preload("User")
// tx.Where("user_id in (?)", userIds)
// tx.Count(&res.Total)
// tx.Scopes(Paginate(page, pageSize))
// tx.Find(&res.Peers)
// return
//}
// UuidBindUserId 绑定用户id
func (ps *PeerService) UuidBindUserId(uuid string, userId uint) {
peer := ps.FindByUuid(uuid)
if peer.RowId > 0 {
peer.UserId = userId
ps.Update(peer)
}
}
// ListByUserIds 根据用户id取列表
func (ps *PeerService) ListByUserIds(userIds []uint, page, pageSize uint) (res *model.PeerList) {
res = &model.PeerList{}
res.Page = int64(page)
res.PageSize = int64(pageSize)
tx := global.DB.Model(&model.Peer{})
tx.Where("user_id in (?)", userIds)
tx.Count(&res.Total)
tx.Scopes(Paginate(page, pageSize))
tx.Find(&res.Peers)
return
}
func (ps *PeerService) List(page, pageSize uint, where func(tx *gorm.DB)) (res *model.PeerList) {
res = &model.PeerList{}

View File

@@ -14,6 +14,11 @@ func (s *TagService) Info(id uint) *model.Tag {
global.DB.Where("id = ?", id).First(p)
return p
}
func (s *TagService) InfoByUserIdAndName(userid uint, name string) *model.Tag {
p := &model.Tag{}
global.DB.Where("user_id = ? and name = ?", userid, name).First(p)
return p
}
func (s *TagService) ListByUserId(userId uint) (res *model.TagList) {
res = s.List(1, 1000, func(tx *gorm.DB) {

View File

@@ -66,6 +66,9 @@ func (us *UserService) Login(u *model.User, llog *model.LoginLog) *model.UserTok
}
global.DB.Create(ut)
global.DB.Create(llog)
if llog.Uuid != "" {
AllService.PeerService.UuidBindUserId(llog.Uuid, u.Id)
}
return ut
}