Compare commits

...

18 Commits

Author SHA1 Message Date
ljw
be4742382d up build.yml 2024-10-23 09:19:02 +08:00
ljw
70d2f1a055 add armv7l build #21 2024-10-23 09:03:01 +08:00
ljw
877fe50049 add ru lang 2024-10-22 19:46:38 +08:00
ljw
7d505705ee add ko lang,but validator dont have translations ko 2024-10-22 12:21:32 +08:00
ljw
38f81a03b5 Merge branch 'master' of https://github.com/lejianwen/rustdesk-api 2024-10-22 11:31:01 +08:00
d549d23819 Merge pull request #19 from jkh0kr/master
Add Korean language file
2024-10-22 11:30:49 +08:00
ljw
934675e0f0 up 2024-10-22 11:28:27 +08:00
진기환
2be397aa38 Add Korean language file 2024-10-22 10:35:58 +09:00
ljw
a0a422ed45 up readme 2024-10-21 21:35:27 +08:00
ljw
fcce10c695 add file conn log 2024-10-21 21:08:25 +08:00
ljw
30eb14702f up build.yml 2024-10-20 20:26:08 +08:00
ljw
3679fcc874 fix group create type 2024-10-20 20:15:12 +08:00
ljw
d085b4e3c2 up readme 2024-10-20 19:40:49 +08:00
dc8fcdf214 Merge pull request #17 from Ogannesson/master
Add proxy option for Google Oauthon
2024-10-20 19:05:01 +08:00
Oganneson
8bab23b65b Add oauth callback via proxy
Improved support for environment variables and configuration files, and standardized default behaviors
2024-10-20 17:56:11 +08:00
ljw
f64022e411 add conn log 2024-10-18 15:05:58 +08:00
ljw
2d37302cf9 fix write when heartbeat #14 2024-10-17 10:24:02 +08:00
ljw
1a1856257d fix write when heartbeat #14 2024-10-16 21:32:55 +08:00
44 changed files with 2351 additions and 342 deletions

View File

