Compare commits

...

20 Commits

Author SHA1 Message Date
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
lejianwen
bd13fe4ef4 up web client v2 2024-12-10 18:26:08 +08:00
lejianwen
6e1b208464 up README.md 2024-12-10 15:28:05 +08:00
lejianwen
76433a409e up README.md 2024-12-09 13:43:04 +08:00
lejianwen
9b4fa679c2 add batch add ab from peer
add batch update ab tags
2024-12-06 19:45:41 +08:00
lejianwen
c2ae95c4cc up api docs 2024-12-06 10:36:40 +08:00
lejianwen
b2b7f60fd5 add batch delete user token 2024-12-06 10:36:27 +08:00
lejianwen
a465888b31 up username length to 32 #70 2024-12-06 10:22:28 +08:00
lejianwen
d368bdc84c up web client v2 2024-12-04 13:43:04 +08:00
lejianwen
cdc1150505 up readme 2024-11-28 12:42:15 +08:00
32d525c53c Create LICENSE 2024-11-26 17:29:46 +08:00
lejianwen
a89b40c607 add es lang 2024-11-26 10:43:01 +08:00
lejianwen
b6bd9150d9 up web client v2 from rustdesk 2024-11-22 19:54:19 +08:00
lejianwen
96e3e3bc86 up docs 2024-11-22 19:53:49 +08:00
lejianwen
41377f41bb Split the language 2024-11-22 19:39:28 +08:00
lejianwen
fb744f81e2 up img 2024-11-20 19:33:27 +08:00
lejianwen
750c3bcbcd fix #62 2024-11-20 19:32:44 +08:00
lejianwen
d4015d7284 fix 2024-11-20 09:20:29 +08:00
lejianwen
a9bf3fda73 fix https://github.com/lejianwen/rustdesk-api/discussions/59#discussioncomment-11306760 2024-11-20 09:17:29 +08:00
34 changed files with 141339 additions and 143517 deletions

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024-present Lejianwen and contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

200
README.md
View File

