Compare commits

...

10 Commits

Author SHA1 Message Date
lejianwen
184d3d357d optimize add ab from peer #84 2024-12-20 11:11:32 +08:00
lejianwen
50b3d85270 up docs and readme 2024-12-18 14:28:06 +08:00
lejianwen
09fdd34ba3 fix captcha 2024-12-18 13:51:06 +08:00
lejianwen
bba10261c5 fix captcha 2024-12-18 13:36:01 +08:00
lejianwen
46bfe54097 add show-swagger config #83 2024-12-18 12:50:09 +08:00
lejianwen
503e7a307e up docs 2024-12-18 12:44:06 +08:00
lejianwen
821b0a6faf add captcha #82 2024-12-18 12:43:55 +08:00
lejianwen
d60fdff179 split my from admin 2024-12-17 21:41:56 +08:00
lejianwen
fdd841e82a add batch add ab from peer and up my 2024-12-13 16:27:12 +08:00
lejianwen
2d6f0a116a add share record manage 2024-12-13 12:32:36 +08:00
48 changed files with 5354 additions and 490 deletions

View File

@@ -140,15 +140,12 @@
1. 如果已经登录了后台web client将自动直接登录 1. 如果已经登录了后台web client将自动直接登录
2. 如果没登录后台点击右上角登录即可api server已经自动配置好了 2. 如果没登录后台点击右上角登录即可api server已经自动配置好了
![webclient_conf](docs/webclient_conf.png)
3. 登录后会自动同步ID服务器和KEY 3. 登录后会自动同步ID服务器和KEY
4. 登录后会将地址簿自动保存到web client中方便使用 4. 登录后会将地址簿自动保存到web client中方便使用
5. 现已支持`v2 Preview`,访问路径是`/webclient2` 5. 现已支持`v2 Preview`,访问路径是`/webclient2`
![webclientv2](./docs/webclientv2.png) ![webclientv2](./docs/webclientv2.png)
6. `v2 preview` 部署 6. `v2 preview` 部署,参考[WIKI](https://github.com/lejianwen/rustdesk-api/wiki)
- 如果是通过`443`端口的`https`部署,必须配置反向代理,可以参考[官方文档](https://rustdesk.com/docs/en/self-host/rustdesk-server-pro/faq/#8-add-websocket-secure-wss-support-for-the-id-server-and-relay-server-to-enable-secure-communication-for-the-web-client)
- 如果是`http`或者其他的`https`端口部署,则和`v1`一样,配置好`21118`,`21119`即可
- 更多参考[Web-Client-V2-Preview-Document](https://github.com/lejianwen/rustdesk-api/wiki/Web-Client-V2-Preview-Document)
### 自动化文档: 使用 Swag 生成 API 文档,方便开发者理解和使用 API。 ### 自动化文档: 使用 Swag 生成 API 文档,方便开发者理解和使用 API。
@@ -180,6 +177,7 @@ lang: "en"
app: app:
web-client: 1 # 1:启用 0:禁用 web-client: 1 # 1:启用 0:禁用
register: false #是否开启注册 register: false #是否开启注册
show-swagger: 0 #是否显示swagger文档
gin: gin:
api-addr: "0.0.0.0:21114" api-addr: "0.0.0.0:21114"
mode: "release" mode: "release"
@@ -218,6 +216,7 @@ proxy:
| RUSTDESK_API_LANG | 语言 | `en`,`zh-CN` | | RUSTDESK_API_LANG | 语言 | `en`,`zh-CN` |
| RUSTDESK_API_APP_WEB_CLIENT | 是否启用web-client; 1:启用,0:不启用; 默认启用 | 1 | | RUSTDESK_API_APP_WEB_CLIENT | 是否启用web-client; 1:启用,0:不启用; 默认启用 | 1 |
| RUSTDESK_API_APP_REGISTER | 是否开启注册; `true`, `false` 默认`false` | `false` | | RUSTDESK_API_APP_REGISTER | 是否开启注册; `true`, `false` 默认`false` | `false` |
| RUSTDESK_API_APP_SHOW_SWAGGER | 是否可见swagger文档;`1`显示,`0`不显示,默认`0`不显示 | `1` |
| -----ADMIN配置----- | ---------- | ---------- | | -----ADMIN配置----- | ---------- | ---------- |
| RUSTDESK_API_ADMIN_TITLE | 后台标题 | `RustDesk Api Admin` | | RUSTDESK_API_ADMIN_TITLE | 后台标题 | `RustDesk Api Admin` |
| RUSTDESK_API_ADMIN_HELLO | 后台欢迎语,可以使用`html` | | | RUSTDESK_API_ADMIN_HELLO | 后台欢迎语,可以使用`html` | |
@@ -263,7 +262,7 @@ proxy:
lejianwen/rustdesk-api lejianwen/rustdesk-api
``` ```
2. 使用`docker compose`,参考[wiki](https://github.com/lejianwen/rustdesk-api/wiki) 2. 使用`docker compose`,参考[WIKI](https://github.com/lejianwen/rustdesk-api/wiki)
#### 下载release直接运行 #### 下载release直接运行

View File

@@ -144,16 +144,11 @@ installation are `admin` `admin`, please change the password immediately.
1. If you're already logged into the admin panel, the web client will log in automatically. 1. If you're already logged into the admin panel, the web client will log in automatically.
2. If you're not logged in, simply click the login button in the top right corner, and the API server will be 2. If you're not logged in, simply click the login button in the top right corner, and the API server will be
pre-configured. pre-configured.
![webclient_conf](docs/webclient_conf.png)
3. After logging in, the ID server and key will be automatically synced. 3. After logging in, the ID server and key will be automatically synced.
4. The address book will also be automatically saved to the web client for convenient use. 4. The address book will also be automatically saved to the web client for convenient use.
5. Now supports `v2 Preview`, accessible at `/webclient2` 5. Now supports `v2 Preview`, accessible at `/webclient2`
![webclientv2](./docs/webclientv2.png) ![webclientv2](./docs/webclientv2.png)
6. `v2 preview` deployment 6. `v2 preview` deployment, [WIKI](https://github.com/lejianwen/rustdesk-api/wiki)
- If deploying via `https` on port `443`, you must configure a reverse proxy. Refer to the [official documentation](https://rustdesk.com/docs/en/self-host/rustdesk-server-pro/faq/#8-add-websocket-secure-wss-support-for-the-id-server-and-relay-server-to-enable-secure-communication-for-the-web-client)
- If deploying via `http` or other `https` ports, configure `21118` and `21119` as with `v1`
- More [Web-Client-V2-Preview-Document](https://github.com/lejianwen/rustdesk-api/wiki/Web-Client-V2-Preview-Document)
### 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.
@@ -185,6 +180,7 @@ lang: "en"
app: app:
web-client: 1 # web client route 1:open 0:close web-client: 1 # web client route 1:open 0:close
register: false #register enable register: false #register enable
show-swagger: 0 #show swagger 1:open 0:close
gin: gin:
api-addr: "0.0.0.0:21114" api-addr: "0.0.0.0:21114"
mode: "release" mode: "release"
@@ -221,8 +217,9 @@ The prefix for variable names is `RUSTDESK_API`. If environment variables exist,
|------------------------------------|-------------------------------------------------------------------------|-------------------------------| |------------------------------------|-------------------------------------------------------------------------|-------------------------------|
| TZ | timezone | Asia/Shanghai | | TZ | timezone | Asia/Shanghai |
| RUSTDESK_API_LANG | Language | `en`,`zh-CN` | | RUSTDESK_API_LANG | Language | `en`,`zh-CN` |
| RUSTDESK_API_APP_WEB_CLIENT | web client on/off; 1: on, 0 off, deault 1 | 1 | | RUSTDESK_API_APP_WEB_CLIENT | web client on/off; 1: on, 0 off, default: 1 | 1 |
| RUSTDESK_API_APP_REGISTER | register enable; `true`, `false`; default:`false` | `false` | | RUSTDESK_API_APP_REGISTER | register enable; `true`, `false`; default:`false` | `false` |
| RUSTDESK_API_APP_SHOW_SWAGGER | swagger visible; 1: yes, 0: no; default: 0 | `0` |
| ----- ADMIN Configuration----- | ---------- | ---------- | | ----- ADMIN Configuration----- | ---------- | ---------- |
| RUSTDESK_API_ADMIN_TITLE | Admin Title | `RustDesk Api Admin` | | RUSTDESK_API_ADMIN_TITLE | Admin Title | `RustDesk Api Admin` |
| RUSTDESK_API_ADMIN_HELLO | Admin welcome message, you can use `html` | | | RUSTDESK_API_ADMIN_HELLO | Admin welcome message, you can use `html` | |
@@ -267,7 +264,7 @@ The prefix for variable names is `RUSTDESK_API`. If environment variables exist,
lejianwen/rustdesk-api lejianwen/rustdesk-api
``` ```
2. Using `docker-compose`,look [wiki](https://github.com/lejianwen/rustdesk-api/wiki) 2. Using `docker-compose`,look [WIKI](https://github.com/lejianwen/rustdesk-api/wiki)
#### Running from Release #### Running from Release

View File

@@ -2,6 +2,7 @@ lang: "zh-CN"
app: app:
web-client: 1 # 1:启用 0:禁用 web-client: 1 # 1:启用 0:禁用
register: false #是否开启注册 register: false #是否开启注册
show-swagger: 0 # 1:启用 0:禁用
admin: admin:
title: "RustDesk Api Admin" title: "RustDesk Api Admin"
hello-file: "./conf/admin/hello.html" #优先使用file hello-file: "./conf/admin/hello.html" #优先使用file

View File

@@ -14,8 +14,9 @@ const (
) )
type App struct { type App struct {
WebClient int `mapstructure:"web-client"` WebClient int `mapstructure:"web-client"`
Register bool `mapstructure:"register"` Register bool `mapstructure:"register"`
ShowSwagger int `mapstructure:"show-swagger"`
} }
type Admin struct { type Admin struct {
Title string `mapstructure:"title"` Title string `mapstructure:"title"`

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -2,6 +2,8 @@ basePath: /api
definitions: definitions:
Gwen_http_request_admin.Login: Gwen_http_request_admin.Login:
properties: properties:
captcha:
type: string
password: password:
type: string type: string
platform: platform:
@@ -182,6 +184,15 @@ definitions:
version: version:
type: string type: string
type: object type: object
admin.PeerShareRecordBatchDeleteForm:
properties:
ids:
items:
type: integer
type: array
required:
- ids
type: object
admin.ShareByWebClientForm: admin.ShareByWebClientForm:
properties: properties:
expire: expire:
@@ -201,6 +212,13 @@ definitions:
- password - password
- password_type - password_type
type: object type: object
admin.ShareRecordForm:
properties:
id:
type: integer
user_id:
type: integer
type: object
admin.TagForm: admin.TagForm:
properties: properties:
collection_id: collection_id:
@@ -757,7 +775,7 @@ info:
title: 管理系统API title: 管理系统API
version: "1.0" version: "1.0"
paths: paths:
/admin/address_book/create: /admin/address_book/batchCreate:
post: post:
consumes: consumes:
- application/json - application/json
@@ -790,6 +808,39 @@ paths:
summary: 批量创建地址簿 summary: 批量创建地址簿
tags: tags:
- 地址簿 - 地址簿
/admin/address_book/create:
post:
consumes:
- application/json
description: 创建地址簿
parameters:
- description: 地址簿信息
in: body
name: body
required: true
schema:
$ref: '#/definitions/admin.AddressBookForm'
produces:
- application/json
responses:
"200":
description: OK
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
data:
$ref: '#/definitions/model.AddressBook'
type: object
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.Response'
security:
- token: []
summary: 创建地址簿
tags:
- 地址簿
/admin/address_book/delete: /admin/address_book/delete:
post: post:
consumes: consumes:
@@ -1959,6 +2010,683 @@ paths:
summary: 登出 summary: 登出
tags: tags:
- 登录 - 登录
/admin/my/address_book/create:
post:
consumes:
- application/json
description: 创建地址簿
parameters:
- description: 地址簿信息
in: body
name: body
required: true
schema:
$ref: '#/definitions/admin.AddressBookForm'
produces:
- application/json
responses:
"200":
description: OK
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
data:
$ref: '#/definitions/model.AddressBook'
type: object
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.Response'
security:
- token: []
summary: 创建地址簿
tags:
- 我的地址簿
/admin/my/address_book/delete:
post:
consumes:
- application/json
description: 地址簿删除
parameters:
- description: 地址簿信息
in: body
name: body
required: true
schema:
$ref: '#/definitions/admin.AddressBookForm'
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/my/address_book/list:
get:
consumes:
- application/json
description: 地址簿列表
parameters:
- description: 页码
in: query
name: page
type: integer
- description: 页大小
in: query
name: page_size
type: integer
- description: 用户id
in: query
name: user_id
type: integer
produces:
- application/json
responses:
"200":
description: OK
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
data:
$ref: '#/definitions/model.AddressBookList'
type: object
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.Response'
security:
- token: []
summary: 地址簿列表
tags:
- 我的地址簿
/admin/my/address_book/update:
post:
consumes:
- application/json
description: 地址簿编辑
parameters:
- description: 地址簿信息
in: body
name: body
required: true
schema:
$ref: '#/definitions/admin.AddressBookForm'
produces:
- application/json
responses:
"200":
description: OK
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
data:
$ref: '#/definitions/model.AddressBook'
type: object
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.Response'
security:
- token: []
summary: 地址簿编辑
tags:
- 我的地址簿
/admin/my/address_book_collection/create:
post:
consumes:
- application/json
description: 创建地址簿名称
parameters:
- description: 地址簿名称信息
in: body
name: body
required: true
schema:
$ref: '#/definitions/model.AddressBookCollection'
produces:
- application/json
responses:
"200":
description: OK
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
data:
$ref: '#/definitions/model.AddressBookCollection'
type: object
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.Response'
security:
- token: []
summary: 创建地址簿名称
tags:
- 我的地址簿名称
/admin/my/address_book_collection/delete:
post:
consumes:
- application/json
description: 地址簿名称删除
parameters:
- description: 地址簿名称信息
in: body
name: body
required: true
schema:
$ref: '#/definitions/model.AddressBookCollection'
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/my/address_book_collection/list:
get:
consumes:
- application/json
description: 地址簿名称列表
parameters:
- description: 页码
in: query
name: page
type: integer
- description: 页大小
in: query
name: page_size
type: integer
produces:
- application/json
responses:
"200":
description: OK
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
data:
$ref: '#/definitions/model.AddressBookCollectionList'
type: object
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.Response'
security:
- token: []
summary: 地址簿名称列表
tags:
- 我的地址簿名称
/admin/my/address_book_collection/update:
post:
consumes:
- application/json
description: 地址簿名称编辑
parameters:
- description: 地址簿名称信息
in: body
name: body
required: true
schema:
$ref: '#/definitions/model.AddressBookCollection'
produces:
- application/json
responses:
"200":
description: OK
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
data:
$ref: '#/definitions/model.AddressBookCollection'
type: object
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.Response'
security:
- token: []
summary: 地址簿名称编辑
tags:
- 我的地址簿名称
/admin/my/address_book_collection_rule/create:
post:
consumes:
- application/json
description: 创建地址簿规则
parameters:
- description: 地址簿规则信息
in: body
name: body
required: true
schema:
$ref: '#/definitions/model.AddressBookCollectionRule'
produces:
- application/json
responses:
"200":
description: OK
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
data:
$ref: '#/definitions/model.AddressBookCollection'
type: object
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.Response'
security:
- token: []
summary: 创建地址簿规则
tags:
- 我的地址簿规则
/admin/my/address_book_collection_rule/delete:
post:
consumes:
- application/json
description: 地址簿规则删除
parameters:
- description: 地址簿规则信息
in: body
name: body
required: true
schema:
$ref: '#/definitions/model.AddressBookCollectionRule'
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/my/address_book_collection_rule/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: is_my
type: integer
- description: 用户id
in: query
name: user_id
type: integer
- description: 地址簿集合id
in: query
name: collection_id
type: integer
produces:
- application/json
responses:
"200":
description: OK
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
data:
$ref: '#/definitions/model.AddressBookCollectionList'
type: object
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.Response'
security:
- token: []
summary: 地址簿规则列表
tags:
- 我的地址簿规则
/admin/my/address_book_collection_rule/update:
post:
consumes:
- application/json
description: 地址簿规则编辑
parameters:
- description: 地址簿规则信息
in: body
name: body
required: true
schema:
$ref: '#/definitions/model.AddressBookCollectionRule'
produces:
- application/json
responses:
"200":
description: OK
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
data:
$ref: '#/definitions/model.AddressBookCollection'
type: object
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.Response'
security:
- token: []
summary: 地址簿规则编辑
tags:
- 我的地址簿规则
/admin/my/peer/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: time_ago
type: integer
- description: ID
in: query
name: id
type: string
- description: 主机名
in: query
name: hostname
type: string
- description: uuids 用逗号分隔
in: query
name: uuids
type: string
produces:
- application/json
responses:
"200":
description: OK
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
data:
$ref: '#/definitions/model.PeerList'
type: object
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.Response'
security:
- token: []
summary: 设备列表
tags:
- 我的设备
/admin/my/share_record/batchDelete:
post:
consumes:
- application/json
description: 批量删除我的分享记录
parameters:
- description: id
in: body
name: body
required: true
schema:
$ref: '#/definitions/admin.PeerShareRecordBatchDeleteForm'
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/my/share_record/delete:
post:
consumes:
- application/json
description: 分享记录删除
parameters:
- description: 分享记录信息
in: body
name: body
required: true
schema:
$ref: '#/definitions/admin.ShareRecordForm'
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/my/share_record/list:
get:
consumes:
- application/json
description: 分享记录列表
parameters:
- description: 页码
in: query
name: page
type: integer
- description: 页大小
in: query
name: page_size
type: integer
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/response.Response'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.Response'
security:
- token: []
summary: 分享记录列表
tags:
- 我的分享记录
/admin/my/tag/create:
post:
consumes:
- application/json
description: 创建标签
parameters:
- description: 标签信息
in: body
name: body
required: true
schema:
$ref: '#/definitions/admin.TagForm'
produces:
- application/json
responses:
"200":
description: OK
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
data:
$ref: '#/definitions/model.Tag'
type: object
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.Response'
security:
- token: []
summary: 创建标签
tags:
- 我的标签
/admin/my/tag/delete:
post:
consumes:
- application/json
description: 标签删除
parameters:
- description: 标签信息
in: body
name: body
required: true
schema:
$ref: '#/definitions/admin.TagForm'
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/my/tag/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: is_my
type: integer
- description: 用户id
in: query
name: user_id
type: integer
produces:
- application/json
responses:
"200":
description: OK
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
data:
$ref: '#/definitions/model.TagList'
type: object
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.Response'
security:
- token: []
summary: 标签列表
tags:
- 我的标签
/admin/my/tag/update:
post:
consumes:
- application/json
description: 标签编辑
parameters:
- description: 标签信息
in: body
name: body
required: true
schema:
$ref: '#/definitions/admin.TagForm'
produces:
- application/json
responses:
"200":
description: OK
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
data:
$ref: '#/definitions/model.Tag'
type: object
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.Response'
security:
- token: []
summary: 标签编辑
tags:
- 我的标签
/admin/oauth/create: /admin/oauth/create:
post: post:
consumes: consumes:
@@ -2381,6 +3109,96 @@ paths:
summary: RUSTDESK服务配置 summary: RUSTDESK服务配置
tags: tags:
- ADMIN - ADMIN
/admin/share_record/batchDelete:
post:
consumes:
- application/json
description: 批量分享记录
parameters:
- description: id
in: body
name: body
required: true
schema:
$ref: '#/definitions/admin.PeerShareRecordBatchDeleteForm'
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/share_record/delete:
post:
consumes:
- application/json
description: 分享记录删除
parameters:
- description: 分享记录信息
in: body
name: body
required: true
schema:
$ref: '#/definitions/admin.ShareRecordForm'
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/share_record/list:
get:
consumes:
- application/json
description: 分享记录列表
parameters:
- description: 用户ID
in: query
name: user_id
type: integer
- description: 页码
in: query
name: page
type: integer
- description: 页大小
in: query
name: page_size
type: integer
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/response.Response'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.Response'
security:
- token: []
summary: 分享记录列表
tags:
- 分享记录
/admin/tag/create: /admin/tag/create:
post: post:
consumes: consumes:
@@ -2764,57 +3582,6 @@ paths:
summary: 我的授权 summary: 我的授权
tags: tags:
- 用户 - 用户
/admin/user/myPeer:
get:
consumes:
- application/json
description: 我的设备列表
parameters:
- description: 页码
in: query
name: page
type: integer
- description: 页大小
in: query
name: page_size
type: integer
- description: 时间
in: query
name: time_ago
type: integer
- description: ID
in: query
name: id
type: string
- description: 主机名
in: query
name: hostname
type: string
- description: uuids 用逗号分隔
in: query
name: uuids
type: string
produces:
- application/json
responses:
"200":
description: OK
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
data:
$ref: '#/definitions/model.PeerList'
type: object
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.Response'
security:
- token: []
summary: 我的设备列表
tags:
- 设备
/admin/user/update: /admin/user/update:
post: post:
consumes: consumes:
@@ -2876,7 +3643,7 @@ paths:
summary: 修改密码 summary: 修改密码
tags: tags:
- 用户 - 用户
/admin/user_token/delete: /admin/user_token/batchDelete:
post: post:
consumes: consumes:
- application/json - application/json
@@ -2904,6 +3671,34 @@ paths:
summary: 登录凭证批量删除 summary: 登录凭证批量删除
tags: tags:
- 登录凭证 - 登录凭证
/admin/user_token/delete:
post:
consumes:
- application/json
description: 登录凭证删除
parameters:
- description: 登录凭证信息
in: body
name: body
required: true
schema:
$ref: '#/definitions/model.UserToken'
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/user_token/list: /admin/user_token/list:
get: get:
consumes: consumes:

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 97 KiB

View File

@@ -1042,6 +1042,40 @@ const docTemplateapi = `{
} }
}, },
"/server-config": { "/server-config": {
"get": {
"security": [
{
"token": []
}
],
"description": "服务配置,给webclient提供api-server",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"WEBCLIENT"
],
"summary": "服务配置",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/server-config-v2": {
"get": { "get": {
"security": [ "security": [
{ {

View File

@@ -1035,6 +1035,40 @@
} }
}, },
"/server-config": { "/server-config": {
"get": {
"security": [
{
"token": []
}
],
"description": "服务配置,给webclient提供api-server",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"WEBCLIENT"
],
"summary": "服务配置",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/server-config-v2": {
"get": { "get": {
"security": [ "security": [
{ {

View File

@@ -850,6 +850,27 @@ paths:
tags: tags:
- 群组 - 群组
/server-config: /server-config:
get:
consumes:
- application/json
description: 服务配置,给webclient提供api-server
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/response.Response'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.Response'
security:
- token: []
summary: 服务配置
tags:
- WEBCLIENT
/server-config-v2:
get: get:
consumes: consumes:
- application/json - application/json

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

3
go.mod
View File

@@ -43,6 +43,7 @@ require (
github.com/go-openapi/swag v0.19.15 // indirect github.com/go-openapi/swag v0.19.15 // indirect
github.com/go-sql-driver/mysql v1.7.0 // indirect github.com/go-sql-driver/mysql v1.7.0 // indirect
github.com/goccy/go-json v0.10.0 // indirect github.com/goccy/go-json v0.10.0 // indirect
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/inflection v1.0.0 // indirect
@@ -58,6 +59,7 @@ require (
github.com/mitchellh/mapstructure v1.4.2 // indirect github.com/mitchellh/mapstructure v1.4.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/mojocn/base64Captcha v1.3.6 // indirect
github.com/pelletier/go-toml v1.9.4 // indirect github.com/pelletier/go-toml v1.9.4 // indirect
github.com/pelletier/go-toml/v2 v2.0.6 // indirect github.com/pelletier/go-toml/v2 v2.0.6 // indirect
github.com/spf13/afero v1.6.0 // indirect github.com/spf13/afero v1.6.0 // indirect
@@ -69,6 +71,7 @@ require (
github.com/ugorji/go/codec v1.2.9 // indirect github.com/ugorji/go/codec v1.2.9 // indirect
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
golang.org/x/crypto v0.23.0 // indirect golang.org/x/crypto v0.23.0 // indirect
golang.org/x/image v0.13.0 // indirect
golang.org/x/net v0.25.0 // indirect golang.org/x/net v0.25.0 // indirect
golang.org/x/sys v0.25.0 // indirect golang.org/x/sys v0.25.0 // indirect
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect

View File

@@ -4,7 +4,6 @@ import (
"Gwen/global" "Gwen/global"
"Gwen/http/request/admin" "Gwen/http/request/admin"
"Gwen/http/response" "Gwen/http/response"
"Gwen/model"
"Gwen/service" "Gwen/service"
"encoding/json" "encoding/json"
_ "encoding/json" _ "encoding/json"
@@ -31,11 +30,6 @@ func (ct *AddressBook) Detail(c *gin.Context) {
id := c.Param("id") id := c.Param("id")
iid, _ := strconv.Atoi(id) iid, _ := strconv.Atoi(id)
t := service.AllService.AddressBookService.InfoByRowId(uint(iid)) t := service.AllService.AddressBookService.InfoByRowId(uint(iid))
u := service.AllService.UserService.CurUser(c)
if !service.AllService.UserService.IsAdmin(u) && t.UserId != u.Id {
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
return
}
if t.RowId > 0 { if t.RowId > 0 {
response.Success(c, t) response.Success(c, t)
return return
@@ -67,9 +61,9 @@ func (ct *AddressBook) Create(c *gin.Context) {
return return
} }
t := f.ToAddressBook() t := f.ToAddressBook()
u := service.AllService.UserService.CurUser(c) if t.UserId == 0 {
if !service.AllService.UserService.IsAdmin(u) || t.UserId == 0 { response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
t.UserId = u.Id return
} }
if t.CollectionId > 0 && !service.AllService.AddressBookService.CheckCollectionOwner(t.UserId, t.CollectionId) { if t.CollectionId > 0 && !service.AllService.AddressBookService.CheckCollectionOwner(t.UserId, t.CollectionId) {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")) response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
@@ -99,7 +93,7 @@ func (ct *AddressBook) Create(c *gin.Context) {
// @Param body body admin.AddressBookForm true "地址簿信息" // @Param body body admin.AddressBookForm true "地址簿信息"
// @Success 200 {object} response.Response{data=model.AddressBook} // @Success 200 {object} response.Response{data=model.AddressBook}
// @Failure 500 {object} response.Response // @Failure 500 {object} response.Response
// @Router /admin/address_book/create [post] // @Router /admin/address_book/batchCreate [post]
// @Security token // @Security token
func (ct *AddressBook) BatchCreate(c *gin.Context) { func (ct *AddressBook) BatchCreate(c *gin.Context) {
f := &admin.AddressBookForm{} f := &admin.AddressBookForm{}
@@ -112,9 +106,21 @@ func (ct *AddressBook) BatchCreate(c *gin.Context) {
response.Fail(c, 101, errList[0]) response.Fail(c, 101, errList[0])
return return
} }
ul := len(f.UserIds)
if ul == 0 {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
return
}
if ul > 1 {
//多用户置空标签
f.Tags = []string{}
//多用户只能创建到默认地址簿
f.CollectionId = 0
}
//创建标签 //创建标签
for _, fu := range f.UserIds { /*for _, fu := range f.UserIds {
if fu == 0 { if fu == 0 {
continue continue
} }
@@ -127,13 +133,13 @@ func (ct *AddressBook) BatchCreate(c *gin.Context) {
}) })
} }
} }
} }*/
ts := f.ToAddressBooks() ts := f.ToAddressBooks()
for _, t := range ts { for _, t := range ts {
if t.UserId == 0 { if t.UserId == 0 {
continue continue
} }
ex := service.AllService.AddressBookService.InfoByUserIdAndId(t.UserId, t.Id) ex := service.AllService.AddressBookService.InfoByUserIdAndIdAndCid(t.UserId, t.Id, t.CollectionId)
if ex.RowId == 0 { if ex.RowId == 0 {
service.AllService.AddressBookService.Create(t) service.AllService.AddressBookService.Create(t)
} }
@@ -162,10 +168,6 @@ func (ct *AddressBook) List(c *gin.Context) {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error()) response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return return
} }
u := service.AllService.UserService.CurUser(c)
if !service.AllService.UserService.IsAdmin(u) || query.IsMy == 1 {
query.UserId = int(u.Id)
}
res := service.AllService.AddressBookService.List(query.Page, query.PageSize, func(tx *gorm.DB) { res := service.AllService.AddressBookService.List(query.Page, query.PageSize, func(tx *gorm.DB) {
tx.Preload("Collection", func(txc *gorm.DB) *gorm.DB { tx.Preload("Collection", func(txc *gorm.DB) *gorm.DB {
return txc.Select("id,name") return txc.Select("id,name")
@@ -191,11 +193,6 @@ func (ct *AddressBook) List(c *gin.Context) {
for _, ab := range res.AddressBooks { for _, ab := range res.AddressBooks {
abCIds = append(abCIds, ab.CollectionId) abCIds = append(abCIds, ab.CollectionId)
} }
//获取地址簿名称
//cRes := service.AllService.AddressBookService.ListCollection(1, 999, func(tx *gorm.DB) {
// tx.Where("id in ?", abCIds)
//})
//
response.Success(c, res) response.Success(c, res)
} }
@@ -222,15 +219,15 @@ func (ct *AddressBook) Update(c *gin.Context) {
return return
} }
if f.RowId == 0 { if f.RowId == 0 {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")) response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
return
}
ex := service.AllService.AddressBookService.InfoByRowId(f.RowId)
if ex.RowId == 0 {
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
return return
} }
t := f.ToAddressBook() t := f.ToAddressBook()
u := service.AllService.UserService.CurUser(c)
if !service.AllService.UserService.IsAdmin(u) && t.UserId != u.Id {
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
return
}
if t.CollectionId > 0 && !service.AllService.AddressBookService.CheckCollectionOwner(t.UserId, t.CollectionId) { if t.CollectionId > 0 && !service.AllService.AddressBookService.CheckCollectionOwner(t.UserId, t.CollectionId) {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")) response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
return return
@@ -271,21 +268,12 @@ func (ct *AddressBook) Delete(c *gin.Context) {
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound")) response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
return return
} }
u := service.AllService.UserService.CurUser(c) err := service.AllService.AddressBookService.Delete(t)
if !service.AllService.UserService.IsAdmin(u) && t.UserId != u.Id { if err == nil {
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess")) response.Success(c, nil)
return return
} }
if u.Id > 0 { response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
err := service.AllService.AddressBookService.Delete(t)
if err == nil {
response.Success(c, nil)
return
}
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
return
}
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
} }
// ShareByWebClient // ShareByWebClient
@@ -335,7 +323,11 @@ func (ct *AddressBook) BatchCreateFromPeers(c *gin.Context) {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error()) response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return return
} }
u := service.AllService.UserService.CurUser(c)
if f.UserId == 0 {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
return
}
if f.CollectionId != 0 { if f.CollectionId != 0 {
collection := service.AllService.AddressBookService.CollectionInfoById(f.CollectionId) collection := service.AllService.AddressBookService.CollectionInfoById(f.CollectionId)
@@ -343,17 +335,13 @@ func (ct *AddressBook) BatchCreateFromPeers(c *gin.Context) {
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound")) response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
return return
} }
if collection.UserId != u.Id {
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
return
}
} }
peers := service.AllService.PeerService.List(1, 999, func(tx *gorm.DB) { pl := int64(len(f.PeerIds))
peers := service.AllService.PeerService.List(1, uint(pl), func(tx *gorm.DB) {
tx.Where("row_id in ?", f.PeerIds) tx.Where("row_id in ?", f.PeerIds)
tx.Where("user_id = ?", u.Id)
}) })
if peers.Total == 0 { if peers.Total == 0 || pl != peers.Total {
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound")) response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
return return
} }
@@ -363,7 +351,8 @@ func (ct *AddressBook) BatchCreateFromPeers(c *gin.Context) {
ab := service.AllService.AddressBookService.FromPeer(peer) ab := service.AllService.AddressBookService.FromPeer(peer)
ab.Tags = tags ab.Tags = tags
ab.CollectionId = f.CollectionId ab.CollectionId = f.CollectionId
ex := service.AllService.AddressBookService.InfoByUserIdAndIdAndCid(u.Id, ab.Id, ab.CollectionId) ab.UserId = f.UserId
ex := service.AllService.AddressBookService.InfoByUserIdAndIdAndCid(f.UserId, ab.Id, ab.CollectionId)
if ex.RowId != 0 { if ex.RowId != 0 {
continue continue
} }
@@ -371,27 +360,3 @@ func (ct *AddressBook) BatchCreateFromPeers(c *gin.Context) {
} }
response.Success(c, nil) response.Success(c, nil)
} }
func (ct *AddressBook) BatchUpdateTags(c *gin.Context) {
f := &admin.BatchUpdateTagsForm{}
if err := c.ShouldBindJSON(f); err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
u := service.AllService.UserService.CurUser(c)
abs := service.AllService.AddressBookService.List(1, 999, func(tx *gorm.DB) {
tx.Where("row_id in ?", f.RowIds)
tx.Where("user_id = ?", u.Id)
})
if abs.Total == 0 {
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
return
}
err := service.AllService.AddressBookService.BatchUpdateTags(abs.AddressBooks, f.Tags)
if err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
return
}
response.Success(c, nil)
}

View File

@@ -29,11 +29,6 @@ func (abc *AddressBookCollection) Detail(c *gin.Context) {
id := c.Param("id") id := c.Param("id")
iid, _ := strconv.Atoi(id) iid, _ := strconv.Atoi(id)
t := service.AllService.AddressBookService.CollectionInfoById(uint(iid)) t := service.AllService.AddressBookService.CollectionInfoById(uint(iid))
u := service.AllService.UserService.CurUser(c)
if !service.AllService.UserService.IsAdmin(u) && t.UserId != u.Id {
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
return
}
if t.Id > 0 { if t.Id > 0 {
response.Success(c, t) response.Success(c, t)
return return
@@ -64,12 +59,11 @@ func (abc *AddressBookCollection) Create(c *gin.Context) {
response.Fail(c, 101, errList[0]) response.Fail(c, 101, errList[0])
return return
} }
//t := f.ToAddressBookCollection() if f.UserId == 0 {
t := f response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
u := service.AllService.UserService.CurUser(c) return
if !service.AllService.UserService.IsAdmin(u) || t.UserId == 0 {
t.UserId = u.Id
} }
t := f
err := service.AllService.AddressBookService.CreateCollection(t) err := service.AllService.AddressBookService.CreateCollection(t)
if err != nil { if err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error()) response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
@@ -98,10 +92,6 @@ func (abc *AddressBookCollection) List(c *gin.Context) {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error()) response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return return
} }
u := service.AllService.UserService.CurUser(c)
if !service.AllService.UserService.IsAdmin(u) || query.IsMy == 1 {
query.UserId = int(u.Id)
}
res := service.AllService.AddressBookService.ListCollection(query.Page, query.PageSize, func(tx *gorm.DB) { res := service.AllService.AddressBookService.ListCollection(query.Page, query.PageSize, func(tx *gorm.DB) {
if query.UserId > 0 { if query.UserId > 0 {
tx.Where("user_id = ?", query.UserId) tx.Where("user_id = ?", query.UserId)
@@ -137,11 +127,6 @@ func (abc *AddressBookCollection) Update(c *gin.Context) {
return return
} }
t := f //f.ToAddressBookCollection() t := f //f.ToAddressBookCollection()
u := service.AllService.UserService.CurUser(c)
if !service.AllService.UserService.IsAdmin(u) && t.UserId != u.Id {
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
return
}
err := service.AllService.AddressBookService.UpdateCollection(t) err := service.AllService.AddressBookService.UpdateCollection(t)
if err != nil { if err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error()) response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
@@ -173,20 +158,15 @@ func (abc *AddressBookCollection) Delete(c *gin.Context) {
response.Fail(c, 101, errList[0]) response.Fail(c, 101, errList[0])
return return
} }
t := service.AllService.AddressBookService.CollectionInfoById(f.Id) ex := service.AllService.AddressBookService.CollectionInfoById(f.Id)
u := service.AllService.UserService.CurUser(c) if ex.Id == 0 {
if !service.AllService.UserService.IsAdmin(u) && t.UserId != u.Id { response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
return return
} }
if u.Id > 0 { err := service.AllService.AddressBookService.DeleteCollection(ex)
err := service.AllService.AddressBookService.DeleteCollection(t) if err == nil {
if err == nil { response.Success(c, nil)
response.Success(c, nil)
return
}
response.Fail(c, 101, err.Error())
return return
} }
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound")) response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
} }

View File

@@ -35,10 +35,6 @@ func (abcr *AddressBookCollectionRule) List(c *gin.Context) {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error()) response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return return
} }
u := service.AllService.UserService.CurUser(c)
if !service.AllService.UserService.IsAdmin(u) || query.IsMy == 1 {
query.UserId = int(u.Id)
}
res := service.AllService.AddressBookService.ListRules(query.Page, query.PageSize, func(tx *gorm.DB) { res := service.AllService.AddressBookService.ListRules(query.Page, query.PageSize, func(tx *gorm.DB) {
if query.UserId > 0 { if query.UserId > 0 {
@@ -66,17 +62,11 @@ func (abcr *AddressBookCollectionRule) Detail(c *gin.Context) {
id := c.Param("id") id := c.Param("id")
iid, _ := strconv.Atoi(id) iid, _ := strconv.Atoi(id)
t := service.AllService.AddressBookService.RuleInfoById(uint(iid)) t := service.AllService.AddressBookService.RuleInfoById(uint(iid))
u := service.AllService.UserService.CurUser(c)
if !service.AllService.UserService.IsAdmin(u) && t.UserId != u.Id {
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
return
}
if t.Id > 0 { if t.Id > 0 {
response.Success(c, t) response.Success(c, t)
return return
} }
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound")) response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
return
} }
// Create 创建地址簿规则 // Create 创建地址簿规则
@@ -105,13 +95,8 @@ func (abcr *AddressBookCollectionRule) Create(c *gin.Context) {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")) response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
return return
} }
//t := f.ToAddressBookCollection()
t := f t := f
u := service.AllService.UserService.CurUser(c) msg, res := abcr.CheckForm(t)
if t.UserId == 0 {
t.UserId = u.Id
}
msg, res := abcr.CheckForm(u, t)
if !res { if !res {
response.Fail(c, 101, response.TranslateMsg(c, msg)) response.Fail(c, 101, response.TranslateMsg(c, msg))
return return
@@ -124,9 +109,9 @@ func (abcr *AddressBookCollectionRule) Create(c *gin.Context) {
response.Success(c, nil) response.Success(c, nil)
} }
func (abcr *AddressBookCollectionRule) CheckForm(u *model.User, t *model.AddressBookCollectionRule) (string, bool) { func (abcr *AddressBookCollectionRule) CheckForm(t *model.AddressBookCollectionRule) (string, bool) {
if !service.AllService.UserService.IsAdmin(u) && t.UserId != u.Id { if t.UserId == 0 {
return "NoAccess", false return "ParamsError", false
} }
if t.CollectionId > 0 && !service.AllService.AddressBookService.CheckCollectionOwner(t.UserId, t.CollectionId) { if t.CollectionId > 0 && !service.AllService.AddressBookService.CheckCollectionOwner(t.UserId, t.CollectionId) {
return "ParamsError", false return "ParamsError", false
@@ -141,15 +126,7 @@ func (abcr *AddressBookCollectionRule) CheckForm(u *model.User, t *model.Address
if tou.Id == 0 { if tou.Id == 0 {
return "ItemNotFound", false return "ItemNotFound", false
} }
//非管理员不能分享给非本组织用户
if tou.GroupId != u.GroupId && !service.AllService.UserService.IsAdmin(u) {
return "NoAccess", false
}
} else if t.Type == model.ShareAddressBookRuleTypeGroup { } else if t.Type == model.ShareAddressBookRuleTypeGroup {
if t.ToId != u.GroupId && !service.AllService.UserService.IsAdmin(u) {
return "NoAccess", false
}
tog := service.AllService.GroupService.InfoById(t.ToId) tog := service.AllService.GroupService.InfoById(t.ToId)
if tog.Id == 0 { if tog.Id == 0 {
return "ItemNotFound", false return "ItemNotFound", false
@@ -194,9 +171,8 @@ func (abcr *AddressBookCollectionRule) Update(c *gin.Context) {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")) response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
return return
} }
t := f //f.ToAddressBookCollection() t := f
u := service.AllService.UserService.CurUser(c) msg, res := abcr.CheckForm(t)
msg, res := abcr.CheckForm(u, t)
if !res { if !res {
response.Fail(c, 101, response.TranslateMsg(c, msg)) response.Fail(c, 101, response.TranslateMsg(c, msg))
return return
@@ -232,20 +208,15 @@ func (abcr *AddressBookCollectionRule) Delete(c *gin.Context) {
response.Fail(c, 101, errList[0]) response.Fail(c, 101, errList[0])
return return
} }
t := service.AllService.AddressBookService.RuleInfoById(f.Id) ex := service.AllService.AddressBookService.RuleInfoById(f.Id)
u := service.AllService.UserService.CurUser(c) if ex.Id == 0 {
if !service.AllService.UserService.IsAdmin(u) && t.UserId != u.Id { response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
return return
} }
if t.Id > 0 { err := service.AllService.AddressBookService.DeleteRule(ex)
err := service.AllService.AddressBookService.DeleteRule(t) if err == nil {
if err == nil { response.Success(c, nil)
response.Success(c, nil)
return
}
response.Fail(c, 101, err.Error())
return return
} }
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound")) response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
} }

View File

@@ -11,11 +11,135 @@ import (
"Gwen/service" "Gwen/service"
"fmt" "fmt"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/mojocn/base64Captcha"
"sync"
"time"
) )
type Login struct { type Login struct {
} }
// Captcha 验证码结构
type Captcha struct {
Id string `json:"id"` // 验证码 ID
B64 string `json:"b64"` // base64 验证码
Code string `json:"-"` // 验证码内容
ExpiresAt time.Time `json:"-"` // 过期时间
}
type LoginLimiter struct {
mu sync.RWMutex
failCount map[string]int // 记录每个 IP 的失败次数
timestamp map[string]time.Time // 记录每个 IP 的最后失败时间
captchas map[string]Captcha // 每个 IP 的验证码
threshold int // 失败阈值
expiry time.Duration // 失败记录过期时间
}
func NewLoginLimiter(threshold int, expiry time.Duration) *LoginLimiter {
return &LoginLimiter{
failCount: make(map[string]int),
timestamp: make(map[string]time.Time),
captchas: make(map[string]Captcha),
threshold: threshold,
expiry: expiry,
}
}
// RecordFailure 记录登录失败
func (l *LoginLimiter) RecordFailure(ip string) {
l.mu.Lock()
defer l.mu.Unlock()
// 如果该 IP 的记录已经过期,重置计数
if lastTime, exists := l.timestamp[ip]; exists && time.Since(lastTime) > l.expiry {
l.failCount[ip] = 0
}
// 更新失败次数和时间戳
l.failCount[ip]++
l.timestamp[ip] = time.Now()
}
// NeedsCaptcha 检查是否需要验证码
func (l *LoginLimiter) NeedsCaptcha(ip string) bool {
l.mu.RLock()
defer l.mu.RUnlock()
// 检查记录是否存在且未过期
if lastTime, exists := l.timestamp[ip]; exists && time.Since(lastTime) <= l.expiry {
return l.failCount[ip] >= l.threshold
}
return false
}
// GenerateCaptcha 为指定 IP 生成验证码
func (l *LoginLimiter) GenerateCaptcha(ip string) Captcha {
l.mu.Lock()
defer l.mu.Unlock()
capd := base64Captcha.NewDriverString(50, 150, 5, 10, 4, "1234567890abcdefghijklmnopqrstuvwxyz", nil, nil, nil)
b64cap := base64Captcha.NewCaptcha(capd, base64Captcha.DefaultMemStore)
id, b64s, answer, err := b64cap.Generate()
if err != nil {
global.Logger.Error("Generate captcha failed: " + err.Error())
return Captcha{}
}
// 保存验证码到对应 IP
l.captchas[ip] = Captcha{
Id: id,
B64: b64s,
Code: answer,
ExpiresAt: time.Now().Add(5 * time.Minute),
}
return l.captchas[ip]
}
// VerifyCaptcha 验证指定 IP 的验证码
func (l *LoginLimiter) VerifyCaptcha(ip, code string) bool {
l.mu.RLock()
defer l.mu.RUnlock()
// 检查验证码是否存在且未过期
if captcha, exists := l.captchas[ip]; exists && time.Now().Before(captcha.ExpiresAt) {
return captcha.Code == code
}
return false
}
// RemoveCaptcha 移除指定 IP 的验证码
func (l *LoginLimiter) RemoveCaptcha(ip string) {
l.mu.Lock()
defer l.mu.Unlock()
delete(l.captchas, ip)
}
// CleanupExpired 清理过期的记录
func (l *LoginLimiter) CleanupExpired() {
l.mu.Lock()
defer l.mu.Unlock()
now := time.Now()
for ip, lastTime := range l.timestamp {
if now.Sub(lastTime) > l.expiry {
delete(l.failCount, ip)
delete(l.timestamp, ip)
delete(l.captchas, ip)
}
}
}
func (l *LoginLimiter) RemoveRecord(ip string) {
l.mu.Lock()
defer l.mu.Unlock()
delete(l.failCount, ip)
delete(l.timestamp, ip)
delete(l.captchas, ip)
}
var loginLimiter = NewLoginLimiter(3, 5*time.Minute)
// Login 登录 // Login 登录
// @Tags 登录 // @Tags 登录
// @Summary 登录 // @Summary 登录
@@ -30,22 +154,39 @@ type Login struct {
func (ct *Login) Login(c *gin.Context) { func (ct *Login) Login(c *gin.Context) {
f := &admin.Login{} f := &admin.Login{}
err := c.ShouldBindJSON(f) err := c.ShouldBindJSON(f)
clientIp := c.ClientIP()
if err != nil { if err != nil {
global.Logger.Warn(fmt.Sprintf("Login Fail: %s %s %s", "ParamsError", c.RemoteIP(), c.ClientIP())) global.Logger.Warn(fmt.Sprintf("Login Fail: %s %s %s", "ParamsError", c.RemoteIP(), clientIp))
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error()) response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return return
} }
errList := global.Validator.ValidStruct(c, f) errList := global.Validator.ValidStruct(c, f)
if len(errList) > 0 { if len(errList) > 0 {
global.Logger.Warn(fmt.Sprintf("Login Fail: %s %s %s", "ParamsError", c.RemoteIP(), c.ClientIP())) global.Logger.Warn(fmt.Sprintf("Login Fail: %s %s %s", "ParamsError", c.RemoteIP(), clientIp))
response.Fail(c, 101, errList[0]) response.Fail(c, 101, errList[0])
return return
} }
// 检查是否需要验证码
if loginLimiter.NeedsCaptcha(clientIp) {
if f.Captcha == "" || !loginLimiter.VerifyCaptcha(clientIp, f.Captcha) {
response.Fail(c, 101, response.TranslateMsg(c, "CaptchaError"))
return
}
}
u := service.AllService.UserService.InfoByUsernamePassword(f.Username, f.Password) u := service.AllService.UserService.InfoByUsernamePassword(f.Username, f.Password)
if u.Id == 0 { if u.Id == 0 {
global.Logger.Warn(fmt.Sprintf("Login Fail: %s %s %s", "UsernameOrPasswordError", c.RemoteIP(), c.ClientIP())) global.Logger.Warn(fmt.Sprintf("Login Fail: %s %s %s", "UsernameOrPasswordError", c.RemoteIP(), clientIp))
loginLimiter.RecordFailure(clientIp)
if loginLimiter.NeedsCaptcha(clientIp) {
// 移除原验证码,重新生成
loginLimiter.RemoveCaptcha(clientIp)
response.Fail(c, 110, response.TranslateMsg(c, "UsernameOrPasswordError"))
return
}
response.Fail(c, 101, response.TranslateMsg(c, "UsernameOrPasswordError")) response.Fail(c, 101, response.TranslateMsg(c, "UsernameOrPasswordError"))
return return
} }
@@ -54,13 +195,30 @@ func (ct *Login) Login(c *gin.Context) {
UserId: u.Id, UserId: u.Id,
Client: model.LoginLogClientWebAdmin, Client: model.LoginLogClientWebAdmin,
Uuid: "", //must be empty Uuid: "", //must be empty
Ip: c.ClientIP(), Ip: clientIp,
Type: model.LoginLogTypeAccount, Type: model.LoginLogTypeAccount,
Platform: f.Platform, Platform: f.Platform,
}) })
// 成功后清除记录
loginLimiter.RemoveRecord(clientIp)
// 清理过期记录
go loginLimiter.CleanupExpired()
responseLoginSuccess(c, u, ut.Token) responseLoginSuccess(c, u, ut.Token)
} }
func (ct *Login) Captcha(c *gin.Context) {
clientIp := c.ClientIP()
if !loginLimiter.NeedsCaptcha(clientIp) {
response.Fail(c, 101, response.TranslateMsg(c, "NoCaptchaRequired"))
return
}
captcha := loginLimiter.GenerateCaptcha(clientIp)
response.Success(c, gin.H{
"captcha": captcha,
})
}
// Logout 登出 // Logout 登出
// @Tags 登录 // @Tags 登录
@@ -90,10 +248,12 @@ func (ct *Login) Logout(c *gin.Context) {
// @Failure 500 {object} response.ErrorResponse // @Failure 500 {object} response.ErrorResponse
// @Router /admin/login-options [post] // @Router /admin/login-options [post]
func (ct *Login) LoginOptions(c *gin.Context) { func (ct *Login) LoginOptions(c *gin.Context) {
ip := c.ClientIP()
ops := service.AllService.OauthService.GetOauthProviders() ops := service.AllService.OauthService.GetOauthProviders()
response.Success(c, gin.H{ response.Success(c, gin.H{
"ops": ops, "ops": ops,
"register": global.Config.App.Register, "register": global.Config.App.Register,
"need_captcha": loginLimiter.NeedsCaptcha(ip),
}) })
} }
@@ -154,11 +314,10 @@ func (ct *Login) OidcAuthQuery(c *gin.Context) {
responseLoginSuccess(c, u, ut.Token) responseLoginSuccess(c, u, ut.Token)
} }
func responseLoginSuccess(c *gin.Context, u *model.User, token string) { func responseLoginSuccess(c *gin.Context, u *model.User, token string) {
lp := &adResp.LoginPayload{} lp := &adResp.LoginPayload{}
lp.FromUser(u) lp.FromUser(u)
lp.Token = token lp.Token = token
lp.RouteNames = service.AllService.UserService.RouteNames(u) lp.RouteNames = service.AllService.UserService.RouteNames(u)
response.Success(c, lp) response.Success(c, lp)
} }

View File

@@ -0,0 +1,271 @@
package my
import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/service"
"encoding/json"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
type AddressBook struct{}
// List 列表
// @Tags 我的地址簿
// @Summary 地址簿列表
// @Description 地址簿列表
// @Accept json
// @Produce json
// @Param page query int false "页码"
// @Param page_size query int false "页大小"
// @Param user_id query int false "用户id"
// @Success 200 {object} response.Response{data=model.AddressBookList}
// @Failure 500 {object} response.Response
// @Router /admin/my/address_book/list [get]
// @Security token
func (ct *AddressBook) List(c *gin.Context) {
query := &admin.AddressBookQuery{}
if err := c.ShouldBindQuery(query); err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
u := service.AllService.UserService.CurUser(c)
query.UserId = int(u.Id)
res := service.AllService.AddressBookService.List(query.Page, query.PageSize, func(tx *gorm.DB) {
//预加载地址簿名称
tx.Preload("Collection", func(txc *gorm.DB) *gorm.DB {
return txc.Select("id,name")
})
if query.Id != "" {
tx.Where("id like ?", "%"+query.Id+"%")
}
tx.Where("user_id = ?", query.UserId)
if query.Username != "" {
tx.Where("username like ?", "%"+query.Username+"%")
}
if query.Hostname != "" {
tx.Where("hostname like ?", "%"+query.Hostname+"%")
}
if query.CollectionId != nil && *query.CollectionId >= 0 {
tx.Where("collection_id = ?", query.CollectionId)
}
})
abCIds := make([]uint, 0)
for _, ab := range res.AddressBooks {
abCIds = append(abCIds, ab.CollectionId)
}
response.Success(c, res)
}
// Create 创建地址簿
// @Tags 我的地址簿
// @Summary 创建地址簿
// @Description 创建地址簿
// @Accept json
// @Produce json
// @Param body body admin.AddressBookForm true "地址簿信息"
// @Success 200 {object} response.Response{data=model.AddressBook}
// @Failure 500 {object} response.Response
// @Router /admin/my/address_book/create [post]
// @Security token
func (ct *AddressBook) Create(c *gin.Context) {
f := &admin.AddressBookForm{}
if err := c.ShouldBindJSON(f); err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
errList := global.Validator.ValidStruct(c, f)
if len(errList) > 0 {
response.Fail(c, 101, errList[0])
return
}
t := f.ToAddressBook()
u := service.AllService.UserService.CurUser(c)
t.UserId = u.Id
if t.CollectionId > 0 && !service.AllService.AddressBookService.CheckCollectionOwner(t.UserId, t.CollectionId) {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
return
}
ex := service.AllService.AddressBookService.InfoByUserIdAndIdAndCid(t.UserId, t.Id, t.CollectionId)
if ex.RowId > 0 {
response.Fail(c, 101, response.TranslateMsg(c, "ItemExists"))
return
}
err := service.AllService.AddressBookService.Create(t)
if err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
return
}
response.Success(c, nil)
}
// Update 编辑
// @Tags 我的地址簿
// @Summary 地址簿编辑
// @Description 地址簿编辑
// @Accept json
// @Produce json
// @Param body body admin.AddressBookForm true "地址簿信息"
// @Success 200 {object} response.Response{data=model.AddressBook}
// @Failure 500 {object} response.Response
// @Router /admin/my/address_book/update [post]
// @Security token
func (ct *AddressBook) Update(c *gin.Context) {
f := &admin.AddressBookForm{}
if err := c.ShouldBindJSON(f); err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
errList := global.Validator.ValidStruct(c, f)
if len(errList) > 0 {
response.Fail(c, 101, errList[0])
return
}
if f.RowId == 0 {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
return
}
u := service.AllService.UserService.CurUser(c)
if f.UserId != u.Id {
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
return
}
ex := service.AllService.AddressBookService.InfoByRowId(f.RowId)
if ex.RowId == 0 {
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
return
}
if ex.UserId != u.Id {
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
return
}
t := f.ToAddressBook()
if t.CollectionId > 0 && !service.AllService.AddressBookService.CheckCollectionOwner(t.UserId, t.CollectionId) {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
return
}
err := service.AllService.AddressBookService.UpdateAll(t)
if err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
return
}
response.Success(c, nil)
}
// Delete 删除
// @Tags 我的地址簿
// @Summary 地址簿删除
// @Description 地址簿删除
// @Accept json
// @Produce json
// @Param body body admin.AddressBookForm true "地址簿信息"
// @Success 200 {object} response.Response
// @Failure 500 {object} response.Response
// @Router /admin/my/address_book/delete [post]
// @Security token
func (ct *AddressBook) Delete(c *gin.Context) {
f := &admin.AddressBookForm{}
if err := c.ShouldBindJSON(f); err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
id := f.RowId
errList := global.Validator.ValidVar(c, id, "required,gt=0")
if len(errList) > 0 {
response.Fail(c, 101, errList[0])
return
}
ex := service.AllService.AddressBookService.InfoByRowId(f.RowId)
if ex.RowId == 0 {
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
return
}
u := service.AllService.UserService.CurUser(c)
if ex.UserId != u.Id {
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
return
}
err := service.AllService.AddressBookService.Delete(ex)
if err == nil {
response.Success(c, nil)
return
}
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
return
}
func (ct *AddressBook) BatchCreateFromPeers(c *gin.Context) {
f := &admin.BatchCreateFromPeersForm{}
if err := c.ShouldBindJSON(f); err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
u := service.AllService.UserService.CurUser(c)
if f.CollectionId != 0 {
collection := service.AllService.AddressBookService.CollectionInfoById(f.CollectionId)
if collection.Id == 0 {
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
return
}
if collection.UserId != u.Id {
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
return
}
}
if len(f.PeerIds) == 0 {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
return
}
pl := int64(len(f.PeerIds))
peers := service.AllService.PeerService.List(1, uint(pl), func(tx *gorm.DB) {
tx.Where("row_id in ?", f.PeerIds)
tx.Where("user_id = ?", u.Id)
})
if peers.Total == 0 || pl != peers.Total {
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
return
}
tags, _ := json.Marshal(f.Tags)
for _, peer := range peers.Peers {
ab := service.AllService.AddressBookService.FromPeer(peer)
ab.Tags = tags
ab.CollectionId = f.CollectionId
ex := service.AllService.AddressBookService.InfoByUserIdAndIdAndCid(u.Id, ab.Id, ab.CollectionId)
if ex.RowId != 0 {
continue
}
service.AllService.AddressBookService.Create(ab)
}
response.Success(c, nil)
}
func (ct *AddressBook) BatchUpdateTags(c *gin.Context) {
f := &admin.BatchUpdateTagsForm{}
if err := c.ShouldBindJSON(f); err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
u := service.AllService.UserService.CurUser(c)
abs := service.AllService.AddressBookService.List(1, 999, func(tx *gorm.DB) {
tx.Where("row_id in ?", f.RowIds)
tx.Where("user_id = ?", u.Id)
})
if abs.Total == 0 {
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
return
}
err := service.AllService.AddressBookService.BatchUpdateTags(abs.AddressBooks, f.Tags)
if err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
return
}
response.Success(c, nil)
}

View File

@@ -0,0 +1,162 @@
package my
import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/model"
"Gwen/service"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
type AddressBookCollection struct {
}
// Create 创建地址簿名称
// @Tags 我的地址簿名称
// @Summary 创建地址簿名称
// @Description 创建地址簿名称
// @Accept json
// @Produce json
// @Param body body model.AddressBookCollection true "地址簿名称信息"
// @Success 200 {object} response.Response{data=model.AddressBookCollection}
// @Failure 500 {object} response.Response
// @Router /admin/my/address_book_collection/create [post]
// @Security token
func (abc *AddressBookCollection) Create(c *gin.Context) {
f := &model.AddressBookCollection{}
if err := c.ShouldBindJSON(f); err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
errList := global.Validator.ValidStruct(c, f)
if len(errList) > 0 {
response.Fail(c, 101, errList[0])
return
}
u := service.AllService.UserService.CurUser(c)
f.UserId = u.Id
err := service.AllService.AddressBookService.CreateCollection(f)
if err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
return
}
response.Success(c, nil)
}
// List 列表
// @Tags 我的地址簿名称
// @Summary 地址簿名称列表
// @Description 地址簿名称列表
// @Accept json
// @Produce json
// @Param page query int false "页码"
// @Param page_size query int false "页大小"
// @Success 200 {object} response.Response{data=model.AddressBookCollectionList}
// @Failure 500 {object} response.Response
// @Router /admin/my/address_book_collection/list [get]
// @Security token
func (abc *AddressBookCollection) List(c *gin.Context) {
query := &admin.AddressBookCollectionQuery{}
if err := c.ShouldBindQuery(query); err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
u := service.AllService.UserService.CurUser(c)
query.UserId = int(u.Id)
res := service.AllService.AddressBookService.ListCollection(query.Page, query.PageSize, func(tx *gorm.DB) {
tx.Where("user_id = ?", query.UserId)
})
response.Success(c, res)
}
// Update 编辑
// @Tags 我的地址簿名称
// @Summary 地址簿名称编辑
// @Description 地址簿名称编辑
// @Accept json
// @Produce json
// @Param body body model.AddressBookCollection true "地址簿名称信息"
// @Success 200 {object} response.Response{data=model.AddressBookCollection}
// @Failure 500 {object} response.Response
// @Router /admin/my/address_book_collection/update [post]
// @Security token
func (abc *AddressBookCollection) Update(c *gin.Context) {
f := &model.AddressBookCollection{}
if err := c.ShouldBindJSON(f); err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
errList := global.Validator.ValidStruct(c, f)
if len(errList) > 0 {
response.Fail(c, 101, errList[0])
return
}
if f.Id == 0 {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
return
}
u := service.AllService.UserService.CurUser(c)
if f.UserId != u.Id {
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
return
}
ex := service.AllService.AddressBookService.CollectionInfoById(f.Id)
if ex.Id == 0 {
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
return
}
if ex.UserId != u.Id {
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
return
}
err := service.AllService.AddressBookService.UpdateCollection(f)
if err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
return
}
response.Success(c, nil)
}
// Delete 删除
// @Tags 我的地址簿名称
// @Summary 地址簿名称删除
// @Description 地址簿名称删除
// @Accept json
// @Produce json
// @Param body body model.AddressBookCollection true "地址簿名称信息"
// @Success 200 {object} response.Response
// @Failure 500 {object} response.Response
// @Router /admin/my/address_book_collection/delete [post]
// @Security token
func (abc *AddressBookCollection) Delete(c *gin.Context) {
f := &model.AddressBookCollection{}
if err := c.ShouldBindJSON(f); err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
id := f.Id
errList := global.Validator.ValidVar(c, id, "required,gt=0")
if len(errList) > 0 {
response.Fail(c, 101, errList[0])
return
}
ex := service.AllService.AddressBookService.CollectionInfoById(f.Id)
if ex.Id == 0 {
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
return
}
u := service.AllService.UserService.CurUser(c)
if ex.UserId != u.Id {
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
return
}
err := service.AllService.AddressBookService.DeleteCollection(ex)
if err == nil {
response.Success(c, nil)
return
}
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
}

View File

@@ -0,0 +1,228 @@
package my
import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/model"
"Gwen/service"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
type AddressBookCollectionRule struct {
}
// List 列表
// @Tags 我的地址簿规则
// @Summary 地址簿规则列表
// @Description 地址簿规则列表
// @Accept json
// @Produce json
// @Param page query int false "页码"
// @Param page_size query int false "页大小"
// @Param is_my query int false "是否是我的"
// @Param user_id query int false "用户id"
// @Param collection_id query int false "地址簿集合id"
// @Success 200 {object} response.Response{data=model.AddressBookCollectionList}
// @Failure 500 {object} response.Response
// @Router /admin/my/address_book_collection_rule/list [get]
// @Security token
func (abcr *AddressBookCollectionRule) List(c *gin.Context) {
query := &admin.AddressBookCollectionRuleQuery{}
if err := c.ShouldBindQuery(query); err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
u := service.AllService.UserService.CurUser(c)
query.UserId = int(u.Id)
res := service.AllService.AddressBookService.ListRules(query.Page, query.PageSize, func(tx *gorm.DB) {
tx.Where("user_id = ?", query.UserId)
if query.CollectionId > 0 {
tx.Where("collection_id = ?", query.CollectionId)
}
})
response.Success(c, res)
}
// Create 创建地址簿规则
// @Tags 我的地址簿规则
// @Summary 创建地址簿规则
// @Description 创建地址簿规则
// @Accept json
// @Produce json
// @Param body body model.AddressBookCollectionRule true "地址簿规则信息"
// @Success 200 {object} response.Response{data=model.AddressBookCollection}
// @Failure 500 {object} response.Response
// @Router /admin/my/address_book_collection_rule/create [post]
// @Security token
func (abcr *AddressBookCollectionRule) Create(c *gin.Context) {
f := &model.AddressBookCollectionRule{}
if err := c.ShouldBindJSON(f); err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
errList := global.Validator.ValidStruct(c, f)
if len(errList) > 0 {
response.Fail(c, 101, errList[0])
return
}
if f.Type != model.ShareAddressBookRuleTypePersonal && f.Type != model.ShareAddressBookRuleTypeGroup {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
return
}
//t := f.ToAddressBookCollection()
t := f
u := service.AllService.UserService.CurUser(c)
t.UserId = u.Id
msg, res := abcr.CheckForm(u, t)
if !res {
response.Fail(c, 101, response.TranslateMsg(c, msg))
return
}
err := service.AllService.AddressBookService.CreateRule(t)
if err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
return
}
response.Success(c, nil)
}
func (abcr *AddressBookCollectionRule) CheckForm(u *model.User, t *model.AddressBookCollectionRule) (string, bool) {
if t.UserId != u.Id {
return "NoAccess", false
}
if t.CollectionId > 0 && !service.AllService.AddressBookService.CheckCollectionOwner(t.UserId, t.CollectionId) {
return "ParamsError", false
}
//check to_id
if t.Type == model.ShareAddressBookRuleTypePersonal {
if t.ToId == t.UserId {
return "ParamsError", false
}
tou := service.AllService.UserService.InfoById(t.ToId)
if tou.Id == 0 {
return "ItemNotFound", false
}
//非管理员不能分享给非本组织用户
if tou.GroupId != u.GroupId {
return "NoAccess", false
}
} else if t.Type == model.ShareAddressBookRuleTypeGroup {
//非管理员不能分享给其他组
if t.ToId != u.GroupId {
return "NoAccess", false
}
tog := service.AllService.GroupService.InfoById(t.ToId)
if tog.Id == 0 {
return "ItemNotFound", false
}
} else {
return "ParamsError", false
}
// 重复检查
ex := service.AllService.AddressBookService.RulePersonalInfoByToIdAndCid(t.ToId, t.CollectionId)
if t.Id == 0 && ex.Id > 0 {
return "ItemExists", false
}
if t.Id > 0 && ex.Id > 0 && t.Id != ex.Id {
return "ItemExists", false
}
return "", true
}
// Update 编辑
// @Tags 我的地址簿规则
// @Summary 地址簿规则编辑
// @Description 地址簿规则编辑
// @Accept json
// @Produce json
// @Param body body model.AddressBookCollectionRule true "地址簿规则信息"
// @Success 200 {object} response.Response{data=model.AddressBookCollection}
// @Failure 500 {object} response.Response
// @Router /admin/my/address_book_collection_rule/update [post]
// @Security token
func (abcr *AddressBookCollectionRule) Update(c *gin.Context) {
f := &model.AddressBookCollectionRule{}
if err := c.ShouldBindJSON(f); err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
errList := global.Validator.ValidStruct(c, f)
if len(errList) > 0 {
response.Fail(c, 101, errList[0])
return
}
if f.Id == 0 {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
return
}
u := service.AllService.UserService.CurUser(c)
ex := service.AllService.AddressBookService.RuleInfoById(f.Id)
if ex.Id == 0 {
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
return
}
if ex.UserId != u.Id {
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
return
}
t := f
msg, res := abcr.CheckForm(u, t)
if !res {
response.Fail(c, 101, response.TranslateMsg(c, msg))
return
}
err := service.AllService.AddressBookService.UpdateRule(t)
if err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
return
}
response.Success(c, nil)
}
// Delete 删除
// @Tags 我的地址簿规则
// @Summary 地址簿规则删除
// @Description 地址簿规则删除
// @Accept json
// @Produce json
// @Param body body model.AddressBookCollectionRule true "地址簿规则信息"
// @Success 200 {object} response.Response
// @Failure 500 {object} response.Response
// @Router /admin/my/address_book_collection_rule/delete [post]
// @Security token
func (abcr *AddressBookCollectionRule) Delete(c *gin.Context) {
f := &model.AddressBookCollectionRule{}
if err := c.ShouldBindJSON(f); err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
id := f.Id
errList := global.Validator.ValidVar(c, id, "required,gt=0")
if len(errList) > 0 {
response.Fail(c, 101, errList[0])
return
}
ex := service.AllService.AddressBookService.RuleInfoById(f.Id)
if ex.Id == 0 {
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
return
}
u := service.AllService.UserService.CurUser(c)
if ex.UserId != u.Id {
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
return
}
err := service.AllService.AddressBookService.DeleteRule(ex)
if err == nil {
response.Success(c, nil)
return
}
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
}

View File

@@ -0,0 +1,59 @@
package my
import (
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/service"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
"time"
)
type Peer struct {
}
// List 列表
// @Tags 我的设备
// @Summary 设备列表
// @Description 设备列表
// @Accept json
// @Produce json
// @Param page query int false "页码"
// @Param page_size query int false "页大小"
// @Param time_ago query int false "时间"
// @Param id query string false "ID"
// @Param hostname query string false "主机名"
// @Param uuids query string false "uuids 用逗号分隔"
// @Success 200 {object} response.Response{data=model.PeerList}
// @Failure 500 {object} response.Response
// @Router /admin/my/peer/list [get]
// @Security token
func (ct *Peer) List(c *gin.Context) {
query := &admin.PeerQuery{}
if err := c.ShouldBindQuery(query); err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
u := service.AllService.UserService.CurUser(c)
res := service.AllService.PeerService.List(query.Page, query.PageSize, func(tx *gorm.DB) {
tx.Where("user_id = ?", u.Id)
if query.TimeAgo > 0 {
lt := time.Now().Unix() - int64(query.TimeAgo)
tx.Where("last_online_time < ?", lt)
}
if query.TimeAgo < 0 {
lt := time.Now().Unix() + int64(query.TimeAgo)
tx.Where("last_online_time > ?", lt)
}
if query.Id != "" {
tx.Where("id like ?", "%"+query.Id+"%")
}
if query.Hostname != "" {
tx.Where("hostname like ?", "%"+query.Hostname+"%")
}
if query.Uuids != "" {
tx.Where("uuid in (?)", query.Uuids)
}
})
response.Success(c, res)
}

View File

@@ -0,0 +1,119 @@
package my
import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/service"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
type ShareRecord struct {
}
// List 分享记录列表
// @Tags 我的分享记录
// @Summary 分享记录列表
// @Description 分享记录列表
// @Accept json
// @Produce json
// @Param page query int false "页码"
// @Param page_size query int false "页大小"
// @Success 200 {object} response.Response
// @Failure 500 {object} response.Response
// @Router /admin/my/share_record/list [get]
// @Security token
func (sr *ShareRecord) List(c *gin.Context) {
query := &admin.PageQuery{}
if err := c.ShouldBindQuery(query); err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
u := service.AllService.UserService.CurUser(c)
res := service.AllService.ShareRecordService.List(query.Page, query.PageSize, func(tx *gorm.DB) {
tx.Where("user_id = ?", u.Id)
})
response.Success(c, res)
}
// Delete 分享记录删除
// @Tags 我的分享记录
// @Summary 分享记录删除
// @Description 分享记录删除
// @Accept json
// @Produce json
// @Param body body admin.ShareRecordForm true "分享记录信息"
// @Success 200 {object} response.Response
// @Failure 500 {object} response.Response
// @Router /admin/my/share_record/delete [post]
// @Security token
func (sr *ShareRecord) Delete(c *gin.Context) {
f := &admin.ShareRecordForm{}
if err := c.ShouldBindJSON(f); err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
id := f.Id
errList := global.Validator.ValidVar(c, id, "required,gt=0")
if len(errList) > 0 {
response.Fail(c, 101, errList[0])
return
}
u := service.AllService.UserService.CurUser(c)
i := service.AllService.ShareRecordService.InfoById(f.Id)
if i.UserId != u.Id {
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
return
}
if i.Id == 0 {
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
return
}
err := service.AllService.ShareRecordService.Delete(i)
if err == nil {
response.Success(c, nil)
return
}
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
}
// BatchDelete 批量删除我的分享记录
// @Tags 我的
// @Summary 批量删除我的分享记录
// @Description 批量删除我的分享记录
// @Accept json
// @Produce json
// @Param body body admin.PeerShareRecordBatchDeleteForm true "id"
// @Success 200 {object} response.Response
// @Failure 500 {object} response.Response
// @Router /admin/my/share_record/batchDelete [post]
// @Security token
func (sr *ShareRecord) BatchDelete(c *gin.Context) {
f := &admin.PeerShareRecordBatchDeleteForm{}
if err := c.ShouldBindJSON(f); err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
if len(f.Ids) == 0 {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
return
}
u := service.AllService.UserService.CurUser(c)
var l int64
l = int64(len(f.Ids))
res := service.AllService.ShareRecordService.List(1, uint(l), func(tx *gorm.DB) {
tx.Where("user_id = ?", u.Id)
tx.Where("id in ?", f.Ids)
})
if res.Total != l {
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
return
}
err := service.AllService.ShareRecordService.BatchDelete(f.Ids)
if err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
return
}
response.Success(c, nil)
}

View File

@@ -0,0 +1,176 @@
package my
import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/service"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
type Tag struct{}
// List 列表
// @Tags 我的标签
// @Summary 标签列表
// @Description 标签列表
// @Accept json
// @Produce json
// @Param page query int false "页码"
// @Param page_size query int false "页大小"
// @Param is_my query int false "是否是我的"
// @Param user_id query int false "用户id"
// @Success 200 {object} response.Response{data=model.TagList}
// @Failure 500 {object} response.Response
// @Router /admin/my/tag/list [get]
// @Security token
func (ct *Tag) List(c *gin.Context) {
query := &admin.TagQuery{}
if err := c.ShouldBindQuery(query); err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
u := service.AllService.UserService.CurUser(c)
query.UserId = int(u.Id)
res := service.AllService.TagService.List(query.Page, query.PageSize, func(tx *gorm.DB) {
tx.Preload("Collection", func(txc *gorm.DB) *gorm.DB {
return txc.Select("id,name")
})
tx.Where("user_id = ?", query.UserId)
if query.CollectionId != nil && *query.CollectionId >= 0 {
tx.Where("collection_id = ?", query.CollectionId)
}
})
response.Success(c, res)
}
// Create 创建标签
// @Tags 我的标签
// @Summary 创建标签
// @Description 创建标签
// @Accept json
// @Produce json
// @Param body body admin.TagForm true "标签信息"
// @Success 200 {object} response.Response{data=model.Tag}
// @Failure 500 {object} response.Response
// @Router /admin/my/tag/create [post]
// @Security token
func (ct *Tag) Create(c *gin.Context) {
f := &admin.TagForm{}
if err := c.ShouldBindJSON(f); err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
errList := global.Validator.ValidStruct(c, f)
if len(errList) > 0 {
response.Fail(c, 101, errList[0])
return
}
t := f.ToTag()
u := service.AllService.UserService.CurUser(c)
t.UserId = u.Id
err := service.AllService.TagService.Create(t)
if err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
return
}
response.Success(c, nil)
}
// Update 编辑
// @Tags 我的标签
// @Summary 标签编辑
// @Description 标签编辑
// @Accept json
// @Produce json
// @Param body body admin.TagForm true "标签信息"
// @Success 200 {object} response.Response{data=model.Tag}
// @Failure 500 {object} response.Response
// @Router /admin/my/tag/update [post]
// @Security token
func (ct *Tag) Update(c *gin.Context) {
f := &admin.TagForm{}
if err := c.ShouldBindJSON(f); err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
errList := global.Validator.ValidStruct(c, f)
if len(errList) > 0 {
response.Fail(c, 101, errList[0])
return
}
if f.Id == 0 {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
return
}
u := service.AllService.UserService.CurUser(c)
if f.UserId != u.Id {
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
return
}
ex := service.AllService.TagService.InfoById(f.Id)
if ex.Id == 0 {
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
return
}
if ex.UserId != u.Id {
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
return
}
t := f.ToTag()
if t.CollectionId > 0 && !service.AllService.AddressBookService.CheckCollectionOwner(t.UserId, t.CollectionId) {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
return
}
err := service.AllService.TagService.Update(t)
if err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
return
}
response.Success(c, nil)
}
// Delete 删除
// @Tags 标签
// @Summary 标签删除
// @Description 标签删除
// @Accept json
// @Produce json
// @Param body body admin.TagForm true "标签信息"
// @Success 200 {object} response.Response
// @Failure 500 {object} response.Response
// @Router /admin/my/tag/delete [post]
// @Security token
func (ct *Tag) Delete(c *gin.Context) {
f := &admin.TagForm{}
if err := c.ShouldBindJSON(f); err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
id := f.Id
errList := global.Validator.ValidVar(c, id, "required,gt=0")
if len(errList) > 0 {
response.Fail(c, 101, errList[0])
return
}
ex := service.AllService.TagService.InfoById(f.Id)
if ex.Id == 0 {
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
return
}
u := service.AllService.UserService.CurUser(c)
if ex.UserId != u.Id {
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
return
}
err := service.AllService.TagService.Delete(ex)
if err == nil {
response.Success(c, nil)
return
}
response.Fail(c, 101, err.Error())
return
}

View File

@@ -0,0 +1,105 @@
package admin
import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/service"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
type ShareRecord struct {
}
// List 列表
// @Tags 分享记录
// @Summary 分享记录列表
// @Description 分享记录列表
// @Accept json
// @Produce json
// @Param user_id query int false "用户ID"
// @Param page query int false "页码"
// @Param page_size query int false "页大小"
// @Success 200 {object} response.Response
// @Failure 500 {object} response.Response
// @Router /admin/share_record/list [get]
// @Security token
func (sr *ShareRecord) List(c *gin.Context) {
query := &admin.ShareRecordQuery{}
if err := c.ShouldBindQuery(query); err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
res := service.AllService.ShareRecordService.List(query.Page, query.PageSize, func(tx *gorm.DB) {
if query.UserId > 0 {
tx.Where("user_id = ?", query.UserId)
}
})
response.Success(c, res)
}
// Delete 删除
// @Tags 分享记录
// @Summary 分享记录删除
// @Description 分享记录删除
// @Accept json
// @Produce json
// @Param body body admin.ShareRecordForm true "分享记录信息"
// @Success 200 {object} response.Response
// @Failure 500 {object} response.Response
// @Router /admin/share_record/delete [post]
// @Security token
func (sr *ShareRecord) Delete(c *gin.Context) {
f := &admin.ShareRecordForm{}
if err := c.ShouldBindJSON(f); err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
id := f.Id
errList := global.Validator.ValidVar(c, id, "required,gt=0")
if len(errList) > 0 {
response.Fail(c, 101, errList[0])
return
}
i := service.AllService.ShareRecordService.InfoById(f.Id)
if i.Id > 0 {
err := service.AllService.ShareRecordService.Delete(i)
if err == nil {
response.Success(c, nil)
return
}
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
return
}
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
}
// BatchDelete 批量删除
// @Tags 分享记录
// @Summary 批量分享记录
// @Description 批量分享记录
// @Accept json
// @Produce json
// @Param body body admin.PeerShareRecordBatchDeleteForm true "id"
// @Success 200 {object} response.Response
// @Failure 500 {object} response.Response
// @Router /admin/share_record/batchDelete [post]
// @Security token
func (sr *ShareRecord) BatchDelete(c *gin.Context) {
f := &admin.PeerShareRecordBatchDeleteForm{}
if err := c.ShouldBindJSON(f); err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
if len(f.Ids) == 0 {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
return
}
err := service.AllService.ShareRecordService.BatchDelete(f.Ids)
if err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
return
}
response.Success(c, nil)
}

View File

@@ -64,9 +64,9 @@ func (ct *Tag) Create(c *gin.Context) {
return return
} }
t := f.ToTag() t := f.ToTag()
u := service.AllService.UserService.CurUser(c) if t.UserId == 0 {
if !service.AllService.UserService.IsAdmin(u) || t.UserId == 0 { response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
t.UserId = u.Id return
} }
err := service.AllService.TagService.Create(t) err := service.AllService.TagService.Create(t)
if err != nil { if err != nil {
@@ -96,10 +96,6 @@ func (ct *Tag) List(c *gin.Context) {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error()) response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return return
} }
u := service.AllService.UserService.CurUser(c)
if !service.AllService.UserService.IsAdmin(u) || query.IsMy == 1 {
query.UserId = int(u.Id)
}
res := service.AllService.TagService.List(query.Page, query.PageSize, func(tx *gorm.DB) { res := service.AllService.TagService.List(query.Page, query.PageSize, func(tx *gorm.DB) {
tx.Preload("Collection", func(txc *gorm.DB) *gorm.DB { tx.Preload("Collection", func(txc *gorm.DB) *gorm.DB {
return txc.Select("id,name") return txc.Select("id,name")
@@ -140,12 +136,12 @@ func (ct *Tag) Update(c *gin.Context) {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")) response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
return return
} }
t := f.ToTag() ex := service.AllService.TagService.InfoById(f.Id)
u := service.AllService.UserService.CurUser(c) if ex.Id == 0 {
if !service.AllService.UserService.IsAdmin(u) && t.UserId != u.Id { response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
return return
} }
t := f.ToTag()
err := service.AllService.TagService.Update(t) err := service.AllService.TagService.Update(t)
if err != nil { if err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error()) response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
@@ -177,20 +173,15 @@ func (ct *Tag) Delete(c *gin.Context) {
response.Fail(c, 101, errList[0]) response.Fail(c, 101, errList[0])
return return
} }
t := service.AllService.TagService.InfoById(f.Id) ex := service.AllService.TagService.InfoById(f.Id)
u := service.AllService.UserService.CurUser(c) if ex.Id == 0 {
if !service.AllService.UserService.IsAdmin(u) && t.UserId != u.Id { response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
return return
} }
if u.Id > 0 { err := service.AllService.TagService.Delete(ex)
err := service.AllService.TagService.Delete(t) if err == nil {
if err == nil { response.Success(c, nil)
response.Success(c, nil)
return
}
response.Fail(c, 101, err.Error())
return return
} }
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound")) response.Fail(c, 101, err.Error())
} }

View File

@@ -10,7 +10,6 @@ import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"gorm.io/gorm" "gorm.io/gorm"
"strconv" "strconv"
"time"
) )
type User struct { type User struct {
@@ -295,51 +294,6 @@ func (ct *User) MyOauth(c *gin.Context) {
response.Success(c, res) response.Success(c, res)
} }
// MyPeer 列表
// @Tags 设备
// @Summary 我的设备列表
// @Description 我的设备列表
// @Accept json
// @Produce json
// @Param page query int false "页码"
// @Param page_size query int false "页大小"
// @Param time_ago query int false "时间"
// @Param id query string false "ID"
// @Param hostname query string false "主机名"
// @Param uuids query string false "uuids 用逗号分隔"
// @Success 200 {object} response.Response{data=model.PeerList}
// @Failure 500 {object} response.Response
// @Router /admin/user/myPeer [get]
// @Security token
func (ct *User) MyPeer(c *gin.Context) {
query := &admin.PeerQuery{}
if err := c.ShouldBindQuery(query); err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
u := service.AllService.UserService.CurUser(c)
res := service.AllService.PeerService.ListFilterByUserId(query.Page, query.PageSize, func(tx *gorm.DB) {
if query.TimeAgo > 0 {
lt := time.Now().Unix() - int64(query.TimeAgo)
tx.Where("last_online_time < ?", lt)
}
if query.TimeAgo < 0 {
lt := time.Now().Unix() + int64(query.TimeAgo)
tx.Where("last_online_time > ?", lt)
}
if query.Id != "" {
tx.Where("id like ?", "%"+query.Id+"%")
}
if query.Hostname != "" {
tx.Where("hostname like ?", "%"+query.Hostname+"%")
}
if query.Uuids != "" {
tx.Where("uuid in (?)", query.Uuids)
}
}, u.Id)
response.Success(c, res)
}
// groupUsers // groupUsers
func (ct *User) GroupUsers(c *gin.Context) { func (ct *User) GroupUsers(c *gin.Context) {
q := &admin.GroupUsersQuery{} q := &admin.GroupUsersQuery{}

View File

@@ -91,7 +91,7 @@ func (ct *UserToken) Delete(c *gin.Context) {
// @Param body body admin.UserTokenBatchDeleteForm true "登录凭证信息" // @Param body body admin.UserTokenBatchDeleteForm true "登录凭证信息"
// @Success 200 {object} response.Response // @Success 200 {object} response.Response
// @Failure 500 {object} response.Response // @Failure 500 {object} response.Response
// @Router /admin/user_token/delete [post] // @Router /admin/user_token/batchDelete [post]
// @Security token // @Security token
func (ct *UserToken) BatchDelete(c *gin.Context) { func (ct *UserToken) BatchDelete(c *gin.Context) {
f := &admin.UserTokenBatchDeleteForm{} f := &admin.UserTokenBatchDeleteForm{}

View File

@@ -127,6 +127,7 @@ type BatchCreateFromPeersForm struct {
CollectionId uint `json:"collection_id"` CollectionId uint `json:"collection_id"`
PeerIds []uint `json:"peer_ids"` PeerIds []uint `json:"peer_ids"`
Tags []string `json:"tags"` Tags []string `json:"tags"`
UserId uint `json:"user_id"`
} }
type BatchUpdateTagsForm struct { type BatchUpdateTagsForm struct {
RowIds []uint `json:"row_ids"` RowIds []uint `json:"row_ids"`

View File

@@ -4,6 +4,7 @@ type Login struct {
Username string `json:"username" validate:"required" label:"用户名"` Username string `json:"username" validate:"required" label:"用户名"`
Password string `json:"password,omitempty" validate:"required" label:"密码"` Password string `json:"password,omitempty" validate:"required" label:"密码"`
Platform string `json:"platform" label:"平台"` Platform string `json:"platform" label:"平台"`
Captcha string `json:"captcha,omitempty" label:"验证码"`
} }
type LoginLogQuery struct { type LoginLogQuery struct {

View File

@@ -0,0 +1,15 @@
package admin
type ShareRecordQuery struct {
UserId uint `json:"user_id" form:"user_id"`
PageQuery
}
type ShareRecordForm struct {
Id uint `json:"id" form:"id"`
UserId uint `json:"user_id" form:"user_id"`
}
type PeerShareRecordBatchDeleteForm struct {
Ids []uint `json:"ids" validate:"required"`
}

View File

@@ -4,8 +4,8 @@ import "Gwen/model"
type LoginPayload struct { type LoginPayload struct {
Username string `json:"username"` Username string `json:"username"`
Email string `json:"email"` Email string `json:"email"`
Avatar string `json:"avatar"` Avatar string `json:"avatar"`
Token string `json:"token"` Token string `json:"token"`
RouteNames []string `json:"route_names"` RouteNames []string `json:"route_names"`
Nickname string `json:"nickname"` Nickname string `json:"nickname"`
@@ -19,13 +19,13 @@ func (lp *LoginPayload) FromUser(user *model.User) {
} }
var UserRouteNames = []string{ var UserRouteNames = []string{
"MyTagList", "MyAddressBookList", "MyInfo", "MyAddressBookCollection", "MyPeer", "MyTagList", "MyAddressBookList", "MyInfo", "MyAddressBookCollection", "MyPeer", "MyShareRecordList",
} }
var AdminRouteNames = []string{"*"} var AdminRouteNames = []string{"*"}
type UserOauthItem struct { type UserOauthItem struct {
Op string `json:"op"` Op string `json:"op"`
Status int `json:"status"` Status int `json:"status"`
} }
type GroupUsersPayload struct { type GroupUsersPayload struct {

View File

@@ -2,7 +2,9 @@ package router
import ( import (
_ "Gwen/docs/admin" _ "Gwen/docs/admin"
"Gwen/global"
"Gwen/http/controller/admin" "Gwen/http/controller/admin"
"Gwen/http/controller/admin/my"
"Gwen/http/middleware" "Gwen/http/middleware"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
swaggerFiles "github.com/swaggo/files" swaggerFiles "github.com/swaggo/files"
@@ -13,7 +15,9 @@ func Init(g *gin.Engine) {
//swagger //swagger
//g.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) //g.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
g.GET("/admin/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler, ginSwagger.InstanceName("admin"))) if global.Config.App.ShowSwagger == 1 {
g.GET("/admin/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler, ginSwagger.InstanceName("admin")))
}
adg := g.Group("/api/admin") adg := g.Group("/api/admin")
LoginBind(adg) LoginBind(adg)
@@ -34,17 +38,21 @@ func Init(g *gin.Engine) {
ConfigBind(adg) ConfigBind(adg)
//deprecated by ConfigBind //deprecated by ConfigBind
rs := &admin.Rustdesk{} //rs := &admin.Rustdesk{}
adg.GET("/server-config", rs.ServerConfig) //adg.GET("/server-config", rs.ServerConfig)
adg.GET("/app-config", rs.AppConfig) //adg.GET("/app-config", rs.AppConfig)
//deprecated end //deprecated end
ShareRecordBind(adg)
MyBind(adg)
//访问静态文件 //访问静态文件
//g.StaticFS("/upload", http.Dir(global.Config.Gin.ResourcesPath+"/upload")) //g.StaticFS("/upload", http.Dir(global.Config.Gin.ResourcesPath+"/upload"))
} }
func LoginBind(rg *gin.RouterGroup) { func LoginBind(rg *gin.RouterGroup) {
cont := &admin.Login{} cont := &admin.Login{}
rg.POST("/login", cont.Login) rg.POST("/login", cont.Login)
rg.GET("/captcha", cont.Captcha)
rg.POST("/logout", cont.Logout) rg.POST("/logout", cont.Logout)
rg.GET("/login-options", cont.LoginOptions) rg.GET("/login-options", cont.LoginOptions)
rg.POST("/oidc/auth", cont.OidcAuth) rg.POST("/oidc/auth", cont.OidcAuth)
@@ -58,7 +66,7 @@ func UserBind(rg *gin.RouterGroup) {
aR.GET("/current", cont.Current) aR.GET("/current", cont.Current)
aR.POST("/changeCurPwd", cont.ChangeCurPwd) aR.POST("/changeCurPwd", cont.ChangeCurPwd)
aR.POST("/myOauth", cont.MyOauth) aR.POST("/myOauth", cont.MyOauth)
aR.GET("/myPeer", cont.MyPeer) //aR.GET("/myPeer", cont.MyPeer)
aR.POST("/groupUsers", cont.GroupUsers) aR.POST("/groupUsers", cont.GroupUsers)
} }
aRP := rg.Group("/user").Use(middleware.AdminPrivilege()) aRP := rg.Group("/user").Use(middleware.AdminPrivilege())
@@ -86,7 +94,7 @@ func GroupBind(rg *gin.RouterGroup) {
} }
func TagBind(rg *gin.RouterGroup) { func TagBind(rg *gin.RouterGroup) {
aR := rg.Group("/tag") aR := rg.Group("/tag").Use(middleware.AdminPrivilege())
{ {
cont := &admin.Tag{} cont := &admin.Tag{}
aR.GET("/list", cont.List) aR.GET("/list", cont.List)
@@ -101,17 +109,16 @@ func AddressBookBind(rg *gin.RouterGroup) {
aR := rg.Group("/address_book") aR := rg.Group("/address_book")
{ {
cont := &admin.AddressBook{} cont := &admin.AddressBook{}
aR.GET("/list", cont.List)
aR.GET("/detail/:id", cont.Detail)
aR.POST("/create", cont.Create)
aR.POST("/update", cont.Update)
aR.POST("/delete", cont.Delete)
aR.POST("/shareByWebClient", cont.ShareByWebClient) aR.POST("/shareByWebClient", cont.ShareByWebClient)
aR.POST("/batchCreateFromPeers", cont.BatchCreateFromPeers)
aR.POST("/batchUpdateTags", cont.BatchUpdateTags)
arp := aR.Use(middleware.AdminPrivilege()) arp := aR.Use(middleware.AdminPrivilege())
arp.GET("/list", cont.List)
//arp.GET("/detail/:id", cont.Detail)
arp.POST("/create", cont.Create)
arp.POST("/update", cont.Update)
arp.POST("/delete", cont.Delete)
arp.POST("/batchCreate", cont.BatchCreate) arp.POST("/batchCreate", cont.BatchCreate)
arp.POST("/batchCreateFromPeers", cont.BatchCreateFromPeers)
} }
} }
@@ -171,7 +178,7 @@ func AuditBind(rg *gin.RouterGroup) {
afR.POST("/batchDelete", cont.BatchFileDelete) afR.POST("/batchDelete", cont.BatchFileDelete)
} }
func AddressBookCollectionBind(rg *gin.RouterGroup) { func AddressBookCollectionBind(rg *gin.RouterGroup) {
aR := rg.Group("/address_book_collection") aR := rg.Group("/address_book_collection").Use(middleware.AdminPrivilege())
{ {
cont := &admin.AddressBookCollection{} cont := &admin.AddressBookCollection{}
aR.GET("/list", cont.List) aR.GET("/list", cont.List)
@@ -183,7 +190,7 @@ func AddressBookCollectionBind(rg *gin.RouterGroup) {
} }
func AddressBookCollectionRuleBind(rg *gin.RouterGroup) { func AddressBookCollectionRuleBind(rg *gin.RouterGroup) {
aR := rg.Group("/address_book_collection_rule") aR := rg.Group("/address_book_collection_rule").Use(middleware.AdminPrivilege())
{ {
cont := &admin.AddressBookCollectionRule{} cont := &admin.AddressBookCollectionRule{}
aR.GET("/list", cont.List) aR.GET("/list", cont.List)
@@ -220,3 +227,62 @@ func FileBind(rg *gin.RouterGroup) {
aR.POST("/upload", cont.Upload) aR.POST("/upload", cont.Upload)
} }
}*/ }*/
func MyBind(rg *gin.RouterGroup) {
{
cont := &my.ShareRecord{}
rg.GET("/my/share_record/list", cont.List)
rg.POST("/my/share_record/delete", cont.Delete)
rg.POST("/my/share_record/batchDelete", cont.BatchDelete)
}
{
cont := &my.AddressBook{}
rg.GET("/my/address_book/list", cont.List)
rg.POST("/my/address_book/create", cont.Create)
rg.POST("/my/address_book/update", cont.Update)
rg.POST("/my/address_book/delete", cont.Delete)
rg.POST("/my/address_book/batchCreateFromPeers", cont.BatchCreateFromPeers)
rg.POST("/my/address_book/batchUpdateTags", cont.BatchUpdateTags)
}
{
cont := &my.Tag{}
rg.GET("/my/tag/list", cont.List)
rg.POST("/my/tag/create", cont.Create)
rg.POST("/my/tag/update", cont.Update)
rg.POST("/my/tag/delete", cont.Delete)
}
{
cont := &my.AddressBookCollection{}
rg.GET("/my/address_book_collection/list", cont.List)
rg.POST("/my/address_book_collection/create", cont.Create)
rg.POST("/my/address_book_collection/update", cont.Update)
rg.POST("/my/address_book_collection/delete", cont.Delete)
}
{
cont := &my.AddressBookCollectionRule{}
rg.GET("/my/address_book_collection_rule/list", cont.List)
rg.POST("/my/address_book_collection_rule/create", cont.Create)
rg.POST("/my/address_book_collection_rule/update", cont.Update)
rg.POST("/my/address_book_collection_rule/delete", cont.Delete)
}
{
cont := &my.Peer{}
rg.GET("/my/peer/list", cont.List)
}
}
func ShareRecordBind(rg *gin.RouterGroup) {
aR := rg.Group("/share_record").Use(middleware.AdminPrivilege())
{
cont := &admin.ShareRecord{}
aR.GET("/list", cont.List)
aR.POST("/delete", cont.Delete)
aR.POST("/batchDelete", cont.BatchDelete)
}
}

View File

@@ -15,7 +15,9 @@ func ApiInit(g *gin.Engine) {
//g.Use(middleware.Cors()) //g.Use(middleware.Cors())
//swagger //swagger
g.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler, ginSwagger.InstanceName("api"))) if global.Config.App.ShowSwagger == 1 {
g.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler, ginSwagger.InstanceName("api")))
}
frg := g.Group("/api") frg := g.Group("/api")

View File

@@ -10,3 +10,9 @@ type ShareRecord struct {
Expire int64 `json:"expire" gorm:"default:0;not null;"` Expire int64 `json:"expire" gorm:"default:0;not null;"`
TimeModel TimeModel
} }
// ShareRecordList 分享记录列表
type ShareRecordList struct {
ShareRecords []*ShareRecord `json:"list,omitempty"`
Pagination
}

View File

@@ -123,3 +123,13 @@ other = "Share Group"
description = "Register closed." description = "Register closed."
one = "Register closed." one = "Register closed."
other = "Register closed." other = "Register closed."
[CaptchaRequired]
description = "Captcha required."
one = "Captcha required."
other = "Captcha required."
[CaptchaError]
description = "Captcha error."
one = "Captcha error."
other = "Captcha error."

View File

@@ -132,3 +132,13 @@ other = "Grupo compartido"
description = "Register closed." description = "Register closed."
one = "Registro cerrado." one = "Registro cerrado."
other = "Registro cerrado." other = "Registro cerrado."
[CaptchaRequired]
description = "Captcha required."
one = "Captcha requerido."
other = "Captcha requerido."
[CaptchaError]
description = "Captcha error."
one = "Error de captcha."
other = "Error de captcha."

View File

@@ -125,4 +125,14 @@ other = "공유 그룹"
[RegisterClosed] [RegisterClosed]
description = "Register closed." description = "Register closed."
one = "가입이 종료되었습니다." one = "가입이 종료되었습니다."
other = "가입이 종료되었습니다." other = "가입이 종료되었습니다."
[CaptchaRequired]
description = "Captcha required."
one = "Captcha가 필요합니다."
other = "Captcha가 필요합니다."
[CaptchaError]
description = "Captcha error."
one = "Captcha 오류."
other = "Captcha 오류."

View File

@@ -131,4 +131,14 @@ other = "Общая группа"
[RegisterClosed] [RegisterClosed]
description = "Register closed." description = "Register closed."
one = "Регистрация закрыта." one = "Регистрация закрыта."
other = "Регистрация закрыта." other = "Регистрация закрыта."
[CaptchaRequired]
description = "Captcha required."
one = "Требуется капча."
other = "Требуется капча."
[CaptchaError]
description = "Captcha error."
one = "Ошибка капчи."
other = "Ошибка капчи."

View File

@@ -124,4 +124,14 @@ other = "共享组"
[RegisterClosed] [RegisterClosed]
description = "Register closed." description = "Register closed."
one = "注册已关闭。" one = "注册已关闭。"
other = "注册已关闭。" other = "注册已关闭。"
[CaptchaRequired]
description = "Captcha required."
one = "需要验证码。"
other = "需要验证码。"
[CaptchaError]
description = "Captcha error."
one = "验证码错误。"
other = "验证码错误。"

View File

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

49
service/shareRecord.go Normal file
View File

@@ -0,0 +1,49 @@
package service
import (
"Gwen/global"
"Gwen/model"
"gorm.io/gorm"
)
type ShareRecordService struct {
}
// InfoById 根据用户id取用户信息
func (srs *ShareRecordService) InfoById(id uint) *model.ShareRecord {
u := &model.ShareRecord{}
global.DB.Where("id = ?", id).First(u)
return u
}
func (srs *ShareRecordService) List(page, pageSize uint, where func(tx *gorm.DB)) (res *model.ShareRecordList) {
res = &model.ShareRecordList{}
res.Page = int64(page)
res.PageSize = int64(pageSize)
tx := global.DB.Model(&model.ShareRecord{})
if where != nil {
where(tx)
}
tx.Count(&res.Total)
tx.Scopes(Paginate(page, pageSize))
tx.Find(&res.ShareRecords)
return
}
// Create 创建
func (srs *ShareRecordService) Create(u *model.ShareRecord) error {
res := global.DB.Create(u).Error
return res
}
func (srs *ShareRecordService) Delete(u *model.ShareRecord) error {
return global.DB.Delete(u).Error
}
// Update 更新
func (srs *ShareRecordService) Update(u *model.ShareRecord) error {
return global.DB.Model(u).Updates(u).Error
}
func (srs *ShareRecordService) BatchDelete(ids []uint) error {
return global.DB.Where("id in (?)", ids).Delete(&model.ShareRecord{}).Error
}