@@ -39,7 +39,7 @@ env:
DOCKERHUB_IMAGE_NAMESPACE: ${{ github.event.inputs.DOCKERHUB_IMAGE_NAMESPACE || github.actor }}
GHCR_IMAGE_NAMESPACE: ${{ github.event.inputs.GHCR_IMAGE_NAMESPACE || github.actor }}
SKIP_DOCKER_HUB: ${{ github.event.inputs.SKIP_DOCKER_HUB || 'false' }}
SKIP_GHCR: ${{ github.event.inputs.SKIP_GHCR }}
SKIP_GHCR: ${{ github.event.inputs.SKIP_GHCR || 'false' }}
jobs:
build:
runs-on: ubuntu-latest
@@ -48,10 +48,10 @@ jobs:
fail-fast: false
matrix:
job:
- { platform: "amd64", goos: "linux" }
- { platform: "arm64", goos: "linux" }
- { platform: "amd64", goos: "windows" }
- { platform: "amd64", goos: "linux", file_ext: "tar.gz" }
- { platform: "arm64", goos: "linux", file_ext: "tar.gz" }
- { platform: "armv7l", goos: "linux", file_ext: "tar.gz" }
- { platform: "amd64", goos: "windows", file_ext: "zip" }
steps:
- name: Checkout code
uses: actions/checkout@v4
@@ -96,18 +96,23 @@ jobs:
if [ "${{ matrix.job.goos }}" = "windows" ]; then
sudo apt-get install gcc-mingw-w64-x86-64 zip -y
GOOS=${{ matrix.job.goos }} GOARCH=${{ matrix.job.platform }} CC=x86_64-w64-mingw32-gcc CGO_LDFLAGS="-static" CGO_ENABLED=1 go build -ldflags "-s -w" -o ./release/apimain.exe ./cmd/apimain.go
zip -r ${{ matrix.job.goos}}-${{ matrix.job.platform }}.zip ./release
zip -r ${{ matrix.job.goos}}-${{ matrix.job.platform }}.${{matrix.job.file_ext}} ./release
else
if [ "${{ matrix.job.platform }}" = "arm64" ]; then
wget https://musl.cc/aarch64-linux-musl-cross.tgz
tar -xf aarch64-linux-musl-cross.tgz
export PATH=$PATH:$PWD/aarch64-linux-musl-cross/bin
GOOS=${{ matrix.job.goos }} GOARCH=${{ matrix.job.platform }} CC=aarch64-linux-musl-gcc CGO_LDFLAGS="-static" CGO_ENABLED=1 go build -ldflags "-s -w" -o ./release/apimain ./cmd/apimain.go
elif [ "${{ matrix.job.platform }}" = "armv7l" ]; then
wget https://musl.cc/armv7l-linux-musleabihf-cross.tgz
tar -xf armv7l-linux-musleabihf-cross.tgz
export PATH=$PATH:$PWD/armv7l-linux-musleabihf-cross/bin
GOOS=${{ matrix.job.goos }} GOARCH=arm GOARM=7 CC=armv7l-linux-musleabihf-gcc CGO_LDFLAGS="-static" CGO_ENABLED=1 go build -ldflags "-s -w" -o ./release/apimain ./cmd/apimain.go
else
sudo apt-get install musl musl-dev musl-tools -y
GOOS=${{ matrix.job.goos }} GOARCH=${{ matrix.job.platform }} CC=musl-gcc CGO_LDFLAGS="-static" CGO_ENABLED=1 go build -ldflags "-s -w" -o ./release/apimain ./cmd/apimain.go
fi
tar -czf ${{ matrix.job.goos}}-${{ matrix.job.platform }}.tar.gz ./release
tar -czf ${{ matrix.job.goos}}-${{ matrix.job.platform }}.${{matrix.job.file_ext}} ./release
fi
- name: Upload artifact
@@ -115,14 +120,12 @@ jobs:
with:
name: rustdesk-api-${{ matrix.job.goos }}-${{ matrix.job.platform }}
path: |
${{ matrix.job.goos}}-${{ matrix.job.platform }}.tar.gz
${{ matrix.job.goos}}-${{ matrix.job.platform }}.zip
${{ matrix.job.goos}}-${{ matrix.job.platform }}.${{matrix.job.file_ext}}
- name: Upload to GitHub Release
uses: softprops/action-gh-release@v2
with:
files: |
${{ matrix.job.goos}}-${{ matrix.job.platform }}.tar.gz
${{ matrix.job.goos}}-${{ matrix.job.platform }}.zip
${{ matrix.job.goos}}-${{ matrix.job.platform }}.${{matrix.job.file_ext}}
# tag_name: ${{ env.LATEST_TAG }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -137,6 +140,7 @@ jobs:
job:
- { platform: "amd64", goos: "linux", docker_platform: "linux/amd64" }
- { platform: "arm64", goos: "linux", docker_platform: "linux/arm64" }
- { platform: "armv7l", goos: "linux", docker_platform: "linux/arm/v7" }
steps:
- name: Checkout code
uses: actions/checkout@v4
@@ -257,6 +261,7 @@ jobs:
with:
base-image: ${{ env.BASE_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}
extra-images: ${{ env.DOCKERHUB_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}-amd64,
${{ env.DOCKERHUB_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}-armv7l,
${{ env.DOCKERHUB_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}-arm64
push: true
@@ -266,6 +271,7 @@ jobs:
with:
base-image: ghcr.io/${{ env.BASE_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}
extra-images: ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}-amd64,
ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}-armv7l,
ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}-arm64
push: true
amend: true
@@ -276,6 +282,7 @@ jobs:
with:
base-image: ${{ env.BASE_IMAGE_NAMESPACE }}/rustdesk-api:latest
extra-images: ${{ env.DOCKERHUB_IMAGE_NAMESPACE }}/rustdesk-api:latest-amd64,
${{ env.DOCKERHUB_IMAGE_NAMESPACE }}/rustdesk-api:latest-armv7l,
${{ env.DOCKERHUB_IMAGE_NAMESPACE }}/rustdesk-api:latest-arm64
push: true
@@ -285,6 +292,7 @@ jobs:
with:
base-image: ghcr.io/${{ env.BASE_IMAGE_NAMESPACE }}/rustdesk-api:latest
extra-images: ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:latest-amd64,
ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:latest-armv7l,
ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:latest-arm64
push: true
amend: true

View File

@@ -28,6 +28,9 @@
- 标签管理
- 群组管理
- Oauth 管理
- 登录日志
- 链接日志
- 文件传输日志
- 快速使用web client
- i18n
- 通过 web client 分享给游客
@@ -44,7 +47,7 @@
#### PC客户端使用的是 ***1.3.0***,经测试 ***1.2.6+*** 都可以
#### 关于PC端链接超时或者链接不上的问题以及解决方案
##### 链接不上或者超时
##### 链接不上或者超时
因为server端相对于客户端落后版本server不会响应客户端的`secure_tcp`请求,所以客户端超时。
相关代码代码位置在`https://github.com/rustdesk/rustdesk/blob/master/src/client.rs#L322`
```rust
@@ -113,12 +116,13 @@
![web_admin](docs/web_admin.png)
2. 普通用户界面
![web_user](docs/web_admin_user.png)
右上角可以更改密码,可以切换语言
右上角可以更改密码,可以切换语言,可以切换`白天/黑夜`模式
![web_resetpwd](docs/web_resetpwd.png)
3. 分组可以自定义,方便管理,暂时支持两种类型: `共享组` 和 `普通组`
![web_admin_gr](docs/web_admin_gr.png)
4. 可以直接打开webclient,方便使用;也可以分享给游客,游客可以直接通过webclient远程到设备
4. You can directly launch the client, or open the web client for convenient use; you can also share it with guests, allowing them to remotely access the device through the web client.
![web_webclient](docs/admin_webclient.png)
5. Oauth,暂时只支持了`Github`和`Google`, 需要创建一个`OAuth App`,然后配置到后台
![web_admin_oauth](docs/web_admin_oauth.png)
@@ -177,6 +181,9 @@ logger:
path: "./runtime/log.txt"
level: "warn" #trace,debug,info,warn,error,fatal
report-caller: true
proxy:
enable: false
host: ""
```
### 环境变量
@@ -204,6 +211,10 @@ logger:
| RUSTDESK_API_RUSTDESK_RELAY_SERVER | Rustdesk的relay服务器地址 | 192.168.1.66:21117 |
| RUSTDESK_API_RUSTDESK_API_SERVER | Rustdesk的api服务器地址 | http://192.168.1.66:21114 |
| RUSTDESK_API_RUSTDESK_KEY | Rustdesk的key | 123456789 |
| ----PROXY配置----- | --------------- | ---------- |
| RUSTDESK_API_PROXY_ENABLE | 是否启用代理:`false`, `true` | `false` |
| RUSTDESK_API_PROXY_HOST | 代理地址 | `http://127.0.0.1:1080` |
### 运行

View File

@@ -27,6 +27,9 @@ desktop software that provides self-hosted solutions.
- Tag Management
- Group Management
- OAuth Management
- Login Logs
- Connection Logs
- File Transfer Logs
- Quick access to web client
- i18n
- Share to guest by web client
@@ -117,15 +120,16 @@ installation are `admin` `admin`, please change the password immediately.
![web_admin](docs/en_img/web_admin.png)
2. Regular user interface:
![web_user](docs/en_img/web_admin_user.png)
You can change your password from the top right corner:
In the top right corner, you can change the password, switch languages, and toggle between `day/night` mode.
![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 directly open the web client for convenient use; it can also be shared with guests, allowing them to remotely access the device via the web client.
4. You can directly launch the client or open the web client for convenience; you can also share it with guests, who can remotely access the device via the web client.
![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.
the admin panel.
![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).
@@ -135,7 +139,7 @@ installation are `admin` `admin`, please change the password immediately.
### 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
2. If you're not logged in, simply click the login button in the top right corner, and the API server will be
pre-configured.
![webclient_conf](docs/webclient_conf.png)
3. After logging in, the ID server and key will be automatically synced.
@@ -183,6 +187,9 @@ logger:
path: "./runtime/log.txt"
level: "warn" #trace,debug,info,warn,error,fatal
report-caller: true
proxy:
enable: false
host: ""
```
### Environment Variables
@@ -210,6 +217,9 @@ The prefix for variable names is `RUSTDESK_API`. If environment variables exist,
| RUSTDESK_API_RUSTDESK_RELAY_SERVER | Rustdesk relay server address | 192.168.1.66:21117 |
| RUSTDESK_API_RUSTDESK_API_SERVER | Rustdesk API server address | http://192.168.1.66:21114 |
| RUSTDESK_API_RUSTDESK_KEY | Rustdesk key | 123456789 |
| ---- PROXY ----- | --------------- | ---------- |
| RUSTDESK_API_PROXY_ENABLE | proxy_enable :`false`, `true` | `false` |
| RUSTDESK_API_PROXY_HOST | proxy_host | `http://127.0.0.1:1080` |
### Installation Steps

View File

@@ -12,19 +12,8 @@ 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"
nethttp "net/http"
"reflect"
)
// @title 管理系统API
@@ -48,7 +37,7 @@ func main() {
ReportCaller: global.Config.Logger.ReportCaller,
})
InitI18n()
global.InitI18n()
//redis
global.Redis = redis.NewClient(&redis.Options{
@@ -87,7 +76,7 @@ func main() {
DatabaseAutoUpdate()
//validator
ApiInitValidator()
global.ApiInitValidator()
//oss
global.Oss = &upload.Oss{
@@ -111,96 +100,8 @@ func main() {
}
func ApiInitValidator() {
validate := validator.New()
// 定义不同的语言翻译
enT := en.New()
cn := zh_Hans_CN.New()
uni := ut.New(enT, cn)
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 == "" {
return field.Name
}
return label
})
global.Validator.Validate = validate
global.Validator.UT = uni // 存储 Universal Translator
global.Validator.VTrans = zhTrans
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 {
errList = append(errList, err.Error())
return errList
}
for _, err2 := range err.(validator.ValidationErrors) {
errList = append(errList, err2.Translate(trans))
}
}
return errList
}
global.Validator.ValidVar = func(ctx *gin.Context, field interface{}, tag string) []string {
err := global.Validator.Validate.Var(field, tag)
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 {
errList = append(errList, err.Error())
return errList
}
for _, err2 := range err.(validator.ValidationErrors) {
errList = append(errList, err2.Translate(trans))
}
}
return errList
}
}
func getTranslatorForLang(lang string) ut.Translator {
switch lang {
case "zh_CN":
fallthrough
case "zh-CN":
fallthrough
case "zh":
trans, _ := global.Validator.UT.GetTranslator("zh_Hans_CN")
return trans
case "en":
fallthrough
default:
trans, _ := global.Validator.UT.GetTranslator("en")
return trans
}
}
func DatabaseAutoUpdate() {
version := 220
version := 235
db := global.DB
@@ -262,6 +163,8 @@ func Migrate(version uint) {
&model.Oauth{},
&model.LoginLog{},
&model.ShareRecord{},
&model.AuditConn{},
&model.AuditFile{},
)
if err != nil {
fmt.Println("migrate err :=>", err)
@@ -271,9 +174,7 @@ func Migrate(version uint) {
var vc int64
global.DB.Model(&model.Version{}).Count(&vc)
if vc == 1 {
localizer := global.Localizer(&gin.Context{
Request: &nethttp.Request{},
})
localizer := global.Localizer("")
defaultGroup, _ := localizer.LocalizeMessage(&i18n.Message{
ID: "DefaultGroup",
})
@@ -305,37 +206,3 @@ 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

@@ -25,6 +25,9 @@ logger:
path: "./runtime/log.txt"
level: "warn" #trace,debug,info,warn,error,fatal
report-caller: true
proxy:
enable: false
host: ""
redis:
addr: "127.0.0.1:6379"
password: ""
@@ -44,4 +47,4 @@ oss:
max-byte: 10240
jwt:
private-key: "./conf/jwt_pri.pem"
expire-duration: 360000
expire-duration: 360000

View File

@@ -30,6 +30,7 @@ type Config struct {
Oss Oss
Jwt Jwt
Rustdesk Rustdesk
Proxy Proxy
}
// Init 初始化配置

6
config/proxy.go Normal file
View File

@@ -0,0 +1,6 @@
package config
type Proxy struct {
Enable bool `mapstructure:"enable"`
Host string `mapstructure:"host"`
}

View File

@@ -380,6 +380,123 @@ const docTemplateadmin = `{
}
}
},
"/admin/audit_conn/delete": {
"post": {
"security": [
{
"token": []
}
],
"description": "文件日志删除",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"文件日志"
],
"summary": "文件日志删除",
"parameters": [
{
"description": "文件日志信息",
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/model.AuditFile"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/admin/audit_conn/list": {
"get": {
"security": [
{
"token": []
}
],
"description": "文件日志列表",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"文件日志"
],
"summary": "文件日志列表",
"parameters": [
{
"type": "integer",
"description": "页码",
"name": "page",
"in": "query"
},
{
"type": "integer",
"description": "页大小",
"name": "page_size",
"in": "query"
},
{
"type": "integer",
"description": "目标设备",
"name": "peer_id",
"in": "query"
},
{
"type": "integer",
"description": "来源设备",
"name": "from_peer",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/response.Response"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/model.AuditFileList"
}
}
}
]
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/admin/file/oss_token": {
"get": {
"security": [
@@ -2449,6 +2566,9 @@ const docTemplateadmin = `{
},
"name": {
"type": "string"
},
"type": {
"type": "integer"
}
}
},
@@ -2744,6 +2864,134 @@ const docTemplateadmin = `{
}
}
},
"model.AuditConn": {
"type": "object",
"properties": {
"action": {
"type": "string"
},
"close_time": {
"type": "integer"
},
"conn_id": {
"type": "integer"
},
"created_at": {
"type": "string"
},
"from_name": {
"type": "string"
},
"from_peer": {
"type": "string"
},
"id": {
"type": "integer"
},
"ip": {
"type": "string"
},
"peer_id": {
"type": "string"
},
"session_id": {
"type": "string"
},
"type": {
"type": "integer"
},
"updated_at": {
"type": "string"
},
"uuid": {
"type": "string"
}
}
},
"model.AuditConnList": {
"type": "object",
"properties": {
"list": {
"type": "array",
"items": {
"$ref": "#/definitions/model.AuditConn"
}
},
"page": {
"type": "integer"
},
"page_size": {
"type": "integer"
},
"total": {
"type": "integer"
}
}
},
"model.AuditFile": {
"type": "object",
"properties": {
"created_at": {
"type": "string"
},
"from_name": {
"type": "string"
},
"from_peer": {
"type": "string"
},
"id": {
"type": "integer"
},
"info": {
"type": "string"
},
"ip": {
"type": "string"
},
"is_file": {
"type": "boolean"
},
"num": {
"type": "integer"
},
"path": {
"type": "string"
},
"peer_id": {
"type": "string"
},
"type": {
"type": "integer"
},
"updated_at": {
"type": "string"
},
"uuid": {
"type": "string"
}
}
},
"model.AuditFileList": {
"type": "object",
"properties": {
"list": {
"type": "array",
"items": {
"$ref": "#/definitions/model.AuditFile"
}
},
"page": {
"type": "integer"
},
"page_size": {
"type": "integer"
},
"total": {
"type": "integer"
}
}
},
"model.Group": {
"type": "object",
"properties": {

View File

@@ -373,6 +373,123 @@
}
}
},
"/admin/audit_conn/delete": {
"post": {
"security": [
{
"token": []
}
],
"description": "文件日志删除",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"文件日志"
],
"summary": "文件日志删除",
"parameters": [
{
"description": "文件日志信息",
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/model.AuditFile"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/admin/audit_conn/list": {
"get": {
"security": [
{
"token": []
}
],
"description": "文件日志列表",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"文件日志"
],
"summary": "文件日志列表",
"parameters": [
{
"type": "integer",
"description": "页码",
"name": "page",
"in": "query"
},
{
"type": "integer",
"description": "页大小",
"name": "page_size",
"in": "query"
},
{
"type": "integer",
"description": "目标设备",
"name": "peer_id",
"in": "query"
},
{
"type": "integer",
"description": "来源设备",
"name": "from_peer",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/response.Response"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/model.AuditFileList"
}
}
}
]
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/admin/file/oss_token": {
"get": {
"security": [
@@ -2442,6 +2559,9 @@
},
"name": {
"type": "string"
},
"type": {
"type": "integer"
}
}
},
@@ -2737,6 +2857,134 @@
}
}
},
"model.AuditConn": {
"type": "object",
"properties": {
"action": {
"type": "string"
},
"close_time": {
"type": "integer"
},
"conn_id": {
"type": "integer"
},
"created_at": {
"type": "string"
},
"from_name": {
"type": "string"
},
"from_peer": {
"type": "string"
},
"id": {
"type": "integer"
},
"ip": {
"type": "string"
},
"peer_id": {
"type": "string"
},
"session_id": {
"type": "string"
},
"type": {
"type": "integer"
},
"updated_at": {
"type": "string"
},
"uuid": {
"type": "string"
}
}
},
"model.AuditConnList": {
"type": "object",
"properties": {
"list": {
"type": "array",
"items": {
"$ref": "#/definitions/model.AuditConn"
}
},
"page": {
"type": "integer"
},
"page_size": {
"type": "integer"
},
"total": {
"type": "integer"
}
}
},
"model.AuditFile": {
"type": "object",
"properties": {
"created_at": {
"type": "string"
},
"from_name": {
"type": "string"
},
"from_peer": {
"type": "string"
},
"id": {
"type": "integer"
},
"info": {
"type": "string"
},
"ip": {
"type": "string"
},
"is_file": {
"type": "boolean"
},
"num": {
"type": "integer"
},
"path": {
"type": "string"
},
"peer_id": {
"type": "string"
},
"type": {
"type": "integer"
},
"updated_at": {
"type": "string"
},
"uuid": {
"type": "string"
}
}
},
"model.AuditFileList": {
"type": "object",
"properties": {
"list": {
"type": "array",
"items": {
"$ref": "#/definitions/model.AuditFile"
}
},
"page": {
"type": "integer"
},
"page_size": {
"type": "integer"
},
"total": {
"type": "integer"
}
}
},
"model.Group": {
"type": "object",
"properties": {

View File

@@ -75,6 +75,8 @@ definitions:
type: integer
name:
type: string
type:
type: integer
required:
- name
type: object
@@ -273,6 +275,90 @@ definitions:
total:
type: integer
type: object
model.AuditConn:
properties:
action:
type: string
close_time:
type: integer
conn_id:
type: integer
created_at:
type: string
from_name:
type: string
from_peer:
type: string
id:
type: integer
ip:
type: string
peer_id:
type: string
session_id:
type: string
type:
type: integer
updated_at:
type: string
uuid:
type: string
type: object
model.AuditConnList:
properties:
list:
items:
$ref: '#/definitions/model.AuditConn'
type: array
page:
type: integer
page_size:
type: integer
total:
type: integer
type: object
model.AuditFile:
properties:
created_at:
type: string
from_name:
type: string
from_peer:
type: string
id:
type: integer
info:
type: string
ip:
type: string
is_file:
type: boolean
num:
type: integer
path:
type: string
peer_id:
type: string
type:
type: integer
updated_at:
type: string
uuid:
type: string
type: object
model.AuditFileList:
properties:
list:
items:
$ref: '#/definitions/model.AuditFile'
type: array
page:
type: integer
page_size:
type: integer
total:
type: integer
type: object
model.Group:
properties:
created_at:
@@ -719,6 +805,77 @@ paths:
summary: APP服务配置
tags:
- ADMIN
/admin/audit_conn/delete:
post:
consumes:
- application/json
description: 文件日志删除
parameters:
- description: 文件日志信息
in: body
name: body
required: true
schema:
$ref: '#/definitions/model.AuditFile'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/response.Response'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.Response'
security:
- token: []
summary: 文件日志删除
tags:
- 文件日志
/admin/audit_conn/list:
get:
consumes:
- application/json
description: 文件日志列表
parameters:
- description: 页码
in: query
name: page
type: integer
- description: 页大小
in: query
name: page_size
type: integer
- description: 目标设备
in: query
name: peer_id
type: integer
- description: 来源设备
in: query
name: from_peer
type: integer
produces:
- application/json
responses:
"200":
description: OK
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
data:
$ref: '#/definitions/model.AuditFileList'
type: object
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.Response'
security:
- token: []
summary: 文件日志列表
tags:
- 文件日志
/admin/file/oss_token:
get:
consumes:

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 51 KiB

View File

@@ -121,40 +121,6 @@ const docTemplateapi = `{
}
}
},
"/ab/add": {
"post": {
"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/peer/add/{guid}": {
"post": {
"security": [
@@ -176,8 +142,8 @@ const docTemplateapi = `{
"parameters": [
{
"type": "string",
"description": "id",
"name": "id",
"description": "guid",
"name": "guid",
"in": "path",
"required": true
}
@@ -217,8 +183,8 @@ const docTemplateapi = `{
"parameters": [
{
"type": "string",
"description": "id",
"name": "id",
"description": "guid",
"name": "guid",
"in": "path",
"required": true
}
@@ -260,8 +226,8 @@ const docTemplateapi = `{
"parameters": [
{
"type": "string",
"description": "id",
"name": "id",
"description": "guid",
"name": "guid",
"in": "path",
"required": true
}
@@ -302,12 +268,22 @@ const docTemplateapi = `{
"summary": "地址列表",
"parameters": [
{
"description": "string valid",
"name": "string",
"in": "body",
"schema": {
"type": "string"
}
"type": "integer",
"description": "页码",
"name": "current",
"in": "query"
},
{
"type": "integer",
"description": "每页数量",
"name": "pageSize",
"in": "query"
},
{
"type": "string",
"description": "guid",
"name": "ab",
"in": "query"
}
],
"responses": {
@@ -434,12 +410,16 @@ const docTemplateapi = `{
"summary": "共享地址簿",
"parameters": [
{
"description": "string valid",
"name": "string",
"in": "body",
"schema": {
"type": "string"
}
"type": "integer",
"description": "页码",
"name": "current",
"in": "query"
},
{
"type": "integer",
"description": "每页数量",
"name": "pageSize",
"in": "query"
}
],
"responses": {
@@ -458,6 +438,49 @@ const docTemplateapi = `{
}
}
},
"/ab/tag/add/{guid}": {
"post": {
"security": [
{
"BearerAuth": []
}
],
"description": "标签",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"地址[Personal]"
],
"summary": "标签添加",
"parameters": [
{
"type": "string",
"description": "guid",
"name": "guid",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "string"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.ErrorResponse"
}
}
}
}
},
"/ab/tag/rename/{guid}": {
"put": {
"security": [
@@ -476,6 +499,15 @@ const docTemplateapi = `{
"地址[Personal]"
],
"summary": "标签重命名",
"parameters": [
{
"type": "string",
"description": "guid",
"name": "guid",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
@@ -510,6 +542,15 @@ const docTemplateapi = `{
"地址[Personal]"
],
"summary": "标签修改颜色",
"parameters": [
{
"type": "string",
"description": "guid",
"name": "guid",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
@@ -544,6 +585,15 @@ const docTemplateapi = `{
"地址[Personal]"
],
"summary": "标签删除",
"parameters": [
{
"type": "string",
"description": "guid",
"name": "guid",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
@@ -581,8 +631,8 @@ const docTemplateapi = `{
"parameters": [
{
"type": "string",
"description": "id",
"name": "id",
"description": "guid",
"name": "guid",
"in": "path",
"required": true
}
@@ -637,6 +687,86 @@ const docTemplateapi = `{
}
}
},
"/audit/conn": {
"post": {
"description": "审计连接",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"审计"
],
"summary": "审计连接",
"parameters": [
{
"description": "审计连接",
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/api.AuditConnForm"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "string"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/audit/file": {
"post": {
"description": "审计文件",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"审计"
],
"summary": "审计文件",
"parameters": [
{
"description": "审计文件",
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/api.AuditFileForm"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "string"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/heartbeat": {
"post": {
"description": "心跳",
@@ -1137,6 +1267,64 @@ const docTemplateapi = `{
}
}
},
"api.AuditConnForm": {
"type": "object",
"properties": {
"action": {
"type": "string"
},
"conn_id": {
"type": "integer"
},
"id": {
"type": "string"
},
"ip": {
"type": "string"
},
"peer": {
"type": "array",
"items": {
"type": "string"
}
},
"session_id": {
"type": "number"
},
"type": {
"type": "integer"
},
"uuid": {
"type": "string"
}
}
},
"api.AuditFileForm": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"info": {
"type": "string"
},
"is_file": {
"type": "boolean"
},
"path": {
"type": "string"
},
"peer_id": {
"type": "string"
},
"type": {
"type": "integer"
},
"uuid": {
"type": "string"
}
}
},
"api.DeviceInfoInLogin": {
"type": "object",
"properties": {

View File

@@ -114,40 +114,6 @@
}
}
},
"/ab/add": {
"post": {
"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/peer/add/{guid}": {
"post": {
"security": [
@@ -169,8 +135,8 @@
"parameters": [
{
"type": "string",
"description": "id",
"name": "id",
"description": "guid",
"name": "guid",
"in": "path",
"required": true
}
@@ -210,8 +176,8 @@
"parameters": [
{
"type": "string",
"description": "id",
"name": "id",
"description": "guid",
"name": "guid",
"in": "path",
"required": true
}
@@ -253,8 +219,8 @@
"parameters": [
{
"type": "string",
"description": "id",
"name": "id",
"description": "guid",
"name": "guid",
"in": "path",
"required": true
}
@@ -295,12 +261,22 @@
"summary": "地址列表",
"parameters": [
{
"description": "string valid",
"name": "string",
"in": "body",
"schema": {
"type": "string"
}
"type": "integer",
"description": "页码",
"name": "current",
"in": "query"
},
{
"type": "integer",
"description": "每页数量",
"name": "pageSize",
"in": "query"
},
{
"type": "string",
"description": "guid",
"name": "ab",
"in": "query"
}
],
"responses": {
@@ -427,12 +403,16 @@
"summary": "共享地址簿",
"parameters": [
{
"description": "string valid",
"name": "string",
"in": "body",
"schema": {
"type": "string"
}
"type": "integer",
"description": "页码",
"name": "current",
"in": "query"
},
{
"type": "integer",
"description": "每页数量",
"name": "pageSize",
"in": "query"
}
],
"responses": {
@@ -451,6 +431,49 @@
}
}
},
"/ab/tag/add/{guid}": {
"post": {
"security": [
{
"BearerAuth": []
}
],
"description": "标签",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"地址[Personal]"
],
"summary": "标签添加",
"parameters": [
{
"type": "string",
"description": "guid",
"name": "guid",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "string"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.ErrorResponse"
}
}
}
}
},
"/ab/tag/rename/{guid}": {
"put": {
"security": [
@@ -469,6 +492,15 @@
"地址[Personal]"
],
"summary": "标签重命名",
"parameters": [
{
"type": "string",
"description": "guid",
"name": "guid",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
@@ -503,6 +535,15 @@
"地址[Personal]"
],
"summary": "标签修改颜色",
"parameters": [
{
"type": "string",
"description": "guid",
"name": "guid",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
@@ -537,6 +578,15 @@
"地址[Personal]"
],
"summary": "标签删除",
"parameters": [
{
"type": "string",
"description": "guid",
"name": "guid",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
@@ -574,8 +624,8 @@
"parameters": [
{
"type": "string",
"description": "id",
"name": "id",
"description": "guid",
"name": "guid",
"in": "path",
"required": true
}
@@ -630,6 +680,86 @@
}
}
},
"/audit/conn": {
"post": {
"description": "审计连接",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"审计"
],
"summary": "审计连接",
"parameters": [
{
"description": "审计连接",
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/api.AuditConnForm"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "string"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/audit/file": {
"post": {
"description": "审计文件",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"审计"
],
"summary": "审计文件",
"parameters": [
{
"description": "审计文件",
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/api.AuditFileForm"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "string"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/heartbeat": {
"post": {
"description": "心跳",
@@ -1130,6 +1260,64 @@
}
}
},
"api.AuditConnForm": {
"type": "object",
"properties": {
"action": {
"type": "string"
},
"conn_id": {
"type": "integer"
},
"id": {
"type": "string"
},
"ip": {
"type": "string"
},
"peer": {
"type": "array",
"items": {
"type": "string"
}
},
"session_id": {
"type": "number"
},
"type": {
"type": "integer"
},
"uuid": {
"type": "string"
}
}
},
"api.AuditFileForm": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"info": {
"type": "string"
},
"is_file": {
"type": "boolean"
},
"path": {
"type": "string"
},
"peer_id": {
"type": "string"
},
"type": {
"type": "integer"
},
"uuid": {
"type": "string"
}
}
},
"api.DeviceInfoInLogin": {
"type": "object",
"properties": {

View File

@@ -6,6 +6,44 @@ definitions:
example: '{"tags":["tag1","tag2","tag3"],"peers":[{"id":"abc","username":"abv-l","hostname":"","platform":"Windows","alias":"","tags":["tag1","tag2"],"hash":"hash"}],"tag_colors":"{\"tag1\":4288585374,\"tag2\":4278238420,\"tag3\":4291681337}"}'
type: string
type: object
api.AuditConnForm:
properties:
action:
type: string
conn_id:
type: integer
id:
type: string
ip:
type: string
peer:
items:
type: string
type: array
session_id:
type: number
type:
type: integer
uuid:
type: string
type: object
api.AuditFileForm:
properties:
id:
type: string
info:
type: string
is_file:
type: boolean
path:
type: string
peer_id:
type: string
type:
type: integer
uuid:
type: string
type: object
api.DeviceInfoInLogin:
properties:
name:
@@ -208,36 +246,15 @@ paths:
summary: 地址更新
tags:
- 地址
/ab/add:
post:
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/peer/add/{guid}:
delete:
consumes:
- application/json
description: 删除地址
parameters:
- description: id
- description: guid
in: path
name: id
name: guid
required: true
type: string
produces:
@@ -261,9 +278,9 @@ paths:
- application/json
description: 添加地址
parameters:
- description: id
- description: guid
in: path
name: id
name: guid
required: true
type: string
produces:
@@ -288,9 +305,9 @@ paths:
- application/json
description: 更新地址
parameters:
- description: id
- description: guid
in: path
name: id
name: guid
required: true
type: string
produces:
@@ -315,11 +332,18 @@ paths:
- application/json
description: 地址
parameters:
- description: string valid
in: body
name: string
schema:
type: string
- description: 页码
in: query
name: current
type: integer
- description: 每页数量
in: query
name: pageSize
type: integer
- description: guid
in: query
name: ab
type: string
produces:
- application/json
responses:
@@ -396,11 +420,14 @@ paths:
- application/json
description: 共享
parameters:
- description: string valid
in: body
name: string
schema:
type: string
- description: 页码
in: query
name: current
type: integer
- description: 每页数量
in: query
name: pageSize
type: integer
produces:
- application/json
responses:
@@ -422,6 +449,12 @@ paths:
consumes:
- application/json
description: 标签
parameters:
- description: guid
in: path
name: guid
required: true
type: string
produces:
- application/json
responses:
@@ -438,11 +471,44 @@ paths:
summary: 标签删除
tags:
- 地址[Personal]
/ab/tag/add/{guid}:
post:
consumes:
- application/json
description: 标签
parameters:
- description: guid
in: path
name: guid
required: true
type: string
produces:
- application/json
responses:
"200":
description: OK
schema:
type: string
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.ErrorResponse'
security:
- BearerAuth: []
summary: 标签添加
tags:
- 地址[Personal]
/ab/tag/rename/{guid}:
put:
consumes:
- application/json
description: 标签
parameters:
- description: guid
in: path
name: guid
required: true
type: string
produces:
- application/json
responses:
@@ -464,6 +530,12 @@ paths:
consumes:
- application/json
description: 标签
parameters:
- description: guid
in: path
name: guid
required: true
type: string
produces:
- application/json
responses:
@@ -486,9 +558,9 @@ paths:
- application/json
description: 标签
parameters:
- description: id
- description: guid
in: path
name: id
name: guid
required: true
type: string
produces:
@@ -528,6 +600,58 @@ paths:
summary: 用户信息
tags:
- 用户
/audit/conn:
post:
consumes:
- application/json
description: 审计连接
parameters:
- description: 审计连接
in: body
name: body
required: true
schema:
$ref: '#/definitions/api.AuditConnForm'
produces:
- application/json
responses:
"200":
description: OK
schema:
type: string
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.Response'
summary: 审计连接
tags:
- 审计
/audit/file:
post:
consumes:
- application/json
description: 审计文件
parameters:
- description: 审计文件
in: body
name: body
required: true
schema:
$ref: '#/definitions/api.AuditFileForm'
produces:
- application/json
responses:
"200":
description: OK
schema:
type: string
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.Response'
summary: 审计文件
tags:
- 审计
/heartbeat:
post:
consumes:

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

124
global/apiValidator.go Normal file
View File

@@ -0,0 +1,124 @@
package global
import (
"github.com/gin-gonic/gin"
"github.com/go-playground/locales/en"
"github.com/go-playground/locales/ko"
"github.com/go-playground/locales/ru"
"github.com/go-playground/locales/zh_Hans_CN"
ut "github.com/go-playground/universal-translator"
"github.com/go-playground/validator/v10"
en_translations "github.com/go-playground/validator/v10/translations/en"
ru_translations "github.com/go-playground/validator/v10/translations/ru"
zh_translations "github.com/go-playground/validator/v10/translations/zh"
"reflect"
)
func ApiInitValidator() {
validate := validator.New()
// 定义不同的语言翻译
enT := en.New()
cn := zh_Hans_CN.New()
koT := ko.New()
ruT := ru.New()
uni := ut.New(enT, cn, koT, ruT)
enTrans, _ := uni.GetTranslator("en")
zhTrans, _ := uni.GetTranslator("zh_Hans_CN")
koTrans, _ := uni.GetTranslator("ko")
ruTrans, _ := uni.GetTranslator("ru")
err := zh_translations.RegisterDefaultTranslations(validate, zhTrans)
if err != nil {
panic(err)
}
err = en_translations.RegisterDefaultTranslations(validate, enTrans)
if err != nil {
panic(err)
}
//validate没有ko的翻译使用zh的翻译
err = zh_translations.RegisterDefaultTranslations(validate, koTrans)
if err != nil {
panic(err)
}
err = ru_translations.RegisterDefaultTranslations(validate, ruTrans)
if err != nil {
panic(err)
}
validate.RegisterTagNameFunc(func(field reflect.StructField) string {
label := field.Tag.Get("label")
if label == "" {
return field.Name
}
return label
})
Validator.Validate = validate
Validator.UT = uni // 存储 Universal Translator
Validator.VTrans = zhTrans
Validator.ValidStruct = func(ctx *gin.Context, i interface{}) []string {
err := Validator.Validate.Struct(i)
lang := ctx.GetHeader("Accept-Language")
if lang == "" {
lang = Config.Lang
}
trans := getTranslatorForLang(lang)
errList := make([]string, 0, 10)
if err != nil {
if _, ok := err.(*validator.InvalidValidationError); ok {
errList = append(errList, err.Error())
return errList
}
for _, err2 := range err.(validator.ValidationErrors) {
errList = append(errList, err2.Translate(trans))
}
}
return errList
}
Validator.ValidVar = func(ctx *gin.Context, field interface{}, tag string) []string {
err := Validator.Validate.Var(field, tag)
lang := ctx.GetHeader("Accept-Language")
if lang == "" {
lang = Config.Lang
}
trans := getTranslatorForLang(lang)
errList := make([]string, 0, 10)
if err != nil {
if _, ok := err.(*validator.InvalidValidationError); ok {
errList = append(errList, err.Error())
return errList
}
for _, err2 := range err.(validator.ValidationErrors) {
errList = append(errList, err2.Translate(trans))
}
}
return errList
}
}
func getTranslatorForLang(lang string) ut.Translator {
switch lang {
case "zh_CN":
fallthrough
case "zh-CN":
fallthrough
case "zh":
trans, _ := Validator.UT.GetTranslator("zh_Hans_CN")
return trans
case "ko":
trans, _ := Validator.UT.GetTranslator("ko")
return trans
case "ru":
trans, _ := Validator.UT.GetTranslator("ru")
return trans
case "en":
fallthrough
default:
trans, _ := Validator.UT.GetTranslator("en")
return trans
}
}

View File

@@ -33,5 +33,5 @@ var (
Oss *upload.Oss
Jwt *jwt.Jwt
Lock lock.Locker
Localizer func(ctx *gin.Context) *i18n.Localizer
Localizer func(lang string) *i18n.Localizer
)

53
global/i18n.go Normal file
View File

@@ -0,0 +1,53 @@
package global
import (
"github.com/BurntSushi/toml"
"github.com/nicksnyder/go-i18n/v2/i18n"
"golang.org/x/text/language"
"os"
)
func InitI18n() {
bundle := i18n.NewBundle(language.English)
bundle.RegisterUnmarshalFunc("toml", toml.Unmarshal)
//读取global.Config.Gin.ResourcesPath下的所有语言文件
dir := Config.Gin.ResourcesPath + "/i18n"
fileInfos, err := os.ReadDir(dir)
if err != nil {
panic(err)
return
}
for _, fileInfo := range fileInfos {
//如果文件名不是.toml结尾
if fileInfo.IsDir() || fileInfo.Name()[len(fileInfo.Name())-5:] != ".toml" {
continue
}
bundle.LoadMessageFile(Config.Gin.ResourcesPath + "/i18n/" + fileInfo.Name())
}
Localizer = func(lang string) *i18n.Localizer {
if lang == "" {
lang = Config.Lang
}
if lang == "en" {
return i18n.NewLocalizer(bundle, "en")
} else {
return i18n.NewLocalizer(bundle, lang, "en")
}
}
//personUnreadEmails := localizer.MustLocalize(&i18n.LocalizeConfig{
// DefaultMessage: &i18n.Message{
// ID: "PersonUnreadEmails",
// },
// PluralCount: 6,
// TemplateData: map[string]interface{}{
// "Name": "LE",
// "PluralCount": 6,
// },
//})
//personUnreadEmails, err := global.Localizer.LocalizeMessage(&i18n.Message{
// ID: "ParamsError",
//})
//fmt.Println(err, personUnreadEmails)
}

View File

@@ -0,0 +1,148 @@
package admin
import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/model"
"Gwen/service"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
type Audit struct {
}
// ConnList 列表
// @Tags 链接日志
// @Summary 链接日志列表
// @Description 链接日志列表
// @Accept json
// @Produce json
// @Param page query int false "页码"
// @Param page_size query int false "页大小"
// @Param peer_id query int false "目标设备"
// @Param from_peer query int false "来源设备"
// @Success 200 {object} response.Response{data=model.AuditConnList}
// @Failure 500 {object} response.Response
// @Router /admin/audit_conn/list [get]
// @Security token
func (a *Audit) ConnList(c *gin.Context) {
query := &admin.AuditQuery{}
if err := c.ShouldBindQuery(query); err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
res := service.AllService.AuditService.AuditConnList(query.Page, query.PageSize, func(tx *gorm.DB) {
if query.PeerId != "" {
tx.Where("peer_id like ?", "%"+query.PeerId+"%")
}
if query.FromPeer != "" {
tx.Where("from_peer like ?", "%"+query.FromPeer+"%")
}
})
response.Success(c, res)
}
// ConnDelete 删除
// @Tags 链接日志
// @Summary 链接日志删除
// @Description 链接日志删除
// @Accept json
// @Produce json
// @Param body body model.AuditConn true "链接日志信息"
// @Success 200 {object} response.Response
// @Failure 500 {object} response.Response
// @Router /admin/audit_conn/delete [post]
// @Security token
func (a *Audit) ConnDelete(c *gin.Context) {
f := &model.AuditConn{}
if err := c.ShouldBindJSON(f); err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
id := f.Id
errList := global.Validator.ValidVar(c, id, "required,gt=0")
if len(errList) > 0 {
response.Fail(c, 101, errList[0])
return
}
l := service.AllService.AuditService.ConnInfoById(f.Id)
if l.Id > 0 {
err := service.AllService.AuditService.DeleteAuditConn(l)
if err == nil {
response.Success(c, nil)
return
}
response.Fail(c, 101, err.Error())
return
}
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
}
// FileList 列表
// @Tags 文件日志
// @Summary 文件日志列表
// @Description 文件日志列表
// @Accept json
// @Produce json
// @Param page query int false "页码"
// @Param page_size query int false "页大小"
// @Param peer_id query int false "目标设备"
// @Param from_peer query int false "来源设备"
// @Success 200 {object} response.Response{data=model.AuditFileList}
// @Failure 500 {object} response.Response
// @Router /admin/audit_conn/list [get]
// @Security token
func (a *Audit) FileList(c *gin.Context) {
query := &admin.AuditQuery{}
if err := c.ShouldBindQuery(query); err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
res := service.AllService.AuditService.AuditFileList(query.Page, query.PageSize, func(tx *gorm.DB) {
if query.PeerId != "" {
tx.Where("peer_id like ?", "%"+query.PeerId+"%")
}
if query.FromPeer != "" {
tx.Where("from_peer like ?", "%"+query.FromPeer+"%")
}
})
response.Success(c, res)
}
// FileDelete 删除
// @Tags 文件日志
// @Summary 文件日志删除
// @Description 文件日志删除
// @Accept json
// @Produce json
// @Param body body model.AuditFile true "文件日志信息"
// @Success 200 {object} response.Response
// @Failure 500 {object} response.Response
// @Router /admin/audit_conn/delete [post]
// @Security token
func (a *Audit) FileDelete(c *gin.Context) {
f := &model.AuditFile{}
if err := c.ShouldBindJSON(f); err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
id := f.Id
errList := global.Validator.ValidVar(c, id, "required,gt=0")
if len(errList) > 0 {
response.Fail(c, 101, errList[0])
return
}
l := service.AllService.AuditService.FileInfoById(f.Id)
if l.Id > 0 {
err := service.AllService.AuditService.DeleteAuditFile(l)
if err == nil {
response.Success(c, nil)
return
}
response.Fail(c, 101, err.Error())
return
}
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
}

View File

@@ -118,9 +118,10 @@ func (a *Ab) Tags(c *gin.Context) {
// @Description 标签
// @Accept json
// @Produce json
// @Param guid path string true "guid"
// @Success 200 {string} string
// @Failure 500 {object} response.ErrorResponse
// @Router /ab/add [post]
// @Router /ab/tag/add/{guid} [post]
// @Security BearerAuth
func (a *Ab) TagAdd(c *gin.Context) {
t := &model.Tag{}
@@ -150,6 +151,7 @@ func (a *Ab) TagAdd(c *gin.Context) {
// @Description 标签
// @Accept json
// @Produce json
// @Param guid path string true "guid"
// @Success 200 {string} string
// @Failure 500 {object} response.ErrorResponse
// @Router /ab/tag/rename/{guid} [put]
@@ -187,6 +189,7 @@ func (a *Ab) TagRename(c *gin.Context) {
// @Description 标签
// @Accept json
// @Produce json
// @Param guid path string true "guid"
// @Success 200 {string} string
// @Failure 500 {object} response.ErrorResponse
// @Router /ab/tag/update/{guid} [put]
@@ -219,6 +222,7 @@ func (a *Ab) TagUpdate(c *gin.Context) {
// @Description 标签
// @Accept json
// @Produce json
// @Param guid path string true "guid"
// @Success 200 {string} string
// @Failure 500 {object} response.ErrorResponse
// @Router /ab/tag/{guid} [delete]
@@ -273,7 +277,7 @@ func (a *Ab) Personal(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"guid": guid,
"name": user.Username,
"rule": 0,
"rule": 3,
})
} else {
c.JSON(http.StatusOK, nil)
@@ -304,7 +308,8 @@ func (a *Ab) Settings(c *gin.Context) {
// @Description 共享
// @Accept json
// @Produce json
// @Param string body string false "string valid"
// @Param current query int false "页码"
// @Param pageSize query int false "每页数量"
// @Success 200 {object} response.Response
// @Failure 500 {object} response.Response
// @Router /ab/shared/profiles [post]
@@ -322,14 +327,14 @@ func (a *Ab) SharedProfiles(c *gin.Context) {
"name": "admin",
"owner": "admin",
"note": "admin11",
"rule": 0,
"rule": 3,
}
item2 := map[string]interface{}{
"guid": "2",
"name": "admin2",
"owner": "admin2",
"note": "admin22",
"rule": 0,
"rule": 2,
}
c.JSON(http.StatusOK, gin.H{
"total": 2,
@@ -348,7 +353,9 @@ func (a *Ab) SharedProfiles(c *gin.Context) {
// @Description 地址
// @Accept json
// @Produce json
// @Param string body string false "string valid"
// @Param current query int false "页码"
// @Param pageSize query int false "每页数量"
// @Param ab query string false "guid"
// @Success 200 {object} response.Response
// @Failure 500 {object} response.Response
// @Router /ab/peers [post]
@@ -369,7 +376,7 @@ func (a *Ab) Peers(c *gin.Context) {
// @Description 标签
// @Accept json
// @Produce json
// @Param id path string true "id"
// @Param guid path string true "guid"
// @Success 200 {object} model.TagList
// @Failure 500 {object} response.ErrorResponse
// @Router /ab/tags/{guid} [post]
@@ -387,7 +394,7 @@ func (a *Ab) PTags(c *gin.Context) {
// @Description 添加地址
// @Accept json
// @Produce json
// @Param id path string true "id"
// @Param guid path string true "guid"
// @Success 200 {string} string
// @Failure 500 {object} response.ErrorResponse
// @Router /ab/peer/add/{guid} [post]
@@ -395,6 +402,7 @@ func (a *Ab) PTags(c *gin.Context) {
func (a *Ab) PeerAdd(c *gin.Context) {
// forceAlwaysRelay永远是字符串"false"
//f := &gin.H{}
//guid := c.Param("guid")
f := &requstform.PersonalAddressBookForm{}
err := c.ShouldBindJSON(f)
if err != nil {
@@ -429,7 +437,7 @@ func (a *Ab) PeerAdd(c *gin.Context) {
// @Description 删除地址
// @Accept json
// @Produce json
// @Param id path string true "id"
// @Param guid path string true "guid"
// @Success 200 {string} string
// @Failure 500 {object} response.ErrorResponse
// @Router /ab/peer/add/{guid} [delete]
@@ -464,7 +472,7 @@ func (a *Ab) PeerDel(c *gin.Context) {
// @Description 更新地址
// @Accept json
// @Produce json
// @Param id path string true "id"
// @Param guid path string true "guid"
// @Success 200 {string} string
// @Failure 500 {object} response.ErrorResponse
// @Router /ab/peer/update/{guid} [put]

View File

@@ -0,0 +1,84 @@
package api
import (
request "Gwen/http/request/api"
"Gwen/http/response"
"Gwen/model"
"Gwen/service"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
"time"
)
type Audit struct {
}
// AuditConn
// @Tags 审计
// @Summary 审计连接
// @Description 审计连接
// @Accept json
// @Produce json
// @Param body body request.AuditConnForm true "审计连接"
// @Success 200 {string} string ""
// @Failure 500 {object} response.Response
// @Router /audit/conn [post]
func (a *Audit) AuditConn(c *gin.Context) {
af := &request.AuditConnForm{}
err := c.ShouldBindBodyWith(af, binding.JSON)
if err != nil {
response.Error(c, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
/*ttt := &gin.H{}
c.ShouldBindBodyWith(ttt, binding.JSON)
fmt.Println(ttt)*/
ac := af.ToAuditConn()
if af.Action == model.AuditActionNew {
service.AllService.AuditService.CreateAuditConn(ac)
} else if af.Action == model.AuditActionClose {
ex := service.AllService.AuditService.InfoByPeerIdAndConnId(af.Id, af.ConnId)
if ex.Id != 0 {
ex.CloseTime = time.Now().Unix()
service.AllService.AuditService.UpdateAuditConn(ex)
}
} else if af.Action == "" {
ex := service.AllService.AuditService.InfoByPeerIdAndConnId(af.Id, af.ConnId)
if ex.Id != 0 {
up := &model.AuditConn{
IdModel: model.IdModel{Id: ex.Id},
FromPeer: ac.FromPeer,
FromName: ac.FromName,
SessionId: ac.SessionId,
Type: ac.Type,
}
service.AllService.AuditService.UpdateAuditConn(up)
}
}
response.Success(c, "")
}
// AuditFile
// @Tags 审计
// @Summary 审计文件
// @Description 审计文件
// @Accept json
// @Produce json
// @Param body body request.AuditFileForm true "审计文件"
// @Success 200 {string} string ""
// @Failure 500 {object} response.Response
// @Router /audit/file [post]
func (a *Audit) AuditFile(c *gin.Context) {
aff := &request.AuditFileForm{}
err := c.ShouldBindBodyWith(aff, binding.JSON)
if err != nil {
response.Error(c, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
//ttt := &gin.H{}
//c.ShouldBindBodyWith(ttt, binding.JSON)
//fmt.Println(ttt)
af := aff.ToAuditFile()
service.AllService.AuditService.CreateAuditFile(af)
response.Success(c, "")
}

View File

@@ -3,6 +3,7 @@ package api
import (
requstform "Gwen/http/request/api"
"Gwen/http/response"
"Gwen/model"
"Gwen/service"
"github.com/gin-gonic/gin"
"net/http"
@@ -53,7 +54,11 @@ func (i *Index) Heartbeat(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{})
return
}
peer.LastOnlineTime = time.Now().Unix()
service.AllService.PeerService.Update(peer)
//如果在一分钟以内则不更新
if time.Now().Unix()-peer.LastOnlineTime > 60 {
peer.LastOnlineTime = time.Now().Unix()
upp := &model.Peer{RowId: peer.RowId, LastOnlineTime: peer.LastOnlineTime}
service.AllService.PeerService.Update(upp)
}
c.JSON(http.StatusOK, gin.H{})
}

View File

@@ -54,5 +54,5 @@ func (p *Peer) SysInfo(c *gin.Context) {
//SYSINFO_UPDATED 上传成功
//ID_NOT_FOUND 下次心跳会上传
//直接响应文本
c.String(http.StatusOK, "")
c.String(http.StatusOK, "SYSINFO_UPDATED")
}

View File

@@ -22,7 +22,7 @@ type User struct {
// @Security token
//func (u *User) currentUser(c *gin.Context) {
// user := service.AllService.UserService.CurUser(c)
// up := (&apiResp.UserPayload{}).FromUser(user)
// up := (&apiResp.UserPayload{}).FromName(user)
// c.JSON(http.StatusOK, up)
//}

View File

@@ -0,0 +1,7 @@
package admin
type AuditQuery struct {
PeerId string `form:"peer_id"`
FromPeer string `form:"from_peer"`
PageQuery
}

View File

@@ -5,11 +5,13 @@ import "Gwen/model"
type GroupForm struct {
Id uint `json:"id"`
Name string `json:"name" validate:"required"`
Type int `json:"type"`
}
func (gf *GroupForm) FromGroup(group *model.Group) *GroupForm {
gf.Id = group.Id
gf.Name = group.Name
gf.Type = group.Type
return gf
}
@@ -17,5 +19,6 @@ func (gf *GroupForm) ToGroup() *model.Group {
group := &model.Group{}
group.Id = gf.Id
group.Name = gf.Name
group.Type = gf.Type
return group
}

78
http/request/api/audit.go Normal file
View File

@@ -0,0 +1,78 @@
package api
import (
"Gwen/global"
"Gwen/model"
"encoding/json"
"strconv"
)
type AuditConnForm struct {
Action string `json:"action"`
ConnId int64 `json:"conn_id"`
Id string `json:"id"`
Peer []string `json:"peer"`
Ip string `json:"ip"`
SessionId float64 `json:"session_id"`
Type int `json:"type"`
Uuid string `json:"uuid"`
}
func (a *AuditConnForm) ToAuditConn() *model.AuditConn {
fp := ""
fn := ""
if len(a.Peer) >= 1 {
fp = a.Peer[0]
if len(a.Peer) == 2 {
fn = a.Peer[1]
}
}
ssid := strconv.FormatFloat(a.SessionId, 'f', -1, 64)
return &model.AuditConn{
Action: a.Action,
ConnId: a.ConnId,
PeerId: a.Id,
FromPeer: fp,
FromName: fn,
Ip: a.Ip,
SessionId: ssid,
Type: a.Type,
Uuid: a.Uuid,
}
}
type AuditFileForm struct {
Id string `json:"id"`
Info string `json:"info"`
IsFile bool `json:"is_file"`
Path string `json:"path"`
PeerId string `json:"peer_id"`
Type int `json:"type"`
Uuid string `json:"uuid"`
}
type AuditFileInfo struct {
Ip string `json:"ip"`
Name string `json:"name"`
Num int `json:"num"`
}
func (a *AuditFileForm) ToAuditFile() *model.AuditFile {
fi := &AuditFileInfo{}
err := json.Unmarshal([]byte(a.Info), fi)
if err != nil {
global.Logger.Warn("ToAuditFile", err)
}
return &model.AuditFile{
PeerId: a.Id,
Info: a.Info,
IsFile: a.IsFile,
FromPeer: a.PeerId,
Path: a.Path,
Type: a.Type,
Uuid: a.Uuid,
FromName: fi.Name,
Ip: fi.Ip,
Num: fi.Num,
}
}

View File

@@ -56,7 +56,7 @@ type ServerConfigResponse struct {
}
func TranslateMsg(c *gin.Context, messageId string) string {
localizer := global.Localizer(c)
localizer := global.Localizer(c.GetHeader("Accept-Language"))
errMsg, err := localizer.LocalizeMessage(&i18n.Message{
ID: messageId,
})
@@ -67,7 +67,7 @@ func TranslateMsg(c *gin.Context, messageId string) string {
return errMsg
}
func TranslateTempMsg(c *gin.Context, messageId string, templateData map[string]interface{}) string {
localizer := global.Localizer(c)
localizer := global.Localizer(c.GetHeader("Accept-Language"))
errMsg, err := localizer.Localize(&i18n.LocalizeConfig{
DefaultMessage: &i18n.Message{
ID: messageId,
@@ -81,7 +81,7 @@ func TranslateTempMsg(c *gin.Context, messageId string, templateData map[string]
return errMsg
}
func TranslateParamMsg(c *gin.Context, messageId string, params ...string) string {
localizer := global.Localizer(c)
localizer := global.Localizer(c.GetHeader("Accept-Language"))
templateData := make(map[string]interface{})
for i, v := range params {
k := fmt.Sprintf("P%d", i)

View File

@@ -27,7 +27,7 @@ func Init(g *gin.Engine) {
PeerBind(adg)
OauthBind(adg)
LoginLogBind(adg)
AuditBind(adg)
rs := &admin.Rustdesk{}
adg.GET("/server-config", rs.ServerConfig)
adg.GET("/app-config", rs.AppConfig)
@@ -143,6 +143,15 @@ func LoginLogBind(rg *gin.RouterGroup) {
aR.GET("/list", cont.List)
aR.POST("/delete", cont.Delete)
}
func AuditBind(rg *gin.RouterGroup) {
cont := &admin.Audit{}
aR := rg.Group("/audit_conn").Use(middleware.AdminPrivilege())
aR.GET("/list", cont.ConnList)
aR.POST("/delete", cont.ConnDelete)
afR := rg.Group("/audit_file").Use(middleware.AdminPrivilege())
afR.GET("/list", cont.FileList)
afR.POST("/delete", cont.FileDelete)
}
/*
func FileBind(rg *gin.RouterGroup) {

View File

@@ -50,7 +50,11 @@ func ApiInit(g *gin.Engine) {
if global.Config.App.WebClient == 1 {
WebClientRoutes(frg)
}
au := &api.Audit{}
//[method:POST] [uri:/api/audit/conn]
frg.POST("/audit/conn", au.AuditConn)
//[method:POST] [uri:/api/audit/file]
frg.POST("/audit/file", au.AuditFile)
frg.Use(middleware.RustAuth())
{
u := &api.User{}

View File

@@ -17,6 +17,7 @@ func WebInit(g *gin.Engine) {
if global.Config.App.WebClient == 1 {
g.StaticFS("/webclient", http.Dir(global.Config.Gin.ResourcesPath+"/web"))
g.StaticFS("/webclient2", http.Dir(global.Config.Gin.ResourcesPath+"/web2"))
}
g.StaticFS("/_admin", http.Dir(global.Config.Gin.ResourcesPath+"/admin"))
}

46
model/audit.go Normal file
View File

@@ -0,0 +1,46 @@
package model
const (
AuditActionNew = "new"
AuditActionClose = "close"
)
type AuditConn struct {
IdModel
Action string `json:"action" gorm:"default:'';not null;"`
ConnId int64 `json:"conn_id" gorm:"default:0;not null;index"`
PeerId string `json:"peer_id" gorm:"default:'';not null;index"`
FromPeer string `json:"from_peer" gorm:"default:'';not null;"`
FromName string `json:"from_name" gorm:"default:'';not null;"`
Ip string `json:"ip" gorm:"default:'';not null;"`
SessionId string `json:"session_id" gorm:"default:'';not null;"`
Type int `json:"type" gorm:"default:0;not null;"`
Uuid string `json:"uuid" gorm:"default:'';not null;"`
CloseTime int64 `json:"close_time" gorm:"default:0;not null;"`
TimeModel
}
type AuditConnList struct {
AuditConns []*AuditConn `json:"list"`
Pagination
}
type AuditFile struct {
IdModel
FromPeer string `json:"from_peer" gorm:"default:'';not null;index"`
Info string `json:"info" gorm:"default:'';not null;"`
IsFile bool `json:"is_file" gorm:"default:0;not null;"`
Path string `json:"path" gorm:"default:'';not null;"`
PeerId string `json:"peer_id" gorm:"default:'';not null;index"`
Type int `json:"type" gorm:"default:0;not null;"`
Uuid string `json:"uuid" gorm:"default:'';not null;"`
Ip string `json:"ip" gorm:"default:'';not null;"`
Num int `json:"num" gorm:"default:0;not null;"`
FromName string `json:"from_name" gorm:"default:'';not null;"`
TimeModel
}
type AuditFileList struct {
AuditFiles []*AuditFile `json:"list"`
Pagination
}

123
resources/i18n/ko.toml Normal file
View File

@@ -0,0 +1,123 @@
[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 = "인증 토큰을 획득하지 못했습니다."
other = "인증 토큰을 획득하지 못했습니다."
[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 = "이전 비밀번호가 잘못되었습니다."
[DefaultGroup]
description = "Default group."
one = "기본 그룹"
other = "기본 그룹"
[ShareGroup]
description = "Share group."
one = "공유 그룹"
other = "공유 그룹"

129
resources/i18n/ru.toml Normal file
View File

@@ -0,0 +1,129 @@
[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 = "Не удалось получить токен авторизации."
other = "Не удалось получить токен авторизации."
[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 = "Неправильный старый пароль."
[DefaultGroup]
description = "Default group."
one = "Группа по умолчанию"
other = "Группа по умолчанию"
[ShareGroup]
description = "Share group."
one = "Общая группа"
other = "Общая группа"

87
service/audit.go Normal file
View File

@@ -0,0 +1,87 @@
package service
import (
"Gwen/global"
"Gwen/model"
"gorm.io/gorm"
)
type AuditService struct {
}
func (as *AuditService) AuditConnList(page, pageSize uint, where func(tx *gorm.DB)) (res *model.AuditConnList) {
res = &model.AuditConnList{}
res.Page = int64(page)
res.PageSize = int64(pageSize)
tx := global.DB.Model(&model.AuditConn{})
if where != nil {
where(tx)
}
tx.Count(&res.Total)
tx.Scopes(Paginate(page, pageSize))
tx.Find(&res.AuditConns)
return
}
// Create 创建
func (as *AuditService) CreateAuditConn(u *model.AuditConn) error {
res := global.DB.Create(u).Error
return res
}
func (as *AuditService) DeleteAuditConn(u *model.AuditConn) error {
return global.DB.Delete(u).Error
}
// Update 更新
func (as *AuditService) UpdateAuditConn(u *model.AuditConn) error {
return global.DB.Model(u).Updates(u).Error
}
// InfoByPeerIdAndConnId
func (as *AuditService) InfoByPeerIdAndConnId(peerId string, connId int64) (res *model.AuditConn) {
res = &model.AuditConn{}
global.DB.Where("peer_id = ? and conn_id = ?", peerId, connId).First(res)
return
}
// ConnInfoById
func (as *AuditService) ConnInfoById(id uint) (res *model.AuditConn) {
res = &model.AuditConn{}
global.DB.Where("id = ?", id).First(res)
return
}
// FileInfoById
func (as *AuditService) FileInfoById(id uint) (res *model.AuditFile) {
res = &model.AuditFile{}
global.DB.Where("id = ?", id).First(res)
return
}
func (as *AuditService) AuditFileList(page, pageSize uint, where func(tx *gorm.DB)) (res *model.AuditFileList) {
res = &model.AuditFileList{}
res.Page = int64(page)
res.PageSize = int64(pageSize)
tx := global.DB.Model(&model.AuditFile{})
if where != nil {
where(tx)
}
tx.Count(&res.Total)
tx.Scopes(Paginate(page, pageSize))
tx.Find(&res.AuditFiles)
return
}
// CreateAuditFile
func (as *AuditService) CreateAuditFile(u *model.AuditFile) error {
res := global.DB.Create(u).Error
return res
}
func (as *AuditService) DeleteAuditFile(u *model.AuditFile) error {
return global.DB.Delete(u).Error
}
// Update 更新
func (as *AuditService) UpdateAuditFile(u *model.AuditFile) error {
return global.DB.Model(u).Updates(u).Error
}

View File

@@ -7,12 +7,13 @@ import (
"context"
"encoding/json"
"errors"
"fmt"
"golang.org/x/oauth2"
"golang.org/x/oauth2/github"
"golang.org/x/oauth2/google"
"gorm.io/gorm"
"io"
"net/http"
"net/url"
"strconv"
"sync"
"time"
@@ -167,36 +168,60 @@ func (os *OauthService) GetOauthConfig(op string) (error, *oauth2.Config) {
return errors.New("ConfigNotFound"), nil
}
func getHTTPClientWithProxy() *http.Client {
if global.Config.Proxy.Enable {
if global.Config.Proxy.Host == "" {
global.Logger.Warn("Proxy is enabled but proxy host is empty.")
return http.DefaultClient
}
proxyURL, err := url.Parse(global.Config.Proxy.Host)
if err != nil {
global.Logger.Warn("Invalid proxy URL: ", err)
return http.DefaultClient
}
transport := &http.Transport{
Proxy: http.ProxyURL(proxyURL),
}
return &http.Client{Transport: transport}
}
return http.DefaultClient
}
func (os *OauthService) GithubCallback(code string) (error error, userData *GithubUserdata) {
err, oauthConfig := os.GetOauthConfig(model.OauthTypeGithub)
if err != nil {
return err, nil
}
token, err := oauthConfig.Exchange(context.Background(), code)
// 使用代理配置创建 HTTP 客户端
httpClient := getHTTPClientWithProxy()
ctx := context.WithValue(context.Background(), oauth2.HTTPClient, httpClient)
token, err := oauthConfig.Exchange(ctx, code)
if err != nil {
global.Logger.Warn(fmt.Printf("oauthConfig.Exchange() failed: %s\n", err))
global.Logger.Warn("oauthConfig.Exchange() failed: ", err)
error = errors.New("GetOauthTokenError")
return
}
// 创建一个 HTTP 客户端,并将 access_token 添加到 Authorization 头中
client := oauthConfig.Client(context.Background(), token)
// 使用带有代理的 HTTP 客户端获取用户信息
client := oauthConfig.Client(ctx, token)
resp, err := client.Get("https://api.github.com/user")
if err != nil {
global.Logger.Warn("failed getting user info: %s\n", err)
global.Logger.Warn("failed getting user info: ", err)
error = errors.New("GetOauthUserInfoError")
return
}
defer func(Body io.ReadCloser) {
err := Body.Close()
if err != nil {
global.Logger.Warn("failed closing response body: %s\n", err)
global.Logger.Warn("failed closing response body: ", err)
}
}(resp.Body)
// 在这里处理 GitHub 用户信息
// 解析用户信息
if err = json.NewDecoder(resp.Body).Decode(&userData); err != nil {
global.Logger.Warn("failed decoding user info: %s\n", err)
global.Logger.Warn("failed decoding user info: ", err)
error = errors.New("DecodeOauthUserInfoError")
return
}
@@ -205,29 +230,39 @@ func (os *OauthService) GithubCallback(code string) (error error, userData *Gith
func (os *OauthService) GoogleCallback(code string) (error error, userData *GoogleUserdata) {
err, oauthConfig := os.GetOauthConfig(model.OauthTypeGoogle)
token, err := oauthConfig.Exchange(context.Background(), code)
if err != nil {
global.Logger.Warn(fmt.Printf("oauthConfig.Exchange() failed: %s\n", err))
return err, nil
}
// 使用代理配置创建 HTTP 客户端
httpClient := getHTTPClientWithProxy()
ctx := context.WithValue(context.Background(), oauth2.HTTPClient, httpClient)
token, err := oauthConfig.Exchange(ctx, code)
if err != nil {
global.Logger.Warn("oauthConfig.Exchange() failed: ", err)
error = errors.New("GetOauthTokenError")
return
}
// 创建 HTTP 客户端,并将 access_token 添加到 Authorization 头中
client := oauthConfig.Client(context.Background(), token)
// 使用带有代理的 HTTP 客户端获取用户信息
client := oauthConfig.Client(ctx, token)
resp, err := client.Get("https://www.googleapis.com/oauth2/v2/userinfo")
if err != nil {
global.Logger.Warn("failed getting user info: %s\n", err)
global.Logger.Warn("failed getting user info: ", err)
error = errors.New("GetOauthUserInfoError")
return
}
defer func(Body io.ReadCloser) {
err := Body.Close()
if err != nil {
global.Logger.Warn("failed closing response body: %s\n", err)
global.Logger.Warn("failed closing response body: ", err)
}
}(resp.Body)
// 解析用户信息
if err = json.NewDecoder(resp.Body).Decode(&userData); err != nil {
global.Logger.Warn("failed decoding user info: %s\n", err)
global.Logger.Warn("failed decoding user info: ", err)
error = errors.New("DecodeOauthUserInfoError")
return
}

View File

@@ -15,6 +15,7 @@ type Service struct {
*GroupService
*OauthService
*LoginLogService
*AuditService
}
func New() *Service {

View File

@@ -21,6 +21,11 @@ func (us *UserService) InfoById(id uint) *model.User {
global.DB.Where("id = ?", id).First(u)
return u
}
func (us *UserService) InfoByUsername(un string) *model.User {
u := &model.User{}
global.DB.Where("username = ?", un).First(u)
return u
}
// InfoByOpenid 根据openid取用户信息
func (us *UserService) InfoByOpenid(openid string) *model.User {