@@ -148,6 +148,7 @@
6. `v2 preview` 部署
- 如果是通过`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。
@@ -262,189 +263,8 @@ proxy:
lejianwen/rustdesk-api
```
2. 使用`docker compose`
- 简单示例
```yaml
services:
rustdesk-api:
container_name: rustdesk-api
environment:
- TZ=Asia/Shanghai
- RUSTDESK_API_RUSTDESK_ID_SERVER=192.168.1.66:21116
- RUSTDESK_API_RUSTDESK_RELAY_SERVER=192.168.1.66:21117
- RUSTDESK_API_RUSTDESK_API_SERVER=http://192.168.1.66:21114
- RUSTDESK_API_RUSTDESK_KEY=<key>
ports:
- 21114:21114
image: lejianwen/rustdesk-api
volumes:
- /data/rustdesk/api:/app/data #将数据库挂载出来方便备份
networks:
- rustdesk-net
restart: unless-stopped
```
2. 使用`docker compose`,参考[wiki](https://github.com/lejianwen/rustdesk-api/wiki)
- 根据rustdesk官方提供的示例加上自己的rustdesk-api
- 如果是使用的系统生成的KEY去掉`-k <key>`参数,在启动后运行`docker-compose logs hbbs`或者`cat ./data/id_ed25519.pub`查看KEY然后再修改`RUSTDESK_API_RUSTDESK_KEY=<key>`再执行`docker-compose up -d`
```yaml
networks:
rustdesk-net:
external: false
services:
hbbs:
container_name: hbbs
ports:
- 21115:21115
- 21116:21116 # 自定义 hbbs 映射端口
- 21116:21116/udp # 自定义 hbbs 映射端口
- 21118:21118 # web client
image: rustdesk/rustdesk-server
command: hbbs -r <relay-server-ip[:port]> -k <key> # 填入个人域名或 IP + hbbr 暴露端口
volumes:
- ./data:/root # 自定义挂载目录
networks:
- rustdesk-net
depends_on:
- hbbr
restart: unless-stopped
deploy:
resources:
limits:
memory: 64M
hbbr:
container_name: hbbr
ports:
- 21117:21117 # 自定义 hbbr 映射端口
- 21119:21119 # web client
image: rustdesk/rustdesk-server
command: hbbr -k <key>
volumes:
- ./data:/root
networks:
- rustdesk-net
restart: unless-stopped
deploy:
resources:
limits:
memory: 64M
rustdesk-api:
container_name: rustdesk-api
environment:
- TZ=Asia/Shanghai
- RUSTDESK_API_RUSTDESK_ID_SERVER=192.168.1.66:21116
- RUSTDESK_API_RUSTDESK_RELAY_SERVER=192.168.1.66:21117
- RUSTDESK_API_RUSTDESK_API_SERVER=http://192.168.1.66:21114
- RUSTDESK_API_RUSTDESK_KEY=<key>
ports:
- 21114:21114
image: lejianwen/rustdesk-api
volumes:
- /data/rustdesk/api:/app/data #将数据库挂载出来方便备份
networks:
- rustdesk-net
restart: unless-stopped
```
- S6的镜像
- 如果使用***自定义KEY***,会需要修改启动脚本,覆盖镜像中的`/etc/s6-overlay/s6-rc.d/hbbr/run`和`/etc/s6-overlay/s6-rc.d/hbbr/run`
1. 创建`hbbr/run`自定义KEY才需要
```bash
#!/command/with-contenv sh
cd /data
PARAMS=
[ "${ENCRYPTED_ONLY}" = "1" ] && PARAMS="-k ${KEY}"
/usr/bin/hbbr $PARAMS
```
2. 创建`hbbs/run`自定义KEY才需要
```bash
#!/command/with-contenv sh
sleep 2
cd /data
PARAMS=
[ "${ENCRYPTED_ONLY}" = "1" ] && PARAMS="-k ${KEY}"
/usr/bin/hbbs -r $RELAY $PARAMS
```
3. 修改`docker-compose.yml`中的`s6`部分
```yaml
networks:
rustdesk-net:
external: false
services:
rustdesk-server:
container_name: rustdesk-server
ports:
- 21115:21115
- 21116:21116
- 21116:21116/udp
- 21117:21117
- 21118:21118
- 21119:21119
image: rustdesk/rustdesk-server-s6:latest
environment:
- RELAY=192.168.1.66:21117
- ENCRYPTED_ONLY=1
- KEY=<key> #自定义KEY
volumes:
- ./data:/data
- ./hbbr/run:/etc/s6-overlay/s6-rc.d/hbbr/run
- ./hbbs/run:/etc/s6-overlay/s6-rc.d/hbbs/run
restart: unless-stopped
rustdesk-api:
container_name: rustdesk-api
ports:
- 21114:21114
image: lejianwen/rustdesk-api
environment:
- TZ=Asia/Shanghai
- RUSTDESK_API_RUSTDESK_ID_SERVER=192.168.1.66:21116
- RUSTDESK_API_RUSTDESK_RELAY_SERVER=192.168.1.66:21117
- RUSTDESK_API_RUSTDESK_API_SERVER=http://192.168.1.66:21114
- RUSTDESK_API_RUSTDESK_KEY=<key>
volumes:
- /data/rustdesk/api:/app/data #将数据库挂载
networks:
- rustdesk-net
restart: unless-stopped
```
- 如果使用***系统生成的KEY***或者***自定义KEY_PUB,KEY_PRIV***不需要修改启动脚本但要在生成KEY后获取到KEY再`docker-compose up -d`
```yaml
networks:
rustdesk-net:
external: false
services:
rustdesk-server:
container_name: rustdesk-server
ports:
- 21115:21115
- 21116:21116
- 21116:21116/udp
- 21117:21117
- 21118:21118
- 21119:21119
image: rustdesk/rustdesk-server-s6:latest
environment:
- RELAY=192.168.1.66:21117
- ENCRYPTED_ONLY=1
volumes:
- ./data:/data
restart: unless-stopped
rustdesk-api:
container_name: rustdesk-api
ports:
- 21114:21114
image: lejianwen/rustdesk-api
environment:
- TZ=Asia/Shanghai
- RUSTDESK_API_RUSTDESK_ID_SERVER=192.168.1.66:21116
- RUSTDESK_API_RUSTDESK_RELAY_SERVER=192.168.1.66:21117
- RUSTDESK_API_RUSTDESK_API_SERVER=http://192.168.1.66:21114
- RUSTDESK_API_RUSTDESK_KEY=<key> #系统生成的KEY
volumes:
- /data/rustdesk/api:/app/data #将数据库挂载
networks:
- rustdesk-net
restart: unless-stopped
```
#### 下载release直接运行
[下载地址](https://github.com/lejianwen/rustdesk-api/releases)
@@ -487,21 +307,7 @@ proxy:
6. 打开浏览器访问`http://<your server[:port]>/_admin/`,默认用户名密码为`admin`,请及时更改密码。
#### nginx反代
在`nginx`中配置反代
```
server {
listen <your port>;
server_name <your server>;
location / {
proxy_pass http://<api-server[:port]>;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
```
## 其他
- [修改客户端ID](https://github.com/abdullah-erturk/RustDesk-ID-Changer)

View File

@@ -152,6 +152,7 @@ installation are `admin` `admin`, please change the password immediately.
6. `v2 preview` deployment
- 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.
@@ -266,189 +267,7 @@ The prefix for variable names is `RUSTDESK_API`. If environment variables exist,
lejianwen/rustdesk-api
```
2. Using `docker-compose`
- Simple example:
```yaml
services:
rustdesk-api:
container_name: rustdesk-api
environment:
- RUSTDESK_API_LANG=en
- RUSTDESK_API_RUSTDESK_ID_SERVER=192.168.1.66:21116
- RUSTDESK_API_RUSTDESK_RELAY_SERVER=192.168.1.66:21117
- RUSTDESK_API_RUSTDESK_API_SERVER=http://192.168.1.66:21114
- RUSTDESK_API_RUSTDESK_KEY=<key>
ports:
- 21114:21114
image: lejianwen/rustdesk-api
volumes:
- /data/rustdesk/api:/app/data # Mount the database for easy backup
networks:
- rustdesk-net
restart: unless-stopped
```
- Example with RustDesk's official Docker Compose file, adding your `rustdesk-api` service:
- If you are using a system-generated KEY, remove the `-k <key>` parameter. However, after the first startup, run `docker-compose logs hbbs` or `cat ./data/id_ed25519.pub` to view the KEY, then modify `RUSTDESK_API_RUSTDESK_KEY=<key>` and execute `docker-compose up -d` again.
```yaml
networks:
rustdesk-net:
external: false
services:
hbbs:
container_name: hbbs
ports:
- 21115:21115
- 21116:21116 # 自定义 hbbs 映射端口
- 21116:21116/udp # 自定义 hbbs 映射端口
- 21118:21118 # web client
image: rustdesk/rustdesk-server
command: hbbs -r <relay-server-ip[:port]> -k <key> # 填入个人域名或 IP + hbbr 暴露端口
volumes:
- ./data:/root # 自定义挂载目录
networks:
- rustdesk-net
depends_on:
- hbbr
restart: unless-stopped
deploy:
resources:
limits:
memory: 64M
hbbr:
container_name: hbbr
ports:
- 21117:21117 # 自定义 hbbr 映射端口
- 21119:21119 # web client
image: rustdesk/rustdesk-server
command: hbbr -k <key>
volumes:
- ./data:/root
networks:
- rustdesk-net
restart: unless-stopped
deploy:
resources:
limits:
memory: 64M
rustdesk-api:
container_name: rustdesk-api
environment:
- TZ=Asia/Shanghai
- RUSTDESK_API_RUSTDESK_ID_SERVER=192.168.1.66:21116
- RUSTDESK_API_RUSTDESK_RELAY_SERVER=192.168.1.66:21117
- RUSTDESK_API_RUSTDESK_API_SERVER=http://192.168.1.66:21114
- RUSTDESK_API_RUSTDESK_KEY=<key>
ports:
- 21114:21114
image: lejianwen/rustdesk-api
volumes:
- /data/rustdesk/api:/app/data #将数据库挂载出来方便备份
networks:
- rustdesk-net
restart: unless-stopped
```
- S6 image
- - If using ***custom KEY***, you will need to modify the startup script to override the `/etc/s6-overlay/s6-rc.d/hbbr/run` and `/etc/s6-overlay/s6-rc.d/hbbr/run` in the image.
1. Create `hbbr/run`, only needed for custom KEY
```bash
#!/command/with-contenv sh
cd /data
PARAMS=
[ "${ENCRYPTED_ONLY}" = "1" ] && PARAMS="-k ${KEY}"
/usr/bin/hbbr $PARAMS
```
2. Create `hbbs/run`, only needed for custom KEY
```bash
#!/command/with-contenv sh
sleep 2
cd /data
PARAMS=
[ "${ENCRYPTED_ONLY}" = "1" ] && PARAMS="-k ${KEY}"
/usr/bin/hbbs -r $RELAY $PARAMS
```
3. Modify the `s6` section in `docker-compose.yml`
```yaml
networks:
rustdesk-net:
external: false
services:
rustdesk-server:
container_name: rustdesk-server
ports:
- 21115:21115
- 21116:21116
- 21116:21116/udp
- 21117:21117
- 21118:21118
- 21119:21119
image: rustdesk/rustdesk-server-s6:latest
environment:
- RELAY=192.168.1.66:21117
- ENCRYPTED_ONLY=1
- KEY=<key> #KEY
volumes:
- ./data:/data
- ./hbbr/run:/etc/s6-overlay/s6-rc.d/hbbr/run
- ./hbbs/run:/etc/s6-overlay/s6-rc.d/hbbs/run
restart: unless-stopped
rustdesk-api:
container_name: rustdesk-api
ports:
- 21114:21114
image: lejianwen/rustdesk-api
environment:
- TZ=Asia/Shanghai
- RUSTDESK_API_RUSTDESK_ID_SERVER=192.168.1.66:21116
- RUSTDESK_API_RUSTDESK_RELAY_SERVER=192.168.1.66:21117
- RUSTDESK_API_RUSTDESK_API_SERVER=http://192.168.1.66:21114
- RUSTDESK_API_RUSTDESK_KEY=<key>
volumes:
- /data/rustdesk/api:/app/data
networks:
- rustdesk-net
restart: unless-stopped
```
- If using ***system-generated KEY*** or ***custom KEY_PUB, KEY_PRIV***, you do not need to modify the startup script, but you need to obtain the KEY after it is generated and then run `docker-compose up -d`
```yaml
networks:
rustdesk-net:
external: false
services:
rustdesk-server:
container_name: rustdesk-server
ports:
- 21115:21115
- 21116:21116
- 21116:21116/udp
- 21117:21117
- 21118:21118
- 21119:21119
image: rustdesk/rustdesk-server-s6:latest
environment:
- RELAY=192.168.1.66:21117
- ENCRYPTED_ONLY=1
volumes:
- ./data:/data
restart: unless-stopped
rustdesk-api:
container_name: rustdesk-api
ports:
- 21114:21114
image: lejianwen/rustdesk-api
environment:
- TZ=Asia/Shanghai
- RUSTDESK_API_RUSTDESK_ID_SERVER=192.168.1.66:21116
- RUSTDESK_API_RUSTDESK_RELAY_SERVER=192.168.1.66:21117
- RUSTDESK_API_RUSTDESK_API_SERVER=http://192.168.1.66:21114
- RUSTDESK_API_RUSTDESK_KEY=<key>
volumes:
- /data/rustdesk/api:/app/data
networks:
- rustdesk-net
restart: unless-stopped
```
2. Using `docker-compose`,look [wiki](https://github.com/lejianwen/rustdesk-api/wiki)
#### Running from Release
@@ -497,21 +316,7 @@ Download the release from [release](https://github.com/lejianwen/rustdesk-api/re
6. Open your browser and visit `http://<your server[:port]>/_admin/`, with default credentials `admin admin`. Please
change the password promptly.
#### nginx reverse proxy
Configure reverse proxy in `nginx`
```
server {
listen <your port>;
server_name <your server>;
location / {
proxy_pass http://<api-server[:port]>;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
```
## Others
- [Change client ID](https://github.com/abdullah-erturk/RustDesk-ID-Changer)

View File

@@ -2019,6 +2019,144 @@ const docTemplateadmin = `{
}
}
},
"/admin/my/share_record/batchDelete": {
"post": {
"security": [
{
"token": []
}
],
"description": "批量删除我的分享记录",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"我的"
],
"summary": "批量删除我的分享记录",
"parameters": [
{
"description": "id",
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/admin.PeerShareRecordBatchDeleteForm"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/admin/my/share_record/delete": {
"post": {
"security": [
{
"token": []
}
],
"description": "分享记录删除",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"我的"
],
"summary": "分享记录删除",
"parameters": [
{
"description": "分享记录信息",
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/admin.ShareRecordForm"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/admin/my/share_record/list": {
"get": {
"security": [
{
"token": []
}
],
"description": "分享记录列表",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"我的"
],
"summary": "分享记录列表",
"parameters": [
{
"type": "integer",
"description": "页码",
"name": "page",
"in": "query"
},
{
"type": "integer",
"description": "页大小",
"name": "page_size",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/admin/oauth/create": {
"post": {
"security": [
@@ -2727,6 +2865,150 @@ const docTemplateadmin = `{
}
}
},
"/admin/share_record/batchDelete": {
"post": {
"security": [
{
"token": []
}
],
"description": "批量分享记录",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"分享记录"
],
"summary": "批量分享记录",
"parameters": [
{
"description": "id",
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/admin.PeerShareRecordBatchDeleteForm"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/admin/share_record/delete": {
"post": {
"security": [
{
"token": []
}
],
"description": "分享记录删除",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"分享记录"
],
"summary": "分享记录删除",
"parameters": [
{
"description": "分享记录信息",
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/admin.ShareRecordForm"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/admin/share_record/list": {
"get": {
"security": [
{
"token": []
}
],
"description": "分享记录列表",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"分享记录"
],
"summary": "分享记录列表",
"parameters": [
{
"type": "integer",
"description": "用户ID",
"name": "user_id",
"in": "query"
},
{
"type": "integer",
"description": "页码",
"name": "page",
"in": "query"
},
{
"type": "integer",
"description": "页大小",
"name": "page_size",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/admin/tag/create": {
"post": {
"security": [
@@ -3562,6 +3844,51 @@ const docTemplateadmin = `{
}
}
},
"/admin/user_token/batchDelete": {
"post": {
"security": [
{
"token": []
}
],
"description": "登录凭证批量删除",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"登录凭证"
],
"summary": "登录凭证批量删除",
"parameters": [
{
"description": "登录凭证信息",
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/admin.UserTokenBatchDeleteForm"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/admin/user_token/delete": {
"post": {
"security": [
@@ -3798,12 +4125,12 @@ const docTemplateadmin = `{
"properties": {
"new_password": {
"type": "string",
"maxLength": 20,
"maxLength": 32,
"minLength": 4
},
"old_password": {
"type": "string",
"maxLength": 20,
"maxLength": 32,
"minLength": 4
}
}
@@ -3949,6 +4276,20 @@ const docTemplateadmin = `{
}
}
},
"admin.PeerShareRecordBatchDeleteForm": {
"type": "object",
"required": [
"ids"
],
"properties": {
"ids": {
"type": "array",
"items": {
"type": "integer"
}
}
}
},
"admin.ShareByWebClientForm": {
"type": "object",
"required": [
@@ -3976,6 +4317,17 @@ const docTemplateadmin = `{
}
}
},
"admin.ShareRecordForm": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"user_id": {
"type": "integer"
}
}
},
"admin.TagForm": {
"type": "object",
"required": [
@@ -4038,7 +4390,7 @@ const docTemplateadmin = `{
},
"username": {
"type": "string",
"maxLength": 10,
"maxLength": 32,
"minLength": 2
}
}
@@ -4066,11 +4418,25 @@ const docTemplateadmin = `{
},
"password": {
"type": "string",
"maxLength": 20,
"maxLength": 32,
"minLength": 4
}
}
},
"admin.UserTokenBatchDeleteForm": {
"type": "object",
"required": [
"ids"
],
"properties": {
"ids": {
"type": "array",
"items": {
"type": "integer"
}
}
}
},
"model.AddressBook": {
"type": "object",
"properties": {

View File

@@ -2012,6 +2012,144 @@
}
}
},
"/admin/my/share_record/batchDelete": {
"post": {
"security": [
{
"token": []
}
],
"description": "批量删除我的分享记录",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"我的"
],
"summary": "批量删除我的分享记录",
"parameters": [
{
"description": "id",
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/admin.PeerShareRecordBatchDeleteForm"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/admin/my/share_record/delete": {
"post": {
"security": [
{
"token": []
}
],
"description": "分享记录删除",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"我的"
],
"summary": "分享记录删除",
"parameters": [
{
"description": "分享记录信息",
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/admin.ShareRecordForm"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/admin/my/share_record/list": {
"get": {
"security": [
{
"token": []
}
],
"description": "分享记录列表",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"我的"
],
"summary": "分享记录列表",
"parameters": [
{
"type": "integer",
"description": "页码",
"name": "page",
"in": "query"
},
{
"type": "integer",
"description": "页大小",
"name": "page_size",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/admin/oauth/create": {
"post": {
"security": [
@@ -2720,6 +2858,150 @@
}
}
},
"/admin/share_record/batchDelete": {
"post": {
"security": [
{
"token": []
}
],
"description": "批量分享记录",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"分享记录"
],
"summary": "批量分享记录",
"parameters": [
{
"description": "id",
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/admin.PeerShareRecordBatchDeleteForm"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/admin/share_record/delete": {
"post": {
"security": [
{
"token": []
}
],
"description": "分享记录删除",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"分享记录"
],
"summary": "分享记录删除",
"parameters": [
{
"description": "分享记录信息",
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/admin.ShareRecordForm"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/admin/share_record/list": {
"get": {
"security": [
{
"token": []
}
],
"description": "分享记录列表",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"分享记录"
],
"summary": "分享记录列表",
"parameters": [
{
"type": "integer",
"description": "用户ID",
"name": "user_id",
"in": "query"
},
{
"type": "integer",
"description": "页码",
"name": "page",
"in": "query"
},
{
"type": "integer",
"description": "页大小",
"name": "page_size",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/admin/tag/create": {
"post": {
"security": [
@@ -3555,6 +3837,51 @@
}
}
},
"/admin/user_token/batchDelete": {
"post": {
"security": [
{
"token": []
}
],
"description": "登录凭证批量删除",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"登录凭证"
],
"summary": "登录凭证批量删除",
"parameters": [
{
"description": "登录凭证信息",
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/admin.UserTokenBatchDeleteForm"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/admin/user_token/delete": {
"post": {
"security": [
@@ -3791,12 +4118,12 @@
"properties": {
"new_password": {
"type": "string",
"maxLength": 20,
"maxLength": 32,
"minLength": 4
},
"old_password": {
"type": "string",
"maxLength": 20,
"maxLength": 32,
"minLength": 4
}
}
@@ -3942,6 +4269,20 @@
}
}
},
"admin.PeerShareRecordBatchDeleteForm": {
"type": "object",
"required": [
"ids"
],
"properties": {
"ids": {
"type": "array",
"items": {
"type": "integer"
}
}
}
},
"admin.ShareByWebClientForm": {
"type": "object",
"required": [
@@ -3969,6 +4310,17 @@
}
}
},
"admin.ShareRecordForm": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"user_id": {
"type": "integer"
}
}
},
"admin.TagForm": {
"type": "object",
"required": [
@@ -4031,7 +4383,7 @@
},
"username": {
"type": "string",
"maxLength": 10,
"maxLength": 32,
"minLength": 2
}
}
@@ -4059,11 +4411,25 @@
},
"password": {
"type": "string",
"maxLength": 20,
"maxLength": 32,
"minLength": 4
}
}
},
"admin.UserTokenBatchDeleteForm": {
"type": "object",
"required": [
"ids"
],
"properties": {
"ids": {
"type": "array",
"items": {
"type": "integer"
}
}
}
},
"model.AddressBook": {
"type": "object",
"properties": {

View File

@@ -78,11 +78,11 @@ definitions:
admin.ChangeCurPasswordForm:
properties:
new_password:
maxLength: 20
maxLength: 32
minLength: 4
type: string
old_password:
maxLength: 20
maxLength: 32
minLength: 4
type: string
required:
@@ -182,6 +182,15 @@ definitions:
version:
type: string
type: object
admin.PeerShareRecordBatchDeleteForm:
properties:
ids:
items:
type: integer
type: array
required:
- ids
type: object
admin.ShareByWebClientForm:
properties:
expire:
@@ -201,6 +210,13 @@ definitions:
- password
- password_type
type: object
admin.ShareRecordForm:
properties:
id:
type: integer
user_id:
type: integer
type: object
admin.TagForm:
properties:
collection_id:
@@ -238,7 +254,7 @@ definitions:
- $ref: '#/definitions/model.StatusCode'
minimum: 0
username:
maxLength: 10
maxLength: 32
minLength: 2
type: string
required:
@@ -258,13 +274,22 @@ definitions:
id:
type: integer
password:
maxLength: 20
maxLength: 32
minLength: 4
type: string
required:
- id
- password
type: object
admin.UserTokenBatchDeleteForm:
properties:
ids:
items:
type: integer
type: array
required:
- ids
type: object
model.AddressBook:
properties:
alias:
@@ -1950,6 +1975,92 @@ paths:
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/oauth/create:
post:
consumes:
@@ -2372,6 +2483,96 @@ paths:
summary: RUSTDESK服务配置
tags:
- 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:
post:
consumes:
@@ -2867,6 +3068,34 @@ paths:
summary: 修改密码
tags:
- 用户
/admin/user_token/batchDelete:
post:
consumes:
- application/json
description: 登录凭证批量删除
parameters:
- description: 登录凭证信息
in: body
name: body
required: true
schema:
$ref: '#/definitions/admin.UserTokenBatchDeleteForm'
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/delete:
post:
consumes:

View File

@@ -1075,6 +1075,40 @@ const docTemplateapi = `{
}
}
},
"/server-config-v2": {
"get": {
"security": [
{
"token": []
}
],
"description": "服务配置,给webclient提供api-server",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"WEBCLIENT_V2"
],
"summary": "服务配置",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/shared-peer": {
"post": {
"description": "分享的peer",
@@ -1356,7 +1390,7 @@ const docTemplateapi = `{
},
"password": {
"type": "string",
"maxLength": 20,
"maxLength": 32,
"minLength": 4
},
"type": {
@@ -1364,7 +1398,7 @@ const docTemplateapi = `{
},
"username": {
"type": "string",
"maxLength": 10,
"maxLength": 32,
"minLength": 2
},
"uuid": {

View File

@@ -1068,6 +1068,40 @@
}
}
},
"/server-config-v2": {
"get": {
"security": [
{
"token": []
}
],
"description": "服务配置,给webclient提供api-server",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"WEBCLIENT_V2"
],
"summary": "服务配置",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/shared-peer": {
"post": {
"description": "分享的peer",
@@ -1349,7 +1383,7 @@
},
"password": {
"type": "string",
"maxLength": 20,
"maxLength": 32,
"minLength": 4
},
"type": {
@@ -1357,7 +1391,7 @@
},
"username": {
"type": "string",
"maxLength": 10,
"maxLength": 32,
"minLength": 2
},
"uuid": {

View File

@@ -62,13 +62,13 @@ definitions:
id:
type: string
password:
maxLength: 20
maxLength: 32
minLength: 4
type: string
type:
type: string
username:
maxLength: 10
maxLength: 32
minLength: 2
type: string
uuid:
@@ -870,6 +870,27 @@ paths:
summary: 服务配置
tags:
- WEBCLIENT
/server-config-v2:
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_V2
/shared-peer:
post:
consumes:

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

@@ -3,6 +3,7 @@ package global
import (
"github.com/gin-gonic/gin"
"github.com/go-playground/locales/en"
"github.com/go-playground/locales/es"
"github.com/go-playground/locales/ko"
"github.com/go-playground/locales/ru"
"github.com/go-playground/locales/zh_Hans_CN"
@@ -10,6 +11,7 @@ import (
ut "github.com/go-playground/universal-translator"
"github.com/go-playground/validator/v10"
en_translations "github.com/go-playground/validator/v10/translations/en"
es_translations "github.com/go-playground/validator/v10/translations/es"
ru_translations "github.com/go-playground/validator/v10/translations/ru"
zh_translations "github.com/go-playground/validator/v10/translations/zh"
"reflect"
@@ -23,13 +25,15 @@ func ApiInitValidator() {
cn := zh_Hans_CN.New()
koT := ko.New()
ruT := ru.New()
esT := es.New()
uni := ut.New(enT, cn, koT, ruT)
uni := ut.New(enT, cn, koT, ruT, esT)
enTrans, _ := uni.GetTranslator("en")
zhTrans, _ := uni.GetTranslator("zh_Hans_CN")
koTrans, _ := uni.GetTranslator("ko")
ruTrans, _ := uni.GetTranslator("ru")
esTrans, _ := uni.GetTranslator("es")
err := zh_translations.RegisterDefaultTranslations(validate, zhTrans)
if err != nil {
@@ -49,6 +53,10 @@ func ApiInitValidator() {
if err != nil {
panic(err)
}
err = es_translations.RegisterDefaultTranslations(validate, esTrans)
if err != nil {
panic(err)
}
validate.RegisterTagNameFunc(func(field reflect.StructField) string {
label := field.Tag.Get("label")
@@ -115,6 +123,9 @@ func getTranslatorForLang(lang string) ut.Translator {
case "ru":
trans, _ := Validator.UT.GetTranslator("ru")
return trans
case "es":
trans, _ := Validator.UT.GetTranslator("es")
return trans
case "en":
fallthrough
default:

View File

@@ -6,6 +6,7 @@ import (
"Gwen/http/response"
"Gwen/model"
"Gwen/service"
"encoding/json"
_ "encoding/json"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
@@ -327,3 +328,71 @@ func (ct *AddressBook) ShareByWebClient(c *gin.Context) {
"share_token": m.ShareToken,
})
}
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
}
if f.UserId == 0 {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
return
}
if f.CollectionId != 0 {
collection := service.AllService.AddressBookService.CollectionInfoById(f.CollectionId)
if collection.Id == 0 {
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
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)
})
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
ab.UserId = f.UserId
ex := service.AllService.AddressBookService.InfoByUserIdAndIdAndCid(f.UserId, 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,59 @@
package my
import (
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/service"
"encoding/json"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
type AddressBook struct{}
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)
}

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,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

@@ -81,3 +81,33 @@ func (ct *UserToken) Delete(c *gin.Context) {
}
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
}
// BatchDelete 批量删除
// @Tags 登录凭证
// @Summary 登录凭证批量删除
// @Description 登录凭证批量删除
// @Accept json
// @Produce json
// @Param body body admin.UserTokenBatchDeleteForm true "登录凭证信息"
// @Success 200 {object} response.Response
// @Failure 500 {object} response.Response
// @Router /admin/user_token/batchDelete [post]
// @Security token
func (ct *UserToken) BatchDelete(c *gin.Context) {
f := &admin.UserTokenBatchDeleteForm{}
if err := c.ShouldBindJSON(f); err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
ids := f.Ids
if len(ids) == 0 {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
return
}
err := service.AllService.UserService.BatchDeleteUserToken(ids)
if err == nil {
response.Success(c, nil)
return
}
response.Fail(c, 101, err.Error())
}

View File

@@ -94,7 +94,7 @@ func (i *WebClient) SharedPeer(c *gin.Context) {
// @Produce json
// @Success 200 {object} response.Response
// @Failure 500 {object} response.Response
// @Router /server-config [get]
// @Router /server-config-v2 [get]
// @Security token
func (i *WebClient) ServerConfigV2(c *gin.Context) {
response.Success(

View File

@@ -122,3 +122,14 @@ type AddressBookCollectionRuleQuery struct {
IsMy int `form:"is_my"`
PageQuery
}
type BatchCreateFromPeersForm struct {
CollectionId uint `json:"collection_id"`
PeerIds []uint `json:"peer_ids"`
Tags []string `json:"tags"`
UserId uint `json:"user_id"`
}
type BatchUpdateTagsForm struct {
RowIds []uint `json:"row_ids"`
Tags []string `json:"tags"`
}

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

@@ -6,7 +6,7 @@ import (
type UserForm struct {
Id uint `json:"id"`
Username string `json:"username" validate:"required,gte=2,lte=10"`
Username string `json:"username" validate:"required,gte=2,lte=32"`
Email string `json:"email"` //validate:"required,email" email不强制
//Password string `json:"password" validate:"required,gte=4,lte=20"`
Nickname string `json:"nickname"`
@@ -51,12 +51,12 @@ type UserQuery struct {
}
type UserPasswordForm struct {
Id uint `json:"id" validate:"required"`
Password string `json:"password" validate:"required,gte=4,lte=20"`
Password string `json:"password" validate:"required,gte=4,lte=32"`
}
type ChangeCurPasswordForm struct {
OldPassword string `json:"old_password" validate:"required,gte=4,lte=20"`
NewPassword string `json:"new_password" validate:"required,gte=4,lte=20"`
OldPassword string `json:"old_password" validate:"required,gte=4,lte=32"`
NewPassword string `json:"new_password" validate:"required,gte=4,lte=32"`
}
type GroupUsersQuery struct {
IsMy int `json:"is_my"`
@@ -64,8 +64,12 @@ type GroupUsersQuery struct {
}
type RegisterForm struct {
Username string `json:"username" validate:"required,gte=2,lte=10"`
Username string `json:"username" validate:"required,gte=2,lte=32"`
Email string `json:"email"` // validate:"required,email"
Password string `json:"password" validate:"required,gte=4,lte=20"`
ConfirmPassword string `json:"confirm_password" validate:"required,gte=4,lte=20"`
Password string `json:"password" validate:"required,gte=4,lte=32"`
ConfirmPassword string `json:"confirm_password" validate:"required,gte=4,lte=32"`
}
type UserTokenBatchDeleteForm struct {
Ids []uint `json:"ids" validate:"required"`
}

View File

@@ -34,8 +34,8 @@ type LoginForm struct {
Id string `json:"id" label:"id"`
Type string `json:"type" label:"type"`
Uuid string `json:"uuid" label:"uuid"`
Username string `json:"username" validate:"required,gte=2,lte=10" label:"用户名"`
Password string `json:"password,omitempty" validate:"gte=4,lte=20" label:"密码"`
Username string `json:"username" validate:"required,gte=2,lte=32" label:"用户名"`
Password string `json:"password,omitempty" validate:"gte=4,lte=32" label:"密码"`
}
type UserListQuery struct {

View File

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

View File

@@ -3,6 +3,7 @@ package router
import (
_ "Gwen/docs/admin"
"Gwen/http/controller/admin"
"Gwen/http/controller/admin/my"
"Gwen/http/middleware"
"github.com/gin-gonic/gin"
swaggerFiles "github.com/swaggo/files"
@@ -34,11 +35,14 @@ func Init(g *gin.Engine) {
ConfigBind(adg)
//deprecated by ConfigBind
rs := &admin.Rustdesk{}
adg.GET("/server-config", rs.ServerConfig)
adg.GET("/app-config", rs.AppConfig)
//rs := &admin.Rustdesk{}
//adg.GET("/server-config", rs.ServerConfig)
//adg.GET("/app-config", rs.AppConfig)
//deprecated end
ShareRecordBind(adg)
MyBind(adg)
//访问静态文件
//g.StaticFS("/upload", http.Dir(global.Config.Gin.ResourcesPath+"/upload"))
}
@@ -108,8 +112,12 @@ func AddressBookBind(rg *gin.RouterGroup) {
aR.POST("/delete", cont.Delete)
aR.POST("/shareByWebClient", cont.ShareByWebClient)
aR.POST("/batchUpdateTags", cont.BatchUpdateTags)
arp := aR.Use(middleware.AdminPrivilege())
arp.POST("/batchCreate", cont.BatchCreate)
arp.POST("/batchCreateFromPeers", cont.BatchCreateFromPeers)
}
}
func PeerBind(rg *gin.RouterGroup) {
@@ -195,6 +203,7 @@ func UserTokenBind(rg *gin.RouterGroup) {
cont := &admin.UserToken{}
aR.GET("/list", cont.List)
aR.POST("/delete", cont.Delete)
aR.POST("/batchDelete", cont.BatchDelete)
}
func ConfigBind(rg *gin.RouterGroup) {
aR := rg.Group("/config")
@@ -216,3 +225,26 @@ func FileBind(rg *gin.RouterGroup) {
aR.POST("/upload", cont.Upload)
}
}*/
func MyBind(rg *gin.RouterGroup) {
{
msr := &my.ShareRecord{}
rg.GET("/my/share_record/list", msr.List)
rg.POST("/my/share_record/delete", msr.Delete)
rg.POST("/my/share_record/batchDelete", msr.BatchDelete)
mab := &my.AddressBook{}
rg.POST("/my/address_book/batchCreateFromPeers", mab.BatchCreateFromPeers)
}
}
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

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

134
resources/i18n/es.toml Normal file
View File

@@ -0,0 +1,134 @@
[Test]
description = "test"
one = "prueba1"
other = "Prueba2 {{.P0}}"
[ParamsError]
description = "Params validation failed."
one = "La validación de los parámetros falló."
other = "La validación de los parámetros falló."
[OperationFailed]
description = "OperationFailed."
one = "La operación falló."
other = "La operación falló."
[OperationSuccess]
description = "OperationSuccess."
one = "La operación fue exitosa."
other = "La operación fue exitosa."
[ItemExists]
description = "Item already exists."
one = "El elemento ya existe."
other = "El elemento ya existe."
[ItemNotFound]
description = "Item not found."
one = "El elemento no fue encontrado."
other = "El elemento no fue encontrado."
[NoAccess]
description = "No access."
one = "Sin acceso."
other = "Sin acceso."
[UsernameOrPasswordError]
description = "Username or password error."
one = "Error de usuario o contraseña."
other = "Error de usuario o contraseña."
[SystemError]
description = "System error."
one = "Error del sistema."
other = "Error del sistema."
[ConfigNotFound]
description = "Config not found."
one = "Configuración no encontrada."
other = "Configuración no encontrada."
[OauthExpired]
description = "Oauth expired."
one = "Oauth expirado, por favor intente nuevamente."
other = "Oauth expirado, por favor intente nuevamente."
[OauthFailed]
description = "Oauth failed."
one = "Oauth falló."
other = "Oauth falló."
[OauthHasBindOtherUser]
description = "Oauth has bind other user."
one = "Oauth está vinculado a otro usuario."
other = "Oauth está vinculado a otro usuario."
[ParamIsEmpty]
description = "Param is empty."
one = "{{.P0}} está vacío."
other = "{{.P0}} está vacío."
[BindFail]
description = "Bind fail."
one = "Fallo al vincular."
other = "Fallo al vincular."
[BindSuccess]
description = "Bind success."
one = "Vinculación exitosa."
other = "Vinculación exitosa."
[OauthHasBeenSuccess]
description = "Oauth has been success."
one = "Oauth fue exitoso."
other = "Oauth fue exitoso."
[OauthSuccess]
description = "Oauth success."
one = "Oauth exitoso."
other = "Oauth exitoso."
[OauthRegisterSuccess]
description = "Oauth register success."
one = "Registro de Oauth exitoso."
other = "Registro de Oauth exitoso."
[OauthRegisterFailed]
description = "Oauth register failed."
one = "Registro de Oauth falló."
other = "Registro de Oauth falló."
[GetOauthTokenError]
description = "Get oauth token error."
one = "Error al obtener el token de Oauth."
other = "Error al obtener el token de Oauth."
[GetOauthUserInfoError]
description = "Get oauth user info error."
one = "Error al obtener la información del usuario de Oauth."
other = "Error al obtener la información del usuario de Oauth."
[DecodeOauthUserInfoError]
description = "Decode oauth user info error."
one = "Error al decodificar la información del usuario de Oauth."
other = "Error al decodificar la información del usuario de Oauth."
[OldPasswordError]
description = "Old password error."
one = "Error con la contraseña anterior."
other = "Error con la contraseña anterior."
[DefaultGroup]
description = "Default group"
one = "Grupo predeterminado"
other = "Grupo predeterminado"
[ShareGroup]
description = "Share group"
one = "Grupo compartido"
other = "Grupo compartido"
[RegisterClosed]
description = "Register closed."
one = "Registro cerrado."
other = "Registro cerrado."

View File

@@ -32,7 +32,7 @@
<title>RustDesk</title>
<script src="/webclient-config/index.js"></script>
<link rel="manifest" href="manifest.json" />
<script type="module" crossorigin src="js/dist/index.js?v=5"></script>
<script type="module" crossorigin src="js/dist/index.js?v=893935a2"></script>
<link rel="modulepreload" href="js/dist/vendor.js?v=0b990c6e" />
<style>
html,
@@ -259,7 +259,7 @@
}
scriptLoaded = true;
var scriptTag = document.createElement("script");
scriptTag.src = "main.dart.js?v=5";
scriptTag.src = "main.dart.js?v=df360f45";
scriptTag.type = "application/javascript";
document.body.append(scriptTag);
}

File diff suppressed because it is too large Load Diff

28163
resources/web2/js/dist/lang.js vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -58,7 +58,7 @@ export function getServerConf(token) {
return
}
console.log('getServerConf', token)
// console.log('getServerConf', token)
if (fetching) {
return
}

222768
resources/web2/main.dart.js vendored

File diff suppressed because one or more lines are too long

View File

@@ -3,6 +3,7 @@ package service
import (
"Gwen/global"
"Gwen/model"
"encoding/json"
"github.com/google/uuid"
"gorm.io/gorm"
"strings"
@@ -116,6 +117,16 @@ func (s *AddressBookService) List(page, pageSize uint, where func(tx *gorm.DB))
return
}
func (s *AddressBookService) FromPeer(peer *model.Peer) (a *model.AddressBook) {
a = &model.AddressBook{}
a.Id = peer.Id
a.Username = peer.Username
a.Hostname = peer.Hostname
a.UserId = peer.UserId
a.Platform = s.PlatformFromOs(peer.Os)
return a
}
// Create 创建
func (s *AddressBookService) Create(u *model.AddressBook) error {
res := global.DB.Create(u).Error
@@ -318,3 +329,12 @@ func (s *AddressBookService) CheckCollectionOwner(uid uint, cid uint) bool {
p := s.CollectionInfoById(cid)
return p.UserId == uid
}
func (s *AddressBookService) BatchUpdateTags(abs []*model.AddressBook, tags []string) error {
ids := make([]uint, 0)
for _, ab := range abs {
ids = append(ids, ab.RowId)
}
tagsv, _ := json.Marshal(tags)
return global.DB.Model(&model.AddressBook{}).Where("row_id in ?", ids).Update("tags", tagsv).Error
}

View File

@@ -16,6 +16,7 @@ type Service struct {
*OauthService
*LoginLogService
*AuditService
*ShareRecordService
}
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
}

View File

@@ -458,3 +458,7 @@ func (us *UserService) AutoRefreshAccessToken(ut *model.UserToken) {
us.RefreshAccessToken(ut)
}
}
func (us *UserService) BatchDeleteUserToken(ids []uint) error {
return global.DB.Where("id in ?", ids).Delete(&model.UserToken{}).Error
}