Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
184d3d357d | ||
|
|
50b3d85270 | ||
|
|
09fdd34ba3 | ||
|
|
bba10261c5 | ||
|
|
46bfe54097 | ||
|
|
503e7a307e | ||
|
|
821b0a6faf | ||
|
|
d60fdff179 | ||
|
|
fdd841e82a | ||
|
|
2d6f0a116a | ||
|
|
bd13fe4ef4 | ||
|
|
6e1b208464 | ||
|
|
76433a409e | ||
|
|
9b4fa679c2 | ||
|
|
c2ae95c4cc | ||
|
|
b2b7f60fd5 | ||
|
|
a465888b31 | ||
|
|
d368bdc84c | ||
|
|
cdc1150505 | ||
|
|
32d525c53c | ||
|
|
a89b40c607 |
21
LICENSE
Normal 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.
|
||||
207
README.md
@@ -140,14 +140,12 @@
|
||||
|
||||
1. 如果已经登录了后台,web client将自动直接登录
|
||||
2. 如果没登录后台,点击右上角登录即可,api server已经自动配置好了
|
||||

|
||||
3. 登录后,会自动同步ID服务器和KEY
|
||||
4. 登录后,会将地址簿自动保存到web client中,方便使用
|
||||
5. 现已支持`v2 Preview`,访问路径是`/webclient2`
|
||||

|
||||
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`即可
|
||||
6. `v2 preview` 部署,参考[WIKI](https://github.com/lejianwen/rustdesk-api/wiki)
|
||||
|
||||
|
||||
### 自动化文档: 使用 Swag 生成 API 文档,方便开发者理解和使用 API。
|
||||
|
||||
@@ -179,6 +177,7 @@ lang: "en"
|
||||
app:
|
||||
web-client: 1 # 1:启用 0:禁用
|
||||
register: false #是否开启注册
|
||||
show-swagger: 0 #是否显示swagger文档
|
||||
gin:
|
||||
api-addr: "0.0.0.0:21114"
|
||||
mode: "release"
|
||||
@@ -217,6 +216,7 @@ proxy:
|
||||
| RUSTDESK_API_LANG | 语言 | `en`,`zh-CN` |
|
||||
| RUSTDESK_API_APP_WEB_CLIENT | 是否启用web-client; 1:启用,0:不启用; 默认启用 | 1 |
|
||||
| RUSTDESK_API_APP_REGISTER | 是否开启注册; `true`, `false` 默认`false` | `false` |
|
||||
| RUSTDESK_API_APP_SHOW_SWAGGER | 是否可见swagger文档;`1`显示,`0`不显示,默认`0`不显示 | `1` |
|
||||
| -----ADMIN配置----- | ---------- | ---------- |
|
||||
| RUSTDESK_API_ADMIN_TITLE | 后台标题 | `RustDesk Api Admin` |
|
||||
| RUSTDESK_API_ADMIN_HELLO | 后台欢迎语,可以使用`html` | |
|
||||
@@ -262,189 +262,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 +306,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)
|
||||
|
||||
210
README_EN.md
@@ -144,15 +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.
|
||||
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.
|
||||

|
||||
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.
|
||||
5. Now supports `v2 Preview`, accessible at `/webclient2`
|
||||

|
||||
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`
|
||||
|
||||
6. `v2 preview` deployment, [WIKI](https://github.com/lejianwen/rustdesk-api/wiki)
|
||||
|
||||
### Automated Documentation : API documentation is generated using Swag, making it easier for developers to understand and use the API.
|
||||
|
||||
@@ -184,6 +180,7 @@ lang: "en"
|
||||
app:
|
||||
web-client: 1 # web client route 1:open 0:close
|
||||
register: false #register enable
|
||||
show-swagger: 0 #show swagger 1:open 0:close
|
||||
gin:
|
||||
api-addr: "0.0.0.0:21114"
|
||||
mode: "release"
|
||||
@@ -220,8 +217,9 @@ The prefix for variable names is `RUSTDESK_API`. If environment variables exist,
|
||||
|------------------------------------|-------------------------------------------------------------------------|-------------------------------|
|
||||
| TZ | timezone | Asia/Shanghai |
|
||||
| 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_SHOW_SWAGGER | swagger visible; 1: yes, 0: no; default: 0 | `0` |
|
||||
| ----- ADMIN Configuration----- | ---------- | ---------- |
|
||||
| RUSTDESK_API_ADMIN_TITLE | Admin Title | `RustDesk Api Admin` |
|
||||
| RUSTDESK_API_ADMIN_HELLO | Admin welcome message, you can use `html` | |
|
||||
@@ -266,189 +264,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 +313,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)
|
||||
|
||||
@@ -2,6 +2,7 @@ lang: "zh-CN"
|
||||
app:
|
||||
web-client: 1 # 1:启用 0:禁用
|
||||
register: false #是否开启注册
|
||||
show-swagger: 0 # 1:启用 0:禁用
|
||||
admin:
|
||||
title: "RustDesk Api Admin"
|
||||
hello-file: "./conf/admin/hello.html" #优先使用file
|
||||
|
||||
@@ -14,8 +14,9 @@ const (
|
||||
)
|
||||
|
||||
type App struct {
|
||||
WebClient int `mapstructure:"web-client"`
|
||||
Register bool `mapstructure:"register"`
|
||||
WebClient int `mapstructure:"web-client"`
|
||||
Register bool `mapstructure:"register"`
|
||||
ShowSwagger int `mapstructure:"show-swagger"`
|
||||
}
|
||||
type Admin struct {
|
||||
Title string `mapstructure:"title"`
|
||||
|
||||
@@ -2,6 +2,8 @@ basePath: /api
|
||||
definitions:
|
||||
Gwen_http_request_admin.Login:
|
||||
properties:
|
||||
captcha:
|
||||
type: string
|
||||
password:
|
||||
type: string
|
||||
platform:
|
||||
@@ -78,11 +80,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 +184,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 +212,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 +256,7 @@ definitions:
|
||||
- $ref: '#/definitions/model.StatusCode'
|
||||
minimum: 0
|
||||
username:
|
||||
maxLength: 10
|
||||
maxLength: 32
|
||||
minLength: 2
|
||||
type: string
|
||||
required:
|
||||
@@ -258,13 +276,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:
|
||||
@@ -748,7 +775,7 @@ info:
|
||||
title: 管理系统API
|
||||
version: "1.0"
|
||||
paths:
|
||||
/admin/address_book/create:
|
||||
/admin/address_book/batchCreate:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
@@ -781,6 +808,39 @@ paths:
|
||||
summary: 批量创建地址簿
|
||||
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:
|
||||
post:
|
||||
consumes:
|
||||
@@ -1950,6 +2010,683 @@ paths:
|
||||
summary: 登出
|
||||
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:
|
||||
post:
|
||||
consumes:
|
||||
@@ -2372,6 +3109,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:
|
||||
@@ -2755,57 +3582,6 @@ paths:
|
||||
summary: 我的授权
|
||||
tags:
|
||||
- 用户
|
||||
/admin/user/myPeer:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 我的设备列表
|
||||
parameters:
|
||||
- description: 页码
|
||||
in: query
|
||||
name: page
|
||||
type: integer
|
||||
- description: 页大小
|
||||
in: query
|
||||
name: page_size
|
||||
type: integer
|
||||
- description: 时间
|
||||
in: query
|
||||
name: time_ago
|
||||
type: integer
|
||||
- description: ID
|
||||
in: query
|
||||
name: id
|
||||
type: string
|
||||
- description: 主机名
|
||||
in: query
|
||||
name: hostname
|
||||
type: string
|
||||
- description: uuids 用逗号分隔
|
||||
in: query
|
||||
name: uuids
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/response.Response'
|
||||
- properties:
|
||||
data:
|
||||
$ref: '#/definitions/model.PeerList'
|
||||
type: object
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/response.Response'
|
||||
security:
|
||||
- token: []
|
||||
summary: 我的设备列表
|
||||
tags:
|
||||
- 设备
|
||||
/admin/user/update:
|
||||
post:
|
||||
consumes:
|
||||
@@ -2867,6 +3643,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:
|
||||
|
||||
|
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 97 KiB |
@@ -1042,6 +1042,40 @@ const docTemplateapi = `{
|
||||
}
|
||||
},
|
||||
"/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": {
|
||||
"security": [
|
||||
{
|
||||
@@ -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": {
|
||||
|
||||
@@ -1035,6 +1035,40 @@
|
||||
}
|
||||
},
|
||||
"/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": {
|
||||
"security": [
|
||||
{
|
||||
@@ -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": {
|
||||
|
||||
@@ -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:
|
||||
@@ -850,6 +850,27 @@ paths:
|
||||
tags:
|
||||
- 群组
|
||||
/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:
|
||||
consumes:
|
||||
- application/json
|
||||
|
||||
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 97 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 13 KiB |
@@ -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:
|
||||
|
||||
3
go.mod
@@ -43,6 +43,7 @@ require (
|
||||
github.com/go-openapi/swag v0.19.15 // indirect
|
||||
github.com/go-sql-driver/mysql v1.7.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/inconshreveable/mousetrap v1.1.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/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // 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/v2 v2.0.6 // indirect
|
||||
github.com/spf13/afero v1.6.0 // indirect
|
||||
@@ -69,6 +71,7 @@ require (
|
||||
github.com/ugorji/go/codec v1.2.9 // indirect
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
|
||||
golang.org/x/crypto v0.23.0 // indirect
|
||||
golang.org/x/image v0.13.0 // indirect
|
||||
golang.org/x/net v0.25.0 // indirect
|
||||
golang.org/x/sys v0.25.0 // indirect
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
|
||||
|
||||
@@ -4,8 +4,8 @@ import (
|
||||
"Gwen/global"
|
||||
"Gwen/http/request/admin"
|
||||
"Gwen/http/response"
|
||||
"Gwen/model"
|
||||
"Gwen/service"
|
||||
"encoding/json"
|
||||
_ "encoding/json"
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
@@ -30,11 +30,6 @@ func (ct *AddressBook) Detail(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
iid, _ := strconv.Atoi(id)
|
||||
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 {
|
||||
response.Success(c, t)
|
||||
return
|
||||
@@ -66,9 +61,9 @@ func (ct *AddressBook) Create(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
t := f.ToAddressBook()
|
||||
u := service.AllService.UserService.CurUser(c)
|
||||
if !service.AllService.UserService.IsAdmin(u) || t.UserId == 0 {
|
||||
t.UserId = u.Id
|
||||
if t.UserId == 0 {
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||
return
|
||||
}
|
||||
if t.CollectionId > 0 && !service.AllService.AddressBookService.CheckCollectionOwner(t.UserId, t.CollectionId) {
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||
@@ -98,7 +93,7 @@ func (ct *AddressBook) Create(c *gin.Context) {
|
||||
// @Param body body admin.AddressBookForm true "地址簿信息"
|
||||
// @Success 200 {object} response.Response{data=model.AddressBook}
|
||||
// @Failure 500 {object} response.Response
|
||||
// @Router /admin/address_book/create [post]
|
||||
// @Router /admin/address_book/batchCreate [post]
|
||||
// @Security token
|
||||
func (ct *AddressBook) BatchCreate(c *gin.Context) {
|
||||
f := &admin.AddressBookForm{}
|
||||
@@ -111,9 +106,21 @@ func (ct *AddressBook) BatchCreate(c *gin.Context) {
|
||||
response.Fail(c, 101, errList[0])
|
||||
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 {
|
||||
continue
|
||||
}
|
||||
@@ -126,13 +133,13 @@ func (ct *AddressBook) BatchCreate(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
ts := f.ToAddressBooks()
|
||||
for _, t := range ts {
|
||||
if t.UserId == 0 {
|
||||
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 {
|
||||
service.AllService.AddressBookService.Create(t)
|
||||
}
|
||||
@@ -161,10 +168,6 @@ func (ct *AddressBook) List(c *gin.Context) {
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||
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) {
|
||||
tx.Preload("Collection", func(txc *gorm.DB) *gorm.DB {
|
||||
return txc.Select("id,name")
|
||||
@@ -190,11 +193,6 @@ func (ct *AddressBook) List(c *gin.Context) {
|
||||
for _, ab := range res.AddressBooks {
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -221,15 +219,15 @@ func (ct *AddressBook) Update(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
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
|
||||
}
|
||||
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) {
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||
return
|
||||
@@ -270,21 +268,12 @@ func (ct *AddressBook) Delete(c *gin.Context) {
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||
return
|
||||
}
|
||||
u := service.AllService.UserService.CurUser(c)
|
||||
if !service.AllService.UserService.IsAdmin(u) && t.UserId != u.Id {
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
|
||||
err := service.AllService.AddressBookService.Delete(t)
|
||||
if err == nil {
|
||||
response.Success(c, nil)
|
||||
return
|
||||
}
|
||||
if u.Id > 0 {
|
||||
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"))
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||
}
|
||||
|
||||
// ShareByWebClient
|
||||
@@ -327,3 +316,47 @@ 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)
|
||||
}
|
||||
|
||||
@@ -29,11 +29,6 @@ func (abc *AddressBookCollection) Detail(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
iid, _ := strconv.Atoi(id)
|
||||
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 {
|
||||
response.Success(c, t)
|
||||
return
|
||||
@@ -64,12 +59,11 @@ func (abc *AddressBookCollection) Create(c *gin.Context) {
|
||||
response.Fail(c, 101, errList[0])
|
||||
return
|
||||
}
|
||||
//t := f.ToAddressBookCollection()
|
||||
t := f
|
||||
u := service.AllService.UserService.CurUser(c)
|
||||
if !service.AllService.UserService.IsAdmin(u) || t.UserId == 0 {
|
||||
t.UserId = u.Id
|
||||
if f.UserId == 0 {
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||
return
|
||||
}
|
||||
t := f
|
||||
err := service.AllService.AddressBookService.CreateCollection(t)
|
||||
if err != nil {
|
||||
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())
|
||||
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) {
|
||||
if query.UserId > 0 {
|
||||
tx.Where("user_id = ?", query.UserId)
|
||||
@@ -137,11 +127,6 @@ func (abc *AddressBookCollection) Update(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
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)
|
||||
if err != nil {
|
||||
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])
|
||||
return
|
||||
}
|
||||
t := service.AllService.AddressBookService.CollectionInfoById(f.Id)
|
||||
u := service.AllService.UserService.CurUser(c)
|
||||
if !service.AllService.UserService.IsAdmin(u) && t.UserId != u.Id {
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
|
||||
ex := service.AllService.AddressBookService.CollectionInfoById(f.Id)
|
||||
if ex.Id == 0 {
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||
return
|
||||
}
|
||||
if u.Id > 0 {
|
||||
err := service.AllService.AddressBookService.DeleteCollection(t)
|
||||
if err == nil {
|
||||
response.Success(c, nil)
|
||||
return
|
||||
}
|
||||
response.Fail(c, 101, err.Error())
|
||||
err := service.AllService.AddressBookService.DeleteCollection(ex)
|
||||
if err == nil {
|
||||
response.Success(c, nil)
|
||||
return
|
||||
}
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||
}
|
||||
|
||||
@@ -35,10 +35,6 @@ func (abcr *AddressBookCollectionRule) List(c *gin.Context) {
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||
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) {
|
||||
if query.UserId > 0 {
|
||||
@@ -66,17 +62,11 @@ func (abcr *AddressBookCollectionRule) Detail(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
iid, _ := strconv.Atoi(id)
|
||||
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 {
|
||||
response.Success(c, t)
|
||||
return
|
||||
}
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||
return
|
||||
}
|
||||
|
||||
// Create 创建地址簿规则
|
||||
@@ -105,13 +95,8 @@ func (abcr *AddressBookCollectionRule) Create(c *gin.Context) {
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||
return
|
||||
}
|
||||
//t := f.ToAddressBookCollection()
|
||||
t := f
|
||||
u := service.AllService.UserService.CurUser(c)
|
||||
if t.UserId == 0 {
|
||||
t.UserId = u.Id
|
||||
}
|
||||
msg, res := abcr.CheckForm(u, t)
|
||||
msg, res := abcr.CheckForm(t)
|
||||
if !res {
|
||||
response.Fail(c, 101, response.TranslateMsg(c, msg))
|
||||
return
|
||||
@@ -124,9 +109,9 @@ func (abcr *AddressBookCollectionRule) Create(c *gin.Context) {
|
||||
response.Success(c, nil)
|
||||
}
|
||||
|
||||
func (abcr *AddressBookCollectionRule) CheckForm(u *model.User, t *model.AddressBookCollectionRule) (string, bool) {
|
||||
if !service.AllService.UserService.IsAdmin(u) && t.UserId != u.Id {
|
||||
return "NoAccess", false
|
||||
func (abcr *AddressBookCollectionRule) CheckForm(t *model.AddressBookCollectionRule) (string, bool) {
|
||||
if t.UserId == 0 {
|
||||
return "ParamsError", false
|
||||
}
|
||||
if t.CollectionId > 0 && !service.AllService.AddressBookService.CheckCollectionOwner(t.UserId, t.CollectionId) {
|
||||
return "ParamsError", false
|
||||
@@ -141,15 +126,7 @@ func (abcr *AddressBookCollectionRule) CheckForm(u *model.User, t *model.Address
|
||||
if tou.Id == 0 {
|
||||
return "ItemNotFound", false
|
||||
}
|
||||
//非管理员不能分享给非本组织用户
|
||||
if tou.GroupId != u.GroupId && !service.AllService.UserService.IsAdmin(u) {
|
||||
return "NoAccess", false
|
||||
}
|
||||
} 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)
|
||||
if tog.Id == 0 {
|
||||
return "ItemNotFound", false
|
||||
@@ -194,9 +171,8 @@ func (abcr *AddressBookCollectionRule) Update(c *gin.Context) {
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||
return
|
||||
}
|
||||
t := f //f.ToAddressBookCollection()
|
||||
u := service.AllService.UserService.CurUser(c)
|
||||
msg, res := abcr.CheckForm(u, t)
|
||||
t := f
|
||||
msg, res := abcr.CheckForm(t)
|
||||
if !res {
|
||||
response.Fail(c, 101, response.TranslateMsg(c, msg))
|
||||
return
|
||||
@@ -232,20 +208,15 @@ func (abcr *AddressBookCollectionRule) Delete(c *gin.Context) {
|
||||
response.Fail(c, 101, errList[0])
|
||||
return
|
||||
}
|
||||
t := service.AllService.AddressBookService.RuleInfoById(f.Id)
|
||||
u := service.AllService.UserService.CurUser(c)
|
||||
if !service.AllService.UserService.IsAdmin(u) && t.UserId != u.Id {
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
|
||||
ex := service.AllService.AddressBookService.RuleInfoById(f.Id)
|
||||
if ex.Id == 0 {
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||
return
|
||||
}
|
||||
if t.Id > 0 {
|
||||
err := service.AllService.AddressBookService.DeleteRule(t)
|
||||
if err == nil {
|
||||
response.Success(c, nil)
|
||||
return
|
||||
}
|
||||
response.Fail(c, 101, err.Error())
|
||||
err := service.AllService.AddressBookService.DeleteRule(ex)
|
||||
if err == nil {
|
||||
response.Success(c, nil)
|
||||
return
|
||||
}
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||
}
|
||||
|
||||
@@ -11,11 +11,135 @@ import (
|
||||
"Gwen/service"
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/mojocn/base64Captcha"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
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 登录
|
||||
// @Tags 登录
|
||||
// @Summary 登录
|
||||
@@ -30,22 +154,39 @@ type Login struct {
|
||||
func (ct *Login) Login(c *gin.Context) {
|
||||
f := &admin.Login{}
|
||||
err := c.ShouldBindJSON(f)
|
||||
clientIp := c.ClientIP()
|
||||
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())
|
||||
return
|
||||
}
|
||||
|
||||
errList := global.Validator.ValidStruct(c, f)
|
||||
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])
|
||||
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)
|
||||
|
||||
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"))
|
||||
return
|
||||
}
|
||||
@@ -54,13 +195,30 @@ func (ct *Login) Login(c *gin.Context) {
|
||||
UserId: u.Id,
|
||||
Client: model.LoginLogClientWebAdmin,
|
||||
Uuid: "", //must be empty
|
||||
Ip: c.ClientIP(),
|
||||
Ip: clientIp,
|
||||
Type: model.LoginLogTypeAccount,
|
||||
Platform: f.Platform,
|
||||
})
|
||||
|
||||
// 成功后清除记录
|
||||
loginLimiter.RemoveRecord(clientIp)
|
||||
|
||||
// 清理过期记录
|
||||
go loginLimiter.CleanupExpired()
|
||||
|
||||
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 登出
|
||||
// @Tags 登录
|
||||
@@ -90,10 +248,12 @@ func (ct *Login) Logout(c *gin.Context) {
|
||||
// @Failure 500 {object} response.ErrorResponse
|
||||
// @Router /admin/login-options [post]
|
||||
func (ct *Login) LoginOptions(c *gin.Context) {
|
||||
ip := c.ClientIP()
|
||||
ops := service.AllService.OauthService.GetOauthProviders()
|
||||
response.Success(c, gin.H{
|
||||
"ops": ops,
|
||||
"register": global.Config.App.Register,
|
||||
"ops": ops,
|
||||
"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)
|
||||
}
|
||||
|
||||
|
||||
func responseLoginSuccess(c *gin.Context, u *model.User, token string) {
|
||||
lp := &adResp.LoginPayload{}
|
||||
lp.FromUser(u)
|
||||
lp.Token = token
|
||||
lp.RouteNames = service.AllService.UserService.RouteNames(u)
|
||||
response.Success(c, lp)
|
||||
}
|
||||
}
|
||||
|
||||
271
http/controller/admin/my/addressBook.go
Normal 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)
|
||||
}
|
||||
162
http/controller/admin/my/addressBookCollection.go
Normal 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())
|
||||
}
|
||||
228
http/controller/admin/my/addressBookCollectionRule.go
Normal 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())
|
||||
}
|
||||
59
http/controller/admin/my/peer.go
Normal 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)
|
||||
}
|
||||
119
http/controller/admin/my/shareRecord.go
Normal 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)
|
||||
}
|
||||
176
http/controller/admin/my/tag.go
Normal 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
|
||||
}
|
||||
105
http/controller/admin/shareRecord.go
Normal 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)
|
||||
}
|
||||
@@ -64,9 +64,9 @@ func (ct *Tag) Create(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
t := f.ToTag()
|
||||
u := service.AllService.UserService.CurUser(c)
|
||||
if !service.AllService.UserService.IsAdmin(u) || t.UserId == 0 {
|
||||
t.UserId = u.Id
|
||||
if t.UserId == 0 {
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||
return
|
||||
}
|
||||
err := service.AllService.TagService.Create(t)
|
||||
if err != nil {
|
||||
@@ -96,10 +96,6 @@ func (ct *Tag) List(c *gin.Context) {
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||
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) {
|
||||
tx.Preload("Collection", func(txc *gorm.DB) *gorm.DB {
|
||||
return txc.Select("id,name")
|
||||
@@ -140,12 +136,12 @@ func (ct *Tag) Update(c *gin.Context) {
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||
return
|
||||
}
|
||||
t := f.ToTag()
|
||||
u := service.AllService.UserService.CurUser(c)
|
||||
if !service.AllService.UserService.IsAdmin(u) && t.UserId != u.Id {
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
|
||||
ex := service.AllService.TagService.InfoById(f.Id)
|
||||
if ex.Id == 0 {
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||
return
|
||||
}
|
||||
t := f.ToTag()
|
||||
err := service.AllService.TagService.Update(t)
|
||||
if err != nil {
|
||||
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])
|
||||
return
|
||||
}
|
||||
t := service.AllService.TagService.InfoById(f.Id)
|
||||
u := service.AllService.UserService.CurUser(c)
|
||||
if !service.AllService.UserService.IsAdmin(u) && t.UserId != u.Id {
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
|
||||
ex := service.AllService.TagService.InfoById(f.Id)
|
||||
if ex.Id == 0 {
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||
return
|
||||
}
|
||||
if u.Id > 0 {
|
||||
err := service.AllService.TagService.Delete(t)
|
||||
if err == nil {
|
||||
response.Success(c, nil)
|
||||
return
|
||||
}
|
||||
response.Fail(c, 101, err.Error())
|
||||
err := service.AllService.TagService.Delete(ex)
|
||||
if err == nil {
|
||||
response.Success(c, nil)
|
||||
return
|
||||
}
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||
response.Fail(c, 101, err.Error())
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
@@ -295,51 +294,6 @@ func (ct *User) MyOauth(c *gin.Context) {
|
||||
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
|
||||
func (ct *User) GroupUsers(c *gin.Context) {
|
||||
q := &admin.GroupUsersQuery{}
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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"`
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ type Login struct {
|
||||
Username string `json:"username" validate:"required" label:"用户名"`
|
||||
Password string `json:"password,omitempty" validate:"required" label:"密码"`
|
||||
Platform string `json:"platform" label:"平台"`
|
||||
Captcha string `json:"captcha,omitempty" label:"验证码"`
|
||||
}
|
||||
|
||||
type LoginLogQuery struct {
|
||||
|
||||
15
http/request/admin/shareRecord.go
Normal 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"`
|
||||
}
|
||||
@@ -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"`
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -2,7 +2,9 @@ package router
|
||||
|
||||
import (
|
||||
_ "Gwen/docs/admin"
|
||||
"Gwen/global"
|
||||
"Gwen/http/controller/admin"
|
||||
"Gwen/http/controller/admin/my"
|
||||
"Gwen/http/middleware"
|
||||
"github.com/gin-gonic/gin"
|
||||
swaggerFiles "github.com/swaggo/files"
|
||||
@@ -13,7 +15,9 @@ func Init(g *gin.Engine) {
|
||||
|
||||
//swagger
|
||||
//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")
|
||||
LoginBind(adg)
|
||||
@@ -34,17 +38,21 @@ 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"))
|
||||
}
|
||||
func LoginBind(rg *gin.RouterGroup) {
|
||||
cont := &admin.Login{}
|
||||
rg.POST("/login", cont.Login)
|
||||
rg.GET("/captcha", cont.Captcha)
|
||||
rg.POST("/logout", cont.Logout)
|
||||
rg.GET("/login-options", cont.LoginOptions)
|
||||
rg.POST("/oidc/auth", cont.OidcAuth)
|
||||
@@ -58,7 +66,7 @@ func UserBind(rg *gin.RouterGroup) {
|
||||
aR.GET("/current", cont.Current)
|
||||
aR.POST("/changeCurPwd", cont.ChangeCurPwd)
|
||||
aR.POST("/myOauth", cont.MyOauth)
|
||||
aR.GET("/myPeer", cont.MyPeer)
|
||||
//aR.GET("/myPeer", cont.MyPeer)
|
||||
aR.POST("/groupUsers", cont.GroupUsers)
|
||||
}
|
||||
aRP := rg.Group("/user").Use(middleware.AdminPrivilege())
|
||||
@@ -86,7 +94,7 @@ func GroupBind(rg *gin.RouterGroup) {
|
||||
}
|
||||
|
||||
func TagBind(rg *gin.RouterGroup) {
|
||||
aR := rg.Group("/tag")
|
||||
aR := rg.Group("/tag").Use(middleware.AdminPrivilege())
|
||||
{
|
||||
cont := &admin.Tag{}
|
||||
aR.GET("/list", cont.List)
|
||||
@@ -101,15 +109,17 @@ func AddressBookBind(rg *gin.RouterGroup) {
|
||||
aR := rg.Group("/address_book")
|
||||
{
|
||||
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)
|
||||
|
||||
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("/batchCreateFromPeers", cont.BatchCreateFromPeers)
|
||||
|
||||
}
|
||||
}
|
||||
func PeerBind(rg *gin.RouterGroup) {
|
||||
@@ -168,7 +178,7 @@ func AuditBind(rg *gin.RouterGroup) {
|
||||
afR.POST("/batchDelete", cont.BatchFileDelete)
|
||||
}
|
||||
func AddressBookCollectionBind(rg *gin.RouterGroup) {
|
||||
aR := rg.Group("/address_book_collection")
|
||||
aR := rg.Group("/address_book_collection").Use(middleware.AdminPrivilege())
|
||||
{
|
||||
cont := &admin.AddressBookCollection{}
|
||||
aR.GET("/list", cont.List)
|
||||
@@ -180,7 +190,7 @@ func AddressBookCollectionBind(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{}
|
||||
aR.GET("/list", cont.List)
|
||||
@@ -195,6 +205,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 +227,62 @@ func FileBind(rg *gin.RouterGroup) {
|
||||
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)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,7 +15,9 @@ func ApiInit(g *gin.Engine) {
|
||||
|
||||
//g.Use(middleware.Cors())
|
||||
//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")
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -123,3 +123,13 @@ other = "Share Group"
|
||||
description = "Register closed."
|
||||
one = "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."
|
||||
|
||||
144
resources/i18n/es.toml
Normal file
@@ -0,0 +1,144 @@
|
||||
[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."
|
||||
|
||||
[CaptchaRequired]
|
||||
description = "Captcha required."
|
||||
one = "Captcha requerido."
|
||||
other = "Captcha requerido."
|
||||
|
||||
[CaptchaError]
|
||||
description = "Captcha error."
|
||||
one = "Error de captcha."
|
||||
other = "Error de captcha."
|
||||
@@ -125,4 +125,14 @@ other = "공유 그룹"
|
||||
[RegisterClosed]
|
||||
description = "Register closed."
|
||||
one = "가입이 종료되었습니다."
|
||||
other = "가입이 종료되었습니다."
|
||||
other = "가입이 종료되었습니다."
|
||||
|
||||
[CaptchaRequired]
|
||||
description = "Captcha required."
|
||||
one = "Captcha가 필요합니다."
|
||||
other = "Captcha가 필요합니다."
|
||||
|
||||
[CaptchaError]
|
||||
description = "Captcha error."
|
||||
one = "Captcha 오류."
|
||||
other = "Captcha 오류."
|
||||
@@ -131,4 +131,14 @@ other = "Общая группа"
|
||||
[RegisterClosed]
|
||||
description = "Register closed."
|
||||
one = "Регистрация закрыта."
|
||||
other = "Регистрация закрыта."
|
||||
other = "Регистрация закрыта."
|
||||
|
||||
[CaptchaRequired]
|
||||
description = "Captcha required."
|
||||
one = "Требуется капча."
|
||||
other = "Требуется капча."
|
||||
|
||||
[CaptchaError]
|
||||
description = "Captcha error."
|
||||
one = "Ошибка капчи."
|
||||
other = "Ошибка капчи."
|
||||
@@ -124,4 +124,14 @@ other = "共享组"
|
||||
[RegisterClosed]
|
||||
description = "Register closed."
|
||||
one = "注册已关闭。"
|
||||
other = "注册已关闭。"
|
||||
other = "注册已关闭。"
|
||||
|
||||
[CaptchaRequired]
|
||||
description = "Captcha required."
|
||||
one = "需要验证码。"
|
||||
other = "需要验证码。"
|
||||
|
||||
[CaptchaError]
|
||||
description = "Captcha error."
|
||||
one = "验证码错误。"
|
||||
other = "验证码错误。"
|
||||
4
resources/web2/index.html
vendored
@@ -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=6"></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=6";
|
||||
scriptTag.src = "main.dart.js?v=df360f45";
|
||||
scriptTag.type = "application/javascript";
|
||||
document.body.append(scriptTag);
|
||||
}
|
||||
|
||||
3621
resources/web2/js/dist/index.js
vendored
184
resources/web2/js/dist/lang.js
vendored
@@ -697,7 +697,8 @@ export const lang = {
|
||||
"Upload folder": "",
|
||||
"Upload files": "",
|
||||
"Clipboard is synchronized": "",
|
||||
"Update client clipboard": ""
|
||||
"Update client clipboard": "",
|
||||
Untagged: ""
|
||||
}, hr: {
|
||||
Status: "Status",
|
||||
"Your Desktop": "Va\u0161a radna povr\u0161ina",
|
||||
@@ -1359,7 +1360,8 @@ Mo\u017Eete se povezati s drugim ure\u0111ajima, ali se drugi ure\u0111aji ne mo
|
||||
"Upload folder": "",
|
||||
"Upload files": "",
|
||||
"Clipboard is synchronized": "",
|
||||
"Update client clipboard": ""
|
||||
"Update client clipboard": "",
|
||||
Untagged: ""
|
||||
}, th: {
|
||||
Status: "\u0E2A\u0E16\u0E32\u0E19\u0E30",
|
||||
"Your Desktop": "\u0E2B\u0E19\u0E49\u0E32\u0E08\u0E2D\u0E02\u0E2D\u0E07\u0E04\u0E38\u0E13",
|
||||
@@ -2013,7 +2015,8 @@ Mo\u017Eete se povezati s drugim ure\u0111ajima, ali se drugi ure\u0111aji ne mo
|
||||
"Upload folder": "",
|
||||
"Upload files": "",
|
||||
"Clipboard is synchronized": "",
|
||||
"Update client clipboard": ""
|
||||
"Update client clipboard": "",
|
||||
Untagged: ""
|
||||
}, ru: {
|
||||
Status: "\u0421\u0442\u0430\u0442\u0443\u0441",
|
||||
"Your Desktop": "\u0412\u0430\u0448 \u0440\u0430\u0431\u043E\u0447\u0438\u0439 \u0441\u0442\u043E\u043B",
|
||||
@@ -2682,7 +2685,8 @@ Mo\u017Eete se povezati s drugim ure\u0111ajima, ali se drugi ure\u0111aji ne mo
|
||||
"Upload folder": "\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C \u043F\u0430\u043F\u043A\u0443",
|
||||
"Upload files": "\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C \u0444\u0430\u0439\u043B\u044B",
|
||||
"Clipboard is synchronized": "\u0411\u0443\u0444\u0435\u0440 \u043E\u0431\u043C\u0435\u043D\u0430 \u0441\u0438\u043D\u0445\u0440\u043E\u043D\u0438\u0437\u0438\u0440\u043E\u0432\u0430\u043D",
|
||||
"Update client clipboard": "\u041E\u0431\u043D\u043E\u0432\u0438\u0442\u044C \u0431\u0443\u0444\u0435\u0440 \u043E\u0431\u043C\u0435\u043D\u0430 \u043A\u043B\u0438\u0435\u043D\u0442\u0430"
|
||||
"Update client clipboard": "\u041E\u0431\u043D\u043E\u0432\u0438\u0442\u044C \u0431\u0443\u0444\u0435\u0440 \u043E\u0431\u043C\u0435\u043D\u0430 \u043A\u043B\u0438\u0435\u043D\u0442\u0430",
|
||||
Untagged: "\u0411\u0435\u0437 \u043C\u0435\u0442\u043A\u0438"
|
||||
}, eu: {
|
||||
Status: "Egoera",
|
||||
"Your Desktop": "Zure mahaigaina",
|
||||
@@ -3349,7 +3353,8 @@ Beste gailuetara konekta zaitezke, baina beste gailu batzuk ezin dira zure gailu
|
||||
"Upload folder": "",
|
||||
"Upload files": "",
|
||||
"Clipboard is synchronized": "",
|
||||
"Update client clipboard": ""
|
||||
"Update client clipboard": "",
|
||||
Untagged: ""
|
||||
}, el: {
|
||||
Status: "\u039A\u03B1\u03C4\u03AC\u03C3\u03C4\u03B1\u03C3\u03B7",
|
||||
"Your Desktop": "\u039F \u03C3\u03C4\u03B1\u03B8\u03BC\u03CC\u03C2 \u03B5\u03C1\u03B3\u03B1\u03C3\u03AF\u03B1\u03C2 \u03C3\u03B1\u03C2",
|
||||
@@ -4008,7 +4013,8 @@ Beste gailuetara konekta zaitezke, baina beste gailu batzuk ezin dira zure gailu
|
||||
"Upload folder": "",
|
||||
"Upload files": "",
|
||||
"Clipboard is synchronized": "",
|
||||
"Update client clipboard": ""
|
||||
"Update client clipboard": "",
|
||||
Untagged: ""
|
||||
}, et: {
|
||||
Status: "",
|
||||
"Your Desktop": "",
|
||||
@@ -4667,7 +4673,8 @@ Kui soovid juurdep\xE4\xE4su seadmele avalikus serveris, sisesta "<id>@public",
|
||||
"Upload folder": "",
|
||||
"Upload files": "",
|
||||
"Clipboard is synchronized": "",
|
||||
"Update client clipboard": ""
|
||||
"Update client clipboard": "",
|
||||
Untagged: ""
|
||||
}, sl: {
|
||||
Status: "Stanje",
|
||||
"Your Desktop": "Va\u0161e namizje",
|
||||
@@ -5333,7 +5340,8 @@ Lahko se pove\u017Eete na druge naprave, druge naprave pa se k vam ne morejo pov
|
||||
"Upload folder": "",
|
||||
"Upload files": "",
|
||||
"Clipboard is synchronized": "",
|
||||
"Update client clipboard": ""
|
||||
"Update client clipboard": "",
|
||||
Untagged: ""
|
||||
}, ko: {
|
||||
Status: "\uC0C1\uD0DC",
|
||||
"Your Desktop": "\uB0B4 \uB370\uC2A4\uD06C\uD0D1",
|
||||
@@ -5995,7 +6003,8 @@ Lahko se pove\u017Eete na druge naprave, druge naprave pa se k vam ne morejo pov
|
||||
"Upload folder": "\uD3F4\uB354 \uC5C5\uB85C\uB4DC",
|
||||
"Upload files": "\uD30C\uC77C \uC5C5\uB85C\uB4DC",
|
||||
"Clipboard is synchronized": "\uD074\uB9BD\uBCF4\uB4DC\uAC00 \uB3D9\uAE30\uD654\uB428",
|
||||
"Update client clipboard": ""
|
||||
"Update client clipboard": "\uD074\uB77C\uC774\uC5B8\uD2B8 \uD074\uB9BD\uBCF4\uB4DC \uC5C5\uB370\uC774\uD2B8",
|
||||
Untagged: "\uD0DC\uADF8 \uC5C6\uC74C"
|
||||
}, lv: {
|
||||
Status: "Statuss",
|
||||
"Your Desktop": "J\u016Bsu darbvirsma",
|
||||
@@ -6663,7 +6672,8 @@ Ja v\u0113laties piek\u013C\u016Bt ier\u012Bcei publiskaj\u0101 server\u012B, l\
|
||||
"Upload folder": "Aug\u0161upiel\u0101d\u0113t mapi",
|
||||
"Upload files": "Aug\u0161upiel\u0101d\u0113t failus",
|
||||
"Clipboard is synchronized": "Starpliktuve ir sinhroniz\u0113ta",
|
||||
"Update client clipboard": ""
|
||||
"Update client clipboard": "Atjaunin\u0101t klienta starpliktuvi",
|
||||
Untagged: "Neatz\u012Bm\u0113ts"
|
||||
}, pl: {
|
||||
Status: "Status",
|
||||
"Your Desktop": "Tw\xF3j pulpit",
|
||||
@@ -7330,7 +7340,8 @@ Je\u015Bli chcesz uzyska\u0107 dost\u0119p do urz\u0105dzenia na serwerze public
|
||||
"Upload folder": "Wy\u015Blij folder",
|
||||
"Upload files": "Wy\u015Blij pliki",
|
||||
"Clipboard is synchronized": "Schowek jest zsynchronizowany",
|
||||
"Update client clipboard": ""
|
||||
"Update client clipboard": "Uaktualnij schowek klienta",
|
||||
Untagged: ""
|
||||
}, pt_PT: {
|
||||
Status: "Estado",
|
||||
"Your Desktop": "Ambiente de Trabalho",
|
||||
@@ -7984,7 +7995,8 @@ Je\u015Bli chcesz uzyska\u0107 dost\u0119p do urz\u0105dzenia na serwerze public
|
||||
"Upload folder": "",
|
||||
"Upload files": "",
|
||||
"Clipboard is synchronized": "",
|
||||
"Update client clipboard": ""
|
||||
"Update client clipboard": "",
|
||||
Untagged: ""
|
||||
}, id: {
|
||||
Status: "Status",
|
||||
"Your Desktop": "Layar Utama",
|
||||
@@ -8650,7 +8662,8 @@ Untuk mengakses perangkat di server publik, cukup masukkan "<id>@public", tanpa
|
||||
"Upload folder": "",
|
||||
"Upload files": "",
|
||||
"Clipboard is synchronized": "",
|
||||
"Update client clipboard": ""
|
||||
"Update client clipboard": "",
|
||||
Untagged: ""
|
||||
}, ptbr: {
|
||||
Status: "Status",
|
||||
"Your Desktop": "Seu Computador",
|
||||
@@ -9314,7 +9327,8 @@ Voc\xEA pode se conectar a outros dispositivos, mas eles n\xE3o podem se conecta
|
||||
"Upload folder": "",
|
||||
"Upload files": "",
|
||||
"Clipboard is synchronized": "",
|
||||
"Update client clipboard": ""
|
||||
"Update client clipboard": "",
|
||||
Untagged: ""
|
||||
}, ja: {
|
||||
Status: "\u72B6\u614B",
|
||||
"Your Desktop": "\u3042\u306A\u305F\u306E\u30B3\u30F3\u30D4\u30E5\u30FC\u30BF\u30FC",
|
||||
@@ -9979,7 +9993,8 @@ QR\u30B3\u30FC\u30C9\u3092\u30B9\u30AD\u30E3\u30F3\u3057\u3001\u30A2\u30D7\u30EA
|
||||
"Upload folder": "",
|
||||
"Upload files": "",
|
||||
"Clipboard is synchronized": "",
|
||||
"Update client clipboard": ""
|
||||
"Update client clipboard": "",
|
||||
Untagged: ""
|
||||
}, he: {
|
||||
Status: "",
|
||||
"Your Desktop": "",
|
||||
@@ -10641,7 +10656,8 @@ QR\u30B3\u30FC\u30C9\u3092\u30B9\u30AD\u30E3\u30F3\u3057\u3001\u30A2\u30D7\u30EA
|
||||
"Upload folder": "",
|
||||
"Upload files": "",
|
||||
"Clipboard is synchronized": "",
|
||||
"Update client clipboard": ""
|
||||
"Update client clipboard": "",
|
||||
Untagged: ""
|
||||
}, bg: {
|
||||
Status: "\u041F\u043E\u043B\u043E\u0436\u0435\u043D\u0438\u0435",
|
||||
"Your Desktop": "\u0412\u0430\u0448\u0430\u0442\u0430 \u0440\u0430\u0431\u043E\u0442\u043D\u0430 \u0441\u0440\u0435\u0434\u0430",
|
||||
@@ -11302,7 +11318,8 @@ QR\u30B3\u30FC\u30C9\u3092\u30B9\u30AD\u30E3\u30F3\u3057\u3001\u30A2\u30D7\u30EA
|
||||
"Upload folder": "",
|
||||
"Upload files": "",
|
||||
"Clipboard is synchronized": "",
|
||||
"Update client clipboard": ""
|
||||
"Update client clipboard": "",
|
||||
Untagged: ""
|
||||
}, fa: {
|
||||
Status: "\u0648\u0636\u0639\u06CC\u062A",
|
||||
"Your Desktop": "\u062F\u0633\u06A9\u062A\u0627\u067E \u0634\u0645\u0627",
|
||||
@@ -11965,7 +11982,8 @@ QR\u30B3\u30FC\u30C9\u3092\u30B9\u30AD\u30E3\u30F3\u3057\u3001\u30A2\u30D7\u30EA
|
||||
"Upload folder": "",
|
||||
"Upload files": "",
|
||||
"Clipboard is synchronized": "",
|
||||
"Update client clipboard": ""
|
||||
"Update client clipboard": "",
|
||||
Untagged: ""
|
||||
}, README: {"ENG-KEY": "translation"}, de: {
|
||||
Status: "Status",
|
||||
"Your Desktop": "Ihr Desktop",
|
||||
@@ -12636,7 +12654,8 @@ Wenn Sie auf ein Ger\xE4t auf einem \xF6ffentlichen Server zugreifen wollen, geb
|
||||
"Upload folder": "Ordner hochladen",
|
||||
"Upload files": "Dateien hochladen",
|
||||
"Clipboard is synchronized": "Zwischenablage ist synchronisiert",
|
||||
"Update client clipboard": "Client-Zwischenablage aktualisieren"
|
||||
"Update client clipboard": "Client-Zwischenablage aktualisieren",
|
||||
Untagged: ""
|
||||
}, kz: {
|
||||
Status: "\u041A\u04AF\u0439",
|
||||
"Your Desktop": "\u0421\u0456\u0437\u0434\u0456\u04A3 \u0416\u04B1\u043C\u044B\u0441 \u04AF\u0441\u0442\u0435\u043B\u0456\u04A3\u0456\u0437",
|
||||
@@ -13290,7 +13309,8 @@ Wenn Sie auf ein Ger\xE4t auf einem \xF6ffentlichen Server zugreifen wollen, geb
|
||||
"Upload folder": "",
|
||||
"Upload files": "",
|
||||
"Clipboard is synchronized": "",
|
||||
"Update client clipboard": ""
|
||||
"Update client clipboard": "",
|
||||
Untagged: ""
|
||||
}, da: {
|
||||
Status: "Status",
|
||||
"Your Desktop": "Dit skrivebord",
|
||||
@@ -13957,7 +13977,8 @@ Du kan forbinde til andre enheder, men andre enheder kan ikke forbinde til din e
|
||||
"Upload folder": "",
|
||||
"Upload files": "",
|
||||
"Clipboard is synchronized": "",
|
||||
"Update client clipboard": ""
|
||||
"Update client clipboard": "",
|
||||
Untagged: ""
|
||||
}, nb: {
|
||||
Status: "Status",
|
||||
"Your Desktop": "Ditt skrivebord",
|
||||
@@ -14611,7 +14632,8 @@ Du kan forbinde til andre enheder, men andre enheder kan ikke forbinde til din e
|
||||
"Upload folder": "",
|
||||
"Upload files": "",
|
||||
"Clipboard is synchronized": "",
|
||||
"Update client clipboard": ""
|
||||
"Update client clipboard": "",
|
||||
Untagged: ""
|
||||
}, be: {
|
||||
Status: "\u0421\u0442\u0430\u0442\u0443\u0441",
|
||||
"Your Desktop": "\u0412\u0430\u0448 \u043F\u0440\u0430\u0446\u043E\u045E\u043D\u044B \u0441\u0442\u043E\u043B",
|
||||
@@ -15274,7 +15296,8 @@ Du kan forbinde til andre enheder, men andre enheder kan ikke forbinde til din e
|
||||
"Upload folder": "",
|
||||
"Upload files": "",
|
||||
"Clipboard is synchronized": "",
|
||||
"Update client clipboard": ""
|
||||
"Update client clipboard": "",
|
||||
Untagged: ""
|
||||
}, ca: {
|
||||
Status: "Estat",
|
||||
"Your Desktop": "Aquest ordinador",
|
||||
@@ -15940,7 +15963,8 @@ Si voleu accedir a un dispositiu en un servidor p\xFAblic, no cal que inseriu la
|
||||
"Upload folder": "",
|
||||
"Upload files": "",
|
||||
"Clipboard is synchronized": "",
|
||||
"Update client clipboard": ""
|
||||
"Update client clipboard": "",
|
||||
Untagged: ""
|
||||
}, eo: {
|
||||
Status: "Stato",
|
||||
"Your Desktop": "Via aparato",
|
||||
@@ -16594,7 +16618,8 @@ Si voleu accedir a un dispositiu en un servidor p\xFAblic, no cal que inseriu la
|
||||
"Upload folder": "",
|
||||
"Upload files": "",
|
||||
"Clipboard is synchronized": "",
|
||||
"Update client clipboard": ""
|
||||
"Update client clipboard": "",
|
||||
Untagged: ""
|
||||
}, ro: {
|
||||
Status: "Stare",
|
||||
"Your Desktop": "Desktopul t\u0103u",
|
||||
@@ -17250,7 +17275,8 @@ Hai s\u0103-\u021Bi g\u0103sim pe cineva cu care s\u0103 te conectezi, iar apoi
|
||||
"Upload folder": "",
|
||||
"Upload files": "",
|
||||
"Clipboard is synchronized": "",
|
||||
"Update client clipboard": ""
|
||||
"Update client clipboard": "",
|
||||
Untagged: ""
|
||||
}, tr: {
|
||||
Status: "Durum",
|
||||
"Your Desktop": "Sizin Masa\xFCst\xFCn\xFCz",
|
||||
@@ -17906,7 +17932,8 @@ Ba\u011Flanacak ve favorilere eklemek i\xE7in birini bulal\u0131m!`,
|
||||
"Upload folder": "",
|
||||
"Upload files": "",
|
||||
"Clipboard is synchronized": "",
|
||||
"Update client clipboard": ""
|
||||
"Update client clipboard": "",
|
||||
Untagged: ""
|
||||
}, vn: {
|
||||
Status: "Tr\u1EA1ng th\xE1i hi\u1EC7n t\u1EA1i",
|
||||
"Your Desktop": "Desktop c\u1EE7a b\u1EA1n",
|
||||
@@ -18563,7 +18590,8 @@ H\xE3y t\xECm ai \u0111\xF3 \u0111\u1EC3 k\u1EBFt n\u1ED1i c\xF9ng v\xE0 th\xEAm
|
||||
"Upload folder": "",
|
||||
"Upload files": "",
|
||||
"Clipboard is synchronized": "",
|
||||
"Update client clipboard": ""
|
||||
"Update client clipboard": "",
|
||||
Untagged: ""
|
||||
}, cn: {
|
||||
Status: "\u72B6\u6001",
|
||||
"Your Desktop": "\u4F60\u7684\u684C\u9762",
|
||||
@@ -19229,7 +19257,8 @@ H\xE3y t\xECm ai \u0111\xF3 \u0111\u1EC3 k\u1EBFt n\u1ED1i c\xF9ng v\xE0 th\xEAm
|
||||
"Upload folder": "\u4E0A\u4F20\u6587\u4EF6\u5939",
|
||||
"Upload files": "\u4E0A\u4F20\u6587\u4EF6",
|
||||
"Clipboard is synchronized": "\u526A\u8D34\u677F\u5DF2\u540C\u6B65",
|
||||
"Update client clipboard": "\u66F4\u65B0\u5BA2\u6237\u7AEF\u7684\u7C98\u8D34\u677F"
|
||||
"Update client clipboard": "\u66F4\u65B0\u5BA2\u6237\u7AEF\u7684\u7C98\u8D34\u677F",
|
||||
Untagged: "\u65E0\u6807\u7B7E"
|
||||
}, ar: {
|
||||
Status: "\u0627\u0644\u062D\u0627\u0644\u0629",
|
||||
"Your Desktop": "\u0633\u0637\u062D \u0645\u0643\u062A\u0628\u0643",
|
||||
@@ -19885,7 +19914,8 @@ H\xE3y t\xECm ai \u0111\xF3 \u0111\u1EC3 k\u1EBFt n\u1ED1i c\xF9ng v\xE0 th\xEAm
|
||||
"Upload folder": "",
|
||||
"Upload files": "",
|
||||
"Clipboard is synchronized": "",
|
||||
"Update client clipboard": ""
|
||||
"Update client clipboard": "",
|
||||
Untagged: ""
|
||||
}, it: {
|
||||
Status: "Stato",
|
||||
"Your Desktop": "Questo desktop",
|
||||
@@ -20568,7 +20598,8 @@ Se vuoi accedere ad un dispositivo in un server pubblico, inserisci "<id>@public
|
||||
"Upload folder": "Cartella upload",
|
||||
"Upload files": "File upload",
|
||||
"Clipboard is synchronized": "Gli appunti sono sincronizzati",
|
||||
"Update client clipboard": "Aggiorna appunti client"
|
||||
"Update client clipboard": "Aggiorna appunti client",
|
||||
Untagged: "Senza tag"
|
||||
}, sk: {
|
||||
Status: "Stav",
|
||||
"Your Desktop": "Va\u0161a plocha",
|
||||
@@ -21233,7 +21264,8 @@ M\xF4\u017Eete sa pripoji\u0165 k in\xFDm zariadeniam, ale in\xE9 zariadenia sa
|
||||
"Upload folder": "",
|
||||
"Upload files": "",
|
||||
"Clipboard is synchronized": "",
|
||||
"Update client clipboard": ""
|
||||
"Update client clipboard": "",
|
||||
Untagged: ""
|
||||
}, es: {
|
||||
Status: "Estado",
|
||||
"Your Desktop": "Tu escritorio",
|
||||
@@ -21902,7 +21934,8 @@ Si quieres accedder a un dispositivo en un servidor p\xFAblico, por favor, intro
|
||||
"Upload folder": "Subir carpeta",
|
||||
"Upload files": "Subir archivos",
|
||||
"Clipboard is synchronized": "Portapapeles sincronizado",
|
||||
"Update client clipboard": ""
|
||||
"Update client clipboard": "Actualizar portapapeles del cliente",
|
||||
Untagged: ""
|
||||
}, sr: {
|
||||
Status: "Status",
|
||||
"Your Desktop": "Va\u0161a radna povr\u0161ina",
|
||||
@@ -22556,7 +22589,8 @@ Si quieres accedder a un dispositivo en un servidor p\xFAblico, por favor, intro
|
||||
"Upload folder": "",
|
||||
"Upload files": "",
|
||||
"Clipboard is synchronized": "",
|
||||
"Update client clipboard": ""
|
||||
"Update client clipboard": "",
|
||||
Untagged: ""
|
||||
}, hu: {
|
||||
Status: "\xC1llapot",
|
||||
"Your Desktop": "Saj\xE1t asztal",
|
||||
@@ -23227,7 +23261,8 @@ Ha egy nyilv\xE1nos szerveren l\xE9v\u0151 eszk\xF6zh\xF6z szeretne hozz\xE1f\xE
|
||||
"Upload folder": "Mappa felt\xF6lt\xE9se",
|
||||
"Upload files": "F\xE1jlok felt\xF6lt\xE9se",
|
||||
"Clipboard is synchronized": "A v\xE1g\xF3lap szinkroniz\xE1lva van",
|
||||
"Update client clipboard": ""
|
||||
"Update client clipboard": "",
|
||||
Untagged: ""
|
||||
}, tw: {
|
||||
Status: "\u72C0\u614B",
|
||||
"Your Desktop": "\u60A8\u7684\u684C\u9762",
|
||||
@@ -23896,7 +23931,8 @@ Ha egy nyilv\xE1nos szerveren l\xE9v\u0151 eszk\xF6zh\xF6z szeretne hozz\xE1f\xE
|
||||
"Upload folder": "\u4E0A\u50B3\u8CC7\u6599\u593E",
|
||||
"Upload files": "\u4E0A\u50B3\u6A94\u6848",
|
||||
"Clipboard is synchronized": "\u526A\u8CBC\u7C3F\u5DF2\u540C\u6B65",
|
||||
"Update client clipboard": ""
|
||||
"Update client clipboard": "\u66F4\u65B0\u5BA2\u6236\u7AEF\u7684\u526A\u8CBC\u7C3F",
|
||||
Untagged: "\u7121\u6A19\u7C64"
|
||||
}, lt: {
|
||||
Status: "B\u016Bsena",
|
||||
"Your Desktop": "J\u016Bs\u0173 darbalaukis",
|
||||
@@ -24552,7 +24588,8 @@ Laikas suplanuoti nauj\u0105.`,
|
||||
"Upload folder": "",
|
||||
"Upload files": "",
|
||||
"Clipboard is synchronized": "",
|
||||
"Update client clipboard": ""
|
||||
"Update client clipboard": "",
|
||||
Untagged: ""
|
||||
}, en: {
|
||||
desk_tip: "Your desktop can be accessed with this ID and password.",
|
||||
connecting_status: "Connecting to the RustDesk network...",
|
||||
@@ -25458,7 +25495,8 @@ If you want to access a device on a public server, please input "<id>@public", t
|
||||
"Upload folder": "",
|
||||
"Upload files": "",
|
||||
"Clipboard is synchronized": "",
|
||||
"Update client clipboard": ""
|
||||
"Update client clipboard": "",
|
||||
Untagged: ""
|
||||
}, uk: {
|
||||
Status: "\u0421\u0442\u0430\u0442\u0443\u0441",
|
||||
"Your Desktop": "\u0412\u0430\u0448\u0430 \u0441\u0442\u0456\u043B\u044C\u043D\u0438\u0446\u044F",
|
||||
@@ -25579,9 +25617,9 @@ If you want to access a device on a public server, please input "<id>@public", t
|
||||
Original: "\u041E\u0440\u0438\u0433\u0456\u043D\u0430\u043B",
|
||||
Shrink: "\u0417\u043C\u0435\u043D\u0448\u0438\u0442\u0438",
|
||||
Stretch: "\u0420\u043E\u0437\u0442\u044F\u0433\u043D\u0443\u0442\u0438",
|
||||
Scrollbar: "\u0421\u043C\u0443\u0433\u0430 \u043F\u0440\u043E\u043A\u0440\u0443\u0442\u043A\u0438",
|
||||
ScrollAuto: "\u0410\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0447\u043D\u0430 \u043F\u0440\u043E\u043A\u0440\u0443\u0442\u043A\u0430",
|
||||
"Good image quality": "\u0425\u043E\u0440\u043E\u0448\u0430 \u044F\u043A\u0456\u0441\u0442\u044C \u0437\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u043D\u044F",
|
||||
Scrollbar: "\u0421\u043C\u0443\u0436\u043A\u0430 \u0433\u043E\u0440\u0442\u0430\u043D\u043D\u044F",
|
||||
ScrollAuto: "\u0410\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0447\u043D\u0435 \u0433\u043E\u0440\u0442\u0430\u043D\u043D\u044F",
|
||||
"Good image quality": "\u0413\u0430\u0440\u043D\u0430 \u044F\u043A\u0456\u0441\u0442\u044C \u0437\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u043D\u044F",
|
||||
Balanced: "\u0417\u0431\u0430\u043B\u0430\u043D\u0441\u043E\u0432\u0430\u043D\u0430",
|
||||
"Optimize reaction time": "\u041E\u043F\u0442\u0438\u043C\u0456\u0437\u0443\u0432\u0430\u0442\u0438 \u0447\u0430\u0441 \u0440\u0435\u0430\u043A\u0446\u0456\u0457",
|
||||
Custom: "\u041A\u043E\u0440\u0438\u0441\u0442\u0443\u0432\u0430\u0446\u044C\u043A\u0430",
|
||||
@@ -25606,7 +25644,7 @@ If you want to access a device on a public server, please input "<id>@public", t
|
||||
"OS Password": "\u041F\u0430\u0440\u043E\u043B\u044C \u041E\u0421",
|
||||
install_tip: "\u0427\u0435\u0440\u0435\u0437 UAC, \u0432 \u0434\u0435\u044F\u043A\u0438\u0445 \u0432\u0438\u043F\u0430\u0434\u043A\u0430\u0445 RustDesk \u043C\u043E\u0436\u0435 \u043F\u0440\u0430\u0446\u044E\u0432\u0430\u0442\u0438 \u043D\u0435\u043A\u043E\u0440\u0435\u043A\u0442\u043D\u043E \u043D\u0430 \u0432\u0456\u0434\u0434\u0430\u043B\u0435\u043D\u043E\u043C\u0443 \u0432\u0443\u0437\u043B\u0456. \u0429\u043E\u0431 \u0443\u043D\u0438\u043A\u043D\u0443\u0442\u0438 UAC, \u043D\u0430\u0442\u0438\u0441\u043D\u0456\u0442\u044C \u043A\u043D\u043E\u043F\u043A\u0443 \u043D\u0438\u0436\u0447\u0435 \u0434\u043B\u044F \u0432\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u043D\u044F RustDesk \u0432 \u0441\u0438\u0441\u0442\u0435\u043C\u0456",
|
||||
"Click to upgrade": "\u041D\u0430\u0442\u0438\u0441\u043D\u0456\u0442\u044C, \u0449\u043E\u0431 \u043F\u0435\u0440\u0435\u0432\u0456\u0440\u0438\u0442\u0438 \u043D\u0430\u044F\u0432\u043D\u0456\u0441\u0442\u044C \u043E\u043D\u043E\u0432\u043B\u0435\u043D\u044C",
|
||||
"Click to download": "\u041D\u0430\u0442\u0438\u0441\u043D\u0456\u0442\u044C, \u0449\u043E\u0431 \u0437\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0438\u0442\u0438",
|
||||
"Click to download": "\u041D\u0430\u0442\u0438\u0441\u043D\u0456\u0442\u044C, \u0449\u043E\u0431 \u043E\u0442\u0440\u0438\u043C\u0430\u0442\u0438",
|
||||
"Click to update": "\u041D\u0430\u0442\u0438\u0441\u043D\u0456\u0442\u044C, \u0449\u043E\u0431 \u043E\u043D\u043E\u0432\u0438\u0442\u0438",
|
||||
Configure: "\u041D\u0430\u043B\u0430\u0448\u0442\u0443\u0432\u0430\u0442\u0438",
|
||||
config_acc: '\u0414\u043B\u044F \u0432\u0456\u0434\u0434\u0430\u043B\u0435\u043D\u043E\u0433\u043E \u043A\u0435\u0440\u0443\u0432\u0430\u043D\u043D\u044F \u0432\u0430\u0448\u043E\u044E \u0441\u0442\u0456\u043B\u044C\u043D\u0438\u0446\u0435\u044E, \u0432\u0430\u043C \u043D\u0435\u043E\u0431\u0445\u0456\u0434\u043D\u043E \u043D\u0430\u0434\u0430\u0442\u0438 RustDesk \u0434\u043E\u0437\u0432\u043E\u043B\u0438 "\u0421\u043F\u0435\u0446\u0456\u0430\u043B\u044C\u043D\u0456 \u043C\u043E\u0436\u043B\u0438\u0432\u043E\u0441\u0442\u0456"',
|
||||
@@ -25658,10 +25696,10 @@ If you want to access a device on a public server, please input "<id>@public", t
|
||||
"Please enter the folder name": "\u0411\u0443\u0434\u044C \u043B\u0430\u0441\u043A\u0430, \u0432\u0432\u0435\u0434\u0456\u0442\u044C \u043D\u0430\u0437\u0432\u0443 \u0434\u043B\u044F \u0442\u0435\u043A\u0438",
|
||||
"Fix it": "\u0412\u0438\u043F\u0440\u0430\u0432\u0438\u0442\u0438",
|
||||
Warning: "\u041F\u043E\u043F\u0435\u0440\u0435\u0434\u0436\u0435\u043D\u043D\u044F",
|
||||
"Login screen using Wayland is not supported": "\u0412\u0445\u0456\u0434 \u0432 \u0441\u0438\u0441\u0442\u0435\u043C\u0443 \u0437 \u0432\u0438\u043A\u043E\u0440\u0438\u0441\u0442\u0430\u043D\u043D\u044F\u043C Wayland \u043D\u0435 \u043F\u0456\u0434\u0442\u0440\u0438\u043C\u0443\u0454\u0442\u044C\u0441\u044F",
|
||||
"Login screen using Wayland is not supported": "\u0415\u043A\u0440\u0430\u043D \u0432\u0445\u043E\u0434\u0443, \u044F\u043A\u0438\u0439 \u0432\u0438\u043A\u043E\u0440\u0438\u0441\u0442\u043E\u0432\u0443\u0454 Wayland, \u043D\u0435 \u043F\u0456\u0434\u0442\u0440\u0438\u043C\u0443\u0454\u0442\u044C\u0441\u044F",
|
||||
"Reboot required": "\u041F\u043E\u0442\u0440\u0456\u0431\u043D\u0435 \u043F\u0435\u0440\u0435\u0437\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0435\u043D\u043D\u044F",
|
||||
"Unsupported display server": "\u0413\u0440\u0430\u0444\u0456\u0447\u043D\u0438\u0439 \u0441\u0435\u0440\u0432\u0435\u0440 \u043D\u0435 \u043F\u0456\u0434\u0442\u0440\u0438\u043C\u0443\u0454\u0442\u044C\u0441\u044F",
|
||||
"x11 expected": "\u041E\u0447\u0456\u043A\u0443\u0454\u0442\u044C\u0441\u044F X11",
|
||||
"x11 expected": "\u041F\u043E\u0442\u0440\u0456\u0431\u0435\u043D X11",
|
||||
Port: "\u041F\u043E\u0440\u0442",
|
||||
Settings: "\u041D\u0430\u043B\u0430\u0448\u0442\u0443\u0432\u0430\u043D\u043D\u044F",
|
||||
Username: "\u0406\u043C\u02BC\u044F \u043A\u043E\u0440\u0438\u0441\u0442\u0443\u0432\u0430\u0447\u0430",
|
||||
@@ -25679,21 +25717,21 @@ If you want to access a device on a public server, please input "<id>@public", t
|
||||
"Verification code": "\u041A\u043E\u0434 \u043F\u0456\u0434\u0442\u0432\u0435\u0440\u0434\u0436\u0435\u043D\u043D\u044F",
|
||||
verification_tip: "\u041A\u043E\u0434 \u043F\u0456\u0434\u0442\u0432\u0435\u0440\u0434\u0436\u0435\u043D\u043D\u044F \u043D\u0430\u0434\u0456\u0441\u043B\u0430\u043D\u043E \u043D\u0430 \u0437\u0430\u0440\u0435\u0454\u0441\u0442\u0440\u043E\u0432\u0430\u043D\u0443 email-\u0430\u0434\u0440\u0435\u0441\u0443, \u0432\u0432\u0435\u0434\u0456\u0442\u044C \u043A\u043E\u0434 \u043F\u0456\u0434\u0442\u0432\u0435\u0440\u0434\u0436\u0435\u043D\u043D\u044F \u0434\u043B\u044F \u043F\u0440\u043E\u0434\u043E\u0432\u0436\u0435\u043D\u043D\u044F \u0430\u0432\u0442\u043E\u0440\u0438\u0437\u0430\u0446\u0456\u0457.",
|
||||
Logout: "\u0412\u0438\u0439\u0442\u0438",
|
||||
Tags: "\u0422\u0435\u0433\u0438",
|
||||
Tags: "\u041C\u0456\u0442\u043A\u0438",
|
||||
"Search ID": "\u041F\u043E\u0448\u0443\u043A \u0437\u0430 ID",
|
||||
whitelist_sep: "\u0412\u0456\u0434\u043E\u043A\u0440\u0435\u043C\u043B\u0435\u043D\u043D\u044F \u043A\u043E\u043C\u043E\u044E, \u043A\u0440\u0430\u043F\u043A\u043E\u044E \u0437 \u043A\u043E\u043C\u043E\u044E, \u043F\u0440\u043E\u043F\u0443\u0441\u043A\u043E\u043C \u0430\u0431\u043E \u043D\u043E\u0432\u0438\u043C \u0440\u044F\u0434\u043A\u043E\u043C",
|
||||
"Add ID": "\u0414\u043E\u0434\u0430\u0442\u0438 ID",
|
||||
"Add Tag": "\u0414\u043E\u0434\u0430\u0442\u0438 \u043A\u043B\u044E\u0447\u043E\u0432\u0435 \u0441\u043B\u043E\u0432\u043E",
|
||||
"Unselect all tags": "\u0421\u043A\u0430\u0441\u0443\u0432\u0430\u0442\u0438 \u0432\u0438\u0431\u0456\u0440 \u0443\u0441\u0456\u0445 \u0442\u0435\u0433\u0456\u0432",
|
||||
"Add Tag": "\u0414\u043E\u0434\u0430\u0442\u0438 \u043C\u0456\u0442\u043A\u0443",
|
||||
"Unselect all tags": "\u0421\u043A\u0430\u0441\u0443\u0432\u0430\u0442\u0438 \u0432\u0438\u0431\u0456\u0440 \u0443\u0441\u0456\u0445 \u043C\u0456\u0442\u043E\u043A",
|
||||
"Network error": "\u041F\u043E\u043C\u0438\u043B\u043A\u0430 \u043C\u0435\u0440\u0435\u0436\u0456",
|
||||
"Username missed": "\u0406\u043C\u02BC\u044F \u043A\u043E\u0440\u0438\u0441\u0442\u0443\u0432\u0430\u0447\u0430 \u0432\u0456\u0434\u0441\u0443\u0442\u043D\u0454",
|
||||
"Password missed": "\u041F\u0430\u0440\u043E\u043B\u044C \u0432\u0456\u0434\u0441\u0443\u0442\u043D\u0456\u0439",
|
||||
"Wrong credentials": "\u041D\u0435\u043F\u0440\u0430\u0432\u0438\u043B\u044C\u043D\u0456 \u0434\u0430\u043D\u0456",
|
||||
"The verification code is incorrect or has expired": "\u041A\u043E\u0434 \u043F\u0456\u0434\u0442\u0432\u0435\u0440\u0434\u0436\u0435\u043D\u043D\u044F \u043D\u0435\u043A\u043E\u0440\u0435\u043A\u0442\u043D\u0438\u0439 \u0430\u0431\u043E \u043F\u0440\u043E\u0442\u0435\u0440\u043C\u0456\u043D\u043E\u0432\u0430\u043D\u0438\u0439",
|
||||
"Edit Tag": "\u0420\u0435\u0434\u0430\u0433\u0443\u0432\u0430\u0442\u0438 \u0442\u0435\u0433",
|
||||
"Edit Tag": "\u0420\u0435\u0434\u0430\u0433\u0443\u0432\u0430\u0442\u0438 \u043C\u0456\u0442\u043A\u0443",
|
||||
"Forget Password": "\u041D\u0435 \u0437\u0431\u0435\u0440\u0456\u0433\u0430\u0442\u0438 \u043F\u0430\u0440\u043E\u043B\u044C",
|
||||
Favorites: "\u0412\u0438\u0431\u0440\u0430\u043D\u0435",
|
||||
"Add to Favorites": "\u0414\u043E\u0434\u0430\u0442\u0438 \u0432 \u043E\u0431\u0440\u0430\u043D\u0435",
|
||||
"Add to Favorites": "\u0414\u043E\u0434\u0430\u0442\u0438 \u0434\u043E \u043E\u0431\u0440\u0430\u043D\u043E\u0433\u043E",
|
||||
"Remove from Favorites": "\u0412\u0438\u0434\u0430\u043B\u0438\u0442\u0438 \u0437 \u043E\u0431\u0440\u0430\u043D\u043E\u0433\u043E",
|
||||
Empty: "\u041F\u0443\u0441\u0442\u043E",
|
||||
"Invalid folder name": "\u041D\u0435\u043F\u0440\u0438\u043F\u0443\u0441\u0442\u0438\u043C\u0430 \u043D\u0430\u0437\u0432\u0430 \u0442\u0435\u043A\u0438",
|
||||
@@ -25705,7 +25743,7 @@ If you want to access a device on a public server, please input "<id>@public", t
|
||||
Paste: "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u0438",
|
||||
"Paste here?": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u0438 \u0441\u044E\u0434\u0438?",
|
||||
"Are you sure to close the connection?": "\u0412\u0438 \u0432\u043F\u0435\u0432\u043D\u0435\u043D\u0456, \u0449\u043E \u0445\u043E\u0447\u0435\u0442\u0435 \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u0438 \u043F\u0456\u0434\u043A\u043B\u044E\u0447\u0435\u043D\u043D\u044F?",
|
||||
"Download new version": "\u0417\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0442\u0435 \u043D\u043E\u0432\u0443 \u0432\u0435\u0440\u0441\u0456\u044E",
|
||||
"Download new version": "\u0417\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0438\u0442\u0438 \u043D\u043E\u0432\u0443 \u0432\u0435\u0440\u0441\u0456\u044E",
|
||||
"Touch mode": "\u0421\u0435\u043D\u0441\u043E\u0440\u043D\u0438\u0439 \u0440\u0435\u0436\u0438\u043C",
|
||||
"Mouse mode": "\u0420\u0435\u0436\u0438\u043C \u043C\u0438\u0448\u0456",
|
||||
"One-Finger Tap": "\u0414\u043E\u0442\u0438\u043A \u043E\u0434\u043D\u0438\u043C \u043F\u0430\u043B\u044C\u0446\u0435\u043C",
|
||||
@@ -25738,7 +25776,7 @@ If you want to access a device on a public server, please input "<id>@public", t
|
||||
"Screen Connection": "\u041F\u0456\u0434\u043A\u043B\u044E\u0447\u0435\u043D\u043D\u044F \u0435\u043A\u0440\u0430\u043D\u0430",
|
||||
"Do you accept?": "\u0412\u0438 \u0437\u0433\u043E\u0434\u043D\u0456?",
|
||||
"Open System Setting": "\u0412\u0456\u0434\u043A\u0440\u0438\u0442\u0438 \u043D\u0430\u043B\u0430\u0448\u0442\u0443\u0432\u0430\u043D\u043D\u044F \u0441\u0438\u0441\u0442\u0435\u043C\u0438",
|
||||
"How to get Android input permission?": "\u042F\u043A \u043E\u0442\u0440\u0438\u043C\u0430\u0442\u0438 \u0434\u043E\u0437\u0432\u0456\u043B \u043D\u0430 \u0432\u0432\u0435\u0434\u0435\u043D\u043D\u044F Android?",
|
||||
"How to get Android input permission?": "\u042F\u043A \u043E\u0442\u0440\u0438\u043C\u0430\u0442\u0438 \u0434\u043E\u0437\u0432\u0456\u043B \u043D\u0430 \u0432\u0432\u0435\u0434\u0435\u043D\u043D\u044F \u0432 Android?",
|
||||
android_input_permission_tip1: '\u0414\u043B\u044F \u0442\u043E\u0433\u043E, \u0449\u043E\u0431 \u0432\u0456\u0434\u0434\u0430\u043B\u0435\u043D\u0438\u0439 \u043F\u0440\u0438\u0441\u0442\u0440\u0456\u0439 \u043C\u0456\u0433 \u043A\u0435\u0440\u0443\u0432\u0430\u0442\u0438 \u0432\u0430\u0448\u0438\u043C Android-\u043F\u0440\u0438\u0441\u0442\u0440\u043E\u0454\u043C \u0437\u0430 \u0434\u043E\u043F\u043E\u043C\u043E\u0433\u043E\u044E \u043C\u0438\u0448\u0456 \u0430\u0431\u043E \u0434\u043E\u0442\u0438\u043A\u0443, \u0432\u0430\u043C \u043D\u0435\u043E\u0431\u0445\u0456\u0434\u043D\u043E \u0434\u043E\u0437\u0432\u043E\u043B\u0438\u0442\u0438 RustDesk \u0432\u0438\u043A\u043E\u0440\u0438\u0441\u0442\u043E\u0432\u0443\u0432\u0430\u0442\u0438 \u0441\u043B\u0443\u0436\u0431\u0443 "\u0421\u043F\u0435\u0446\u0456\u0430\u043B\u044C\u043D\u0456 \u043C\u043E\u0436\u043B\u0438\u0432\u043E\u0441\u0442\u0456".',
|
||||
android_input_permission_tip2: "\u0411\u0443\u0434\u044C \u043B\u0430\u0441\u043A\u0430, \u043F\u0435\u0440\u0435\u0439\u0434\u0456\u0442\u044C \u043D\u0430 \u043D\u0430\u0441\u0442\u0443\u043F\u043D\u0443 \u0441\u0442\u043E\u0440\u0456\u043D\u043A\u0443 \u0441\u0438\u0441\u0442\u0435\u043C\u043D\u0438\u0445 \u043D\u0430\u043B\u0430\u0448\u0442\u0443\u0432\u0430\u043D\u044C, \u0437\u043D\u0430\u0439\u0434\u0456\u0442\u044C \u0442\u0430 \u0443\u0432\u0456\u0439\u0434\u0456\u0442\u044C \u0443 [\u0412\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u0456 \u0441\u043B\u0443\u0436\u0431\u0438], \u0443\u0432\u0456\u043C\u043A\u043D\u0456\u0442\u044C \u0441\u043B\u0443\u0436\u0431\u0443 [RustDesk Input].",
|
||||
android_new_connection_tip: "\u041E\u0442\u0440\u0438\u043C\u0430\u043D\u043E \u043D\u043E\u0432\u0438\u0439 \u0437\u0430\u043F\u0438\u0442 \u043D\u0430 \u043A\u0435\u0440\u0443\u0432\u0430\u043D\u043D\u044F \u0432\u0430\u0448\u0438\u043C \u043F\u043E\u0442\u043E\u0447\u043D\u0438\u043C \u043F\u0440\u0438\u0441\u0442\u0440\u043E\u0454\u043C.",
|
||||
@@ -25766,7 +25804,7 @@ If you want to access a device on a public server, please input "<id>@public", t
|
||||
"Ignore Battery Optimizations": "\u0406\u0433\u043D\u043E\u0440\u0443\u0432\u0430\u0442\u0438 \u043E\u043F\u0442\u0438\u043C\u0456\u0437\u0430\u0446\u0456\u0457 \u0431\u0430\u0442\u0430\u0440\u0435\u0457",
|
||||
android_open_battery_optimizations_tip: "\u041F\u0435\u0440\u0435\u0439\u0434\u0456\u0442\u044C \u043D\u0430 \u043D\u0430\u0441\u0442\u0443\u043F\u043D\u0443 \u0441\u0442\u043E\u0440\u0456\u043D\u043A\u0443 \u043D\u0430\u043B\u0430\u0448\u0442\u0443\u0432\u0430\u043D\u044C",
|
||||
"Start on boot": "\u0410\u0432\u0442\u043E\u0437\u0430\u043F\u0443\u0441\u043A",
|
||||
"Start the screen sharing service on boot, requires special permissions": "\u0417\u0430\u043F\u0443\u0441\u0442\u0438\u0442\u0438 \u0441\u043B\u0443\u0436\u0431\u0443 \u0441\u043B\u0443\u0436\u0431\u0443 \u0441\u043F\u0456\u043B\u044C\u043D\u043E\u0433\u043E \u0434\u043E\u0441\u0442\u0443\u043F\u0443 \u0434\u043E \u0435\u043A\u0440\u0430\u043D\u0430 \u043F\u0456\u0434 \u0447\u0430\u0441 \u0437\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0435\u043D\u043D\u044F, \u043F\u043E\u0442\u0440\u0435\u0431\u0443\u0454 \u0441\u043F\u0435\u0446\u0456\u0430\u043B\u044C\u043D\u0438\u0445 \u0434\u043E\u0437\u0432\u043E\u043B\u0456\u0432",
|
||||
"Start the screen sharing service on boot, requires special permissions": "\u0417\u0430\u043F\u0443\u0441\u043A\u0430\u0442\u0438 \u0441\u043B\u0443\u0436\u0431\u0443 \u0441\u043F\u0456\u043B\u044C\u043D\u043E\u0433\u043E \u0434\u043E\u0441\u0442\u0443\u043F\u0443 \u0434\u043E \u0435\u043A\u0440\u0430\u043D\u0430 \u043F\u0456\u0434 \u0447\u0430\u0441 \u0437\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0435\u043D\u043D\u044F, \u043F\u043E\u0442\u0440\u0435\u0431\u0443\u0454 \u0441\u043F\u0435\u0446\u0456\u0430\u043B\u044C\u043D\u0438\u0445 \u0434\u043E\u0437\u0432\u043E\u043B\u0456\u0432",
|
||||
"Connection not allowed": "\u041F\u0456\u0434\u043A\u043B\u044E\u0447\u0435\u043D\u043D\u044F \u043D\u0435 \u0434\u043E\u0437\u0432\u043E\u043B\u0435\u043D\u043E",
|
||||
"Legacy mode": "\u0417\u0430\u0441\u0442\u0430\u0440\u0456\u043B\u0438\u0439 \u0440\u0435\u0436\u0438\u043C",
|
||||
"Map mode": "\u0420\u0435\u0436\u0438\u043C \u043A\u0430\u0440\u0442\u0438",
|
||||
@@ -25788,15 +25826,15 @@ If you want to access a device on a public server, please input "<id>@public", t
|
||||
"Display Settings": "\u041D\u0430\u043B\u0430\u0448\u0442\u0443\u0432\u0430\u043D\u043D\u044F \u0434\u0438\u0441\u043F\u043B\u0435\u044E",
|
||||
Ratio: "\u0421\u043F\u0456\u0432\u0432\u0456\u0434\u043D\u043E\u0448\u0435\u043D\u043D\u044F",
|
||||
"Image Quality": "\u042F\u043A\u0456\u0441\u0442\u044C \u0437\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u043D\u044F",
|
||||
"Scroll Style": "\u0421\u0442\u0438\u043B\u044C \u043F\u0440\u043E\u043A\u0440\u0443\u0442\u043A\u0438",
|
||||
"Scroll Style": "\u0421\u0442\u0438\u043B\u044C \u0433\u043E\u0440\u0442\u0430\u043D\u043D\u044F",
|
||||
"Show Toolbar": "\u041F\u043E\u043A\u0430\u0437\u0430\u0442\u0438 \u043F\u0430\u043D\u0435\u043B\u044C \u0456\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442\u0456\u0432",
|
||||
"Hide Toolbar": "\u041F\u0440\u0438\u0445\u043E\u0432\u0430\u0442\u0438 \u043F\u0430\u043D\u0435\u043B\u044C \u0456\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442\u0456\u0432",
|
||||
"Direct Connection": "\u041F\u0440\u044F\u043C\u0435 \u043F\u0456\u0434\u043A\u043B\u044E\u0447\u0435\u043D\u043D\u044F",
|
||||
"Relay Connection": "\u0420\u0435\u0442\u0440\u0430\u043D\u0441\u043B\u044C\u043E\u0432\u0430\u043D\u0435 \u043F\u0456\u0434\u043A\u043B\u044E\u0447\u0435\u043D\u043D\u044F",
|
||||
"Secure Connection": "\u0411\u0435\u0437\u043F\u0435\u0447\u043D\u0435 \u043F\u0456\u0434\u043A\u043B\u044E\u0447\u0435\u043D\u043D\u044F",
|
||||
"Insecure Connection": "\u041D\u0435\u0431\u0435\u0437\u043F\u0435\u0447\u043D\u0435 \u043F\u0456\u0434\u043A\u043B\u044E\u0447\u0435\u043D\u043D\u044F",
|
||||
"Scale original": "\u041E\u0440\u0438\u0433\u0456\u043D\u0430\u043B \u043C\u0430\u0441\u0448\u0442\u0430\u0431\u0443",
|
||||
"Scale adaptive": "\u041C\u0430\u0441\u0448\u0442\u0430\u0431 \u0430\u0434\u0430\u043F\u0442\u0438\u0432\u043D\u0438\u0439",
|
||||
"Scale original": "\u041E\u0440\u0438\u0433\u0456\u043D\u0430\u043B\u044C\u043D\u0438\u0439 \u043C\u0430\u0441\u0448\u0442\u0430\u0431",
|
||||
"Scale adaptive": "\u0410\u0434\u0430\u043F\u0442\u0438\u0432\u043D\u0438\u0439 \u043C\u0430\u0441\u0448\u0442\u0430\u0431",
|
||||
General: "\u0417\u0430\u0433\u0430\u043B\u044C\u043D\u0456",
|
||||
Security: "\u0411\u0435\u0437\u043F\u0435\u043A\u0430",
|
||||
Theme: "\u0422\u0435\u043C\u0430",
|
||||
@@ -25861,7 +25899,7 @@ If you want to access a device on a public server, please input "<id>@public", t
|
||||
"Hide connection management window": "\u041F\u0440\u0438\u0445\u043E\u0432\u0430\u0442\u0438 \u0432\u0456\u043A\u043D\u043E \u043A\u0435\u0440\u0443\u0432\u0430\u043D\u043D\u044F \u043F\u0456\u0434\u043A\u043B\u044E\u0447\u0435\u043D\u043D\u044F\u043C\u0438",
|
||||
hide_cm_tip: "\u0414\u043E\u0437\u0432\u043E\u043B\u0435\u043D\u043E \u043F\u0440\u0438\u0445\u043E\u0432\u0430\u0442\u0438 \u043B\u0438\u0448\u0435 \u044F\u043A\u0449\u043E \u0441\u0435\u0430\u043D\u0441 \u043F\u0456\u0434\u0442\u0432\u0435\u0440\u0434\u0436\u0443\u0454\u0442\u044C\u0441\u044F \u043F\u043E\u0441\u0442\u0456\u0439\u043D\u0438\u043C \u043F\u0430\u0440\u043E\u043B\u0435\u043C",
|
||||
wayland_experiment_tip: "\u041F\u0456\u0434\u0442\u0440\u0438\u043C\u043A\u0430 Wayland \u043D\u0430 \u0435\u043A\u0441\u043F\u0435\u0440\u0438\u043C\u0435\u043D\u0442\u0430\u043B\u044C\u043D\u0456\u0439 \u0441\u0442\u0430\u0434\u0456\u0457, \u0431\u0443\u0434\u044C \u043B\u0430\u0441\u043A\u0430, \u0432\u0438\u043A\u043E\u0440\u0438\u0441\u0442\u043E\u0432\u0443\u0439\u0442\u0435 X11, \u044F\u043A\u0449\u043E \u043D\u0435\u043E\u0431\u0445\u0456\u0434\u043D\u0438\u0439 \u0430\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0447\u043D\u0438\u0439 \u0434\u043E\u0441\u0442\u0443\u043F.",
|
||||
"Right click to select tabs": "\u041F\u0440\u0430\u0432\u0438\u0439 \u043A\u043B\u0456\u043A \u0434\u043B\u044F \u0432\u0438\u0431\u043E\u0440\u0443 \u0432\u043A\u043B\u0430\u0434\u043A\u0438",
|
||||
"Right click to select tabs": "\u0412\u0438\u0431\u0456\u0440 \u0432\u043A\u043B\u0430\u0434\u043E\u043A \u043A\u043B\u0430\u0446\u0430\u043D\u043D\u044F\u043C \u043F\u0440\u0430\u0432\u043E\u044E",
|
||||
Skipped: "\u041F\u0440\u043E\u043F\u0443\u0449\u0435\u043D\u043E",
|
||||
"Add to address book": "\u0414\u043E\u0434\u0430\u0442\u0438 IP \u0434\u043E \u0410\u0434\u0440\u0435\u0441\u043D\u043E\u0457 \u043A\u043D\u0438\u0433\u0438",
|
||||
Group: "\u0413\u0440\u0443\u043F\u0430",
|
||||
@@ -25975,7 +26013,7 @@ If you want to access a device on a public server, please input "<id>@public", t
|
||||
Stop: "\u0417\u0443\u043F\u0438\u043D\u0438\u0442\u0438",
|
||||
exceed_max_devices: "\u0423 \u0432\u0430\u0441 \u043C\u0430\u043A\u0441\u0438\u043C\u0430\u043B\u044C\u043D\u0430 \u043A\u0456\u043B\u044C\u043A\u0456\u0441\u0442\u044C \u043A\u0435\u0440\u043E\u0432\u0430\u043D\u0438\u0445 \u043F\u0440\u0438\u0441\u0442\u0440\u043E\u0457\u0432.",
|
||||
"Sync with recent sessions": "\u0421\u0438\u043D\u0445\u0440\u043E\u043D\u0456\u0437\u0430\u0446\u0456\u044F \u0437 \u043D\u0435\u0449\u043E\u0434\u0430\u0432\u043D\u0456\u043C\u0438 \u0441\u0435\u0430\u043D\u0441\u0430\u043C\u0438",
|
||||
"Sort tags": "\u0421\u043E\u0440\u0442\u0443\u0432\u0430\u0442\u0438 \u0442\u0435\u0433\u0438",
|
||||
"Sort tags": "\u0421\u043E\u0440\u0442\u0443\u0432\u0430\u0442\u0438 \u043C\u0456\u0442\u043A\u0438",
|
||||
"Open connection in new tab": "\u0412\u0456\u0434\u043A\u0440\u0438\u0442\u0438 \u043F\u0456\u0434\u043A\u043B\u044E\u0447\u0435\u043D\u043D\u044F \u0432 \u043D\u043E\u0432\u0456\u0439 \u0432\u043A\u043B\u0430\u0434\u0446\u0456",
|
||||
"Move tab to new window": "\u041F\u0435\u0440\u0435\u043C\u0456\u0441\u0442\u0438\u0442\u0438 \u0432\u043A\u043B\u0430\u0434\u043A\u0443 \u0434\u043E \u043D\u043E\u0432\u043E\u0433\u043E \u0432\u0456\u043A\u043D\u0430",
|
||||
"Can not be empty": "\u041D\u0435 \u043C\u043E\u0436\u0435 \u0431\u0443\u0442\u0438 \u043F\u043E\u0440\u043E\u0436\u043D\u0456\u043C",
|
||||
@@ -25986,7 +26024,7 @@ If you want to access a device on a public server, please input "<id>@public", t
|
||||
"Grid View": "\u041F\u0435\u0440\u0435\u0433\u043B\u044F\u0434 \u0491\u0440\u0430\u0442\u043A\u043E\u044E",
|
||||
"List View": "\u041F\u0435\u0440\u0435\u0433\u043B\u044F\u0434 \u0441\u043F\u0438\u0441\u043A\u043E\u043C",
|
||||
Select: "\u0412\u0438\u0431\u0440\u0430\u0442\u0438",
|
||||
"Toggle Tags": "\u0412\u0438\u0434\u0438\u043C\u0456\u0441\u0442\u044C \u0442\u0435\u0433\u0456\u0432",
|
||||
"Toggle Tags": "\u0412\u0438\u0434\u0438\u043C\u0456\u0441\u0442\u044C \u043C\u0456\u0442\u043E\u043A",
|
||||
pull_ab_failed_tip: "\u041D\u0435 \u0432\u0434\u0430\u043B\u043E\u0441\u044F \u043E\u043D\u043E\u0432\u0438\u0442\u0438 \u0430\u0434\u0440\u0435\u0441\u043D\u0443 \u043A\u043D\u0438\u0433\u0443",
|
||||
push_ab_failed_tip: "\u041D\u0435 \u0432\u0434\u0430\u043B\u043E\u0441\u044F \u0441\u0438\u043D\u0445\u0440\u043E\u043D\u0456\u0437\u0443\u0432\u0430\u0442\u0438 \u0430\u0434\u0440\u0435\u0441\u043D\u0443 \u043A\u043D\u0438\u0433\u0443",
|
||||
synced_peer_readded_tip: "\u041F\u0440\u0438\u0441\u0442\u0440\u043E\u0457 \u0437 \u043D\u0435\u0449\u043E\u0434\u0430\u0432\u043D\u0456\u0445 \u0441\u0435\u0430\u043D\u0441\u0456\u0432 \u0431\u0443\u0434\u0443\u0442\u044C \u0441\u0438\u043D\u0445\u0440\u043E\u043D\u0456\u0437\u043E\u0432\u0430\u043D\u0456 \u0437 \u0430\u0434\u0440\u0435\u0441\u043D\u043E\u044E \u043A\u043D\u0438\u0433\u043E\u044E.",
|
||||
@@ -25995,7 +26033,7 @@ If you want to access a device on a public server, please input "<id>@public", t
|
||||
"HSV Color": "\u041A\u043E\u043B\u0456\u0440 HSV",
|
||||
"Installation Successful!": "\u0423\u0441\u043F\u0456\u0448\u043D\u0435 \u0432\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u043D\u044F!",
|
||||
"Installation failed!": "\u041D\u0435\u0432\u0434\u0430\u043B\u0435 \u0432\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u043D\u044F!",
|
||||
"Reverse mouse wheel": "\u0417\u0432\u043E\u0440\u043E\u0442\u043D\u0456\u0439 \u043D\u0430\u043F\u0440\u044F\u043C \u043F\u0440\u043E\u043A\u0440\u0443\u0442\u043A\u0438",
|
||||
"Reverse mouse wheel": "\u0417\u0432\u043E\u0440\u043E\u0442\u043D\u0456\u0439 \u043D\u0430\u043F\u0440\u044F\u043C \u0433\u043E\u0440\u0442\u0430\u043D\u043D\u044F",
|
||||
"{} sessions": "{} \u0441\u0435\u0430\u043D\u0441\u0456\u0432",
|
||||
scam_title: "\u0412\u0430\u0441 \u043C\u043E\u0436\u0443\u0442\u044C \u041E\u0411\u041C\u0410\u041D\u0423\u0422\u0418!",
|
||||
scam_text1: "\u042F\u043A\u0449\u043E \u0432\u0438 \u0440\u043E\u0437\u043C\u043E\u0432\u043B\u044F\u0454\u0442\u0435 \u043F\u043E \u0442\u0435\u043B\u0435\u0444\u043E\u043D\u0443 \u0437 \u043A\u0438\u043C\u043E\u0441\u044C, \u043A\u043E\u0433\u043E \u041D\u0415 \u0417\u041D\u0410\u0404\u0422\u0415 \u0447\u0438 \u043A\u043E\u043C\u0443 \u041D\u0415 \u0414\u041E\u0412\u0406\u0420\u042F\u0404\u0422\u0415, \u0456 \u0446\u044F \u043E\u0441\u043E\u0431\u0430 \u0445\u043E\u0447\u0435, \u0449\u043E\u0431 \u0432\u0438 \u0432\u0438\u043A\u043E\u0440\u0438\u0441\u0442\u0430\u043B\u0438 RustDesk \u0442\u0430 \u0437\u0430\u043F\u0443\u0441\u0442\u0438\u043B\u0438 \u0441\u043B\u0443\u0436\u0431\u0443, \u043D\u0435 \u0440\u043E\u0431\u0456\u0442\u044C \u0446\u044C\u043E\u0433\u043E \u0442\u0430 \u043D\u0435\u0433\u0430\u0439\u043D\u043E \u0437\u0430\u0432\u0435\u0440\u0448\u0456\u0442\u044C \u0434\u0437\u0432\u0456\u043D\u043E\u043A.",
|
||||
@@ -26119,15 +26157,16 @@ If you want to access a device on a public server, please input "<id>@public", t
|
||||
"one-way-file-transfer-tip": "\u041D\u0430 \u0441\u0442\u043E\u0440\u043E\u043D\u0456, \u0449\u043E \u043A\u0435\u0440\u0443\u0454\u0442\u044C\u0441\u044F, \u0443\u0432\u0456\u043C\u043A\u043D\u0435\u043D\u043E \u043E\u0434\u043D\u043E\u0441\u0442\u043E\u0440\u043E\u043D\u043D\u044E \u043F\u0435\u0440\u0435\u0434\u0430\u0447\u0443 \u0444\u0430\u0439\u043B\u0456\u0432.",
|
||||
"Authentication Required": "\u041F\u043E\u0442\u0440\u0456\u0431\u043D\u0430 \u0430\u0432\u0442\u0435\u043D\u0442\u0438\u0444\u0456\u043A\u0430\u0446\u0456\u044F",
|
||||
Authenticate: "\u0410\u0432\u0442\u0435\u043D\u0442\u0438\u0444\u0456\u043A\u0443\u0432\u0430\u0442\u0438",
|
||||
web_id_input_tip: `\u0412\u0438 \u043C\u043E\u0436\u0435\u0442\u0435 \u0432\u0432\u0435\u0441\u0442\u0438 ID \u0437 \u0442\u043E\u0433\u043E \u0441\u0430\u043C\u043E\u0433\u043E \u0441\u0435\u0440\u0432\u0435\u0440\u0443, \u043F\u0440\u044F\u043C\u0438\u0439 IP-\u0434\u043E\u0441\u0442\u0443\u043F \u0443 \u0432\u0435\u0431-\u043A\u043B\u0456\u0454\u043D\u0442\u0456 \u043D\u0435 \u043F\u0456\u0434\u0442\u0440\u0438\u043C\u0443\u0454\u0442\u044C\u0441\u044F.
|
||||
\u042F\u043A\u0449\u043E \u0432\u0438 \u0445\u043E\u0447\u0435\u0442\u0435 \u043E\u0442\u0440\u0438\u043C\u0430\u0442\u0438 \u0434\u043E\u0441\u0442\u0443\u043F \u0434\u043E \u043F\u0440\u0438\u0441\u0442\u0440\u043E\u044E \u043D\u0430 \u0456\u043D\u0448\u043E\u043C\u0443 \u0441\u0435\u0440\u0432\u0435\u0440\u0456, \u0431\u0443\u0434\u044C \u043B\u0430\u0441\u043A\u0430, \u0434\u043E\u0434\u0430\u0439\u0442\u0435 \u0430\u0434\u0440\u0435\u0441\u0443 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 (<id>@<\u0430\u0434\u0440\u0435\u0441\u0430_\u0441\u0435\u0440\u0432\u0435\u0440\u0430>?key=<\u0437\u043D\u0430\u0447\u0435\u043D\u043D\u044F_\u043A\u043B\u044E\u0447\u0430>), \u043D\u0430\u043F\u0440\u0438\u043A\u043B\u0430\u0434,
|
||||
web_id_input_tip: `\u0412\u0438 \u043C\u043E\u0436\u0435\u0442\u0435 \u0432\u0432\u0435\u0441\u0442\u0438 ID \u043D\u0430 \u0442\u043E\u043C\u0443 \u0441\u0430\u043C\u043E\u043C\u0443 \u0441\u0435\u0440\u0432\u0435\u0440\u0443, \u043F\u0440\u044F\u043C\u0438\u0439 IP-\u0434\u043E\u0441\u0442\u0443\u043F \u0443 \u0432\u0435\u0431-\u043A\u043B\u0456\u0454\u043D\u0442\u0456 \u043D\u0435 \u043F\u0456\u0434\u0442\u0440\u0438\u043C\u0443\u0454\u0442\u044C\u0441\u044F.
|
||||
\u042F\u043A\u0449\u043E \u0432\u0438 \u0445\u043E\u0447\u0435\u0442\u0435 \u043E\u0442\u0440\u0438\u043C\u0430\u0442\u0438 \u0434\u043E\u0441\u0442\u0443\u043F \u0434\u043E \u043F\u0440\u0438\u0441\u0442\u0440\u043E\u044E \u043D\u0430 \u0456\u043D\u0448\u043E\u043C\u0443 \u0441\u0435\u0440\u0432\u0435\u0440\u0456, \u0431\u0443\u0434\u044C \u043B\u0430\u0441\u043A\u0430, \u0434\u043E\u0434\u0430\u0439\u0442\u0435 \u0430\u0434\u0440\u0435\u0441\u0443 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 (<id>@<\u0430\u0434\u0440\u0435\u0441\u0430_\u0441\u0435\u0440\u0432\u0435\u0440\u0430>?key=<\u0437\u043D\u0430\u0447\u0435\u043D\u043D\u044F_\u043A\u043B\u044E\u0447\u0430>). \u041D\u0430\u043F\u0440\u0438\u043A\u043B\u0430\u0434,
|
||||
9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.
|
||||
\u042F\u043A\u0449\u043E \u0432\u0438 \u0445\u043E\u0447\u0435\u0442\u0435 \u043E\u0442\u0440\u0438\u043C\u0430\u0442\u0438 \u0434\u043E\u0441\u0442\u0443\u043F \u0434\u043E \u043F\u0440\u0438\u0441\u0442\u0440\u043E\u044E \u043D\u0430 \u043F\u0443\u0431\u043B\u0456\u0447\u043D\u043E\u043C\u0443 \u0441\u0435\u0440\u0432\u0435\u0440\u0456, \u0431\u0443\u0434\u044C \u043B\u0430\u0441\u043A\u0430, \u0432\u0432\u0435\u0434\u0456\u0442\u044C "<id>@public", \u0434\u043B\u044F \u043F\u0443\u0431\u043B\u0456\u0447\u043D\u043E\u0433\u043E \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u043A\u043B\u044E\u0447 \u043D\u0435 \u043F\u043E\u0442\u0440\u0456\u0431\u0435\u043D.`,
|
||||
Download: "",
|
||||
"Upload folder": "",
|
||||
"Upload files": "",
|
||||
"Clipboard is synchronized": "",
|
||||
"Update client clipboard": ""
|
||||
\u042F\u043A\u0449\u043E \u0432\u0438 \u0445\u043E\u0447\u0435\u0442\u0435 \u043E\u0442\u0440\u0438\u043C\u0430\u0442\u0438 \u0434\u043E\u0441\u0442\u0443\u043F \u0434\u043E \u043F\u0440\u0438\u0441\u0442\u0440\u043E\u044E \u043D\u0430 \u043F\u0443\u0431\u043B\u0456\u0447\u043D\u043E\u043C\u0443 \u0441\u0435\u0440\u0432\u0435\u0440\u0456, \u0431\u0443\u0434\u044C \u043B\u0430\u0441\u043A\u0430, \u0432\u0432\u0435\u0434\u0456\u0442\u044C "<id>@public". \u0414\u043B\u044F \u043F\u0443\u0431\u043B\u0456\u0447\u043D\u043E\u0433\u043E \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u043A\u043B\u044E\u0447 \u043D\u0435 \u043F\u043E\u0442\u0440\u0456\u0431\u0435\u043D.`,
|
||||
Download: "\u041E\u0442\u0440\u0438\u043C\u0430\u0442\u0438",
|
||||
"Upload folder": "\u041D\u0430\u0434\u0456\u0441\u043B\u0430\u0442\u0438 \u0442\u0435\u043A\u0443",
|
||||
"Upload files": "\u041D\u0430\u0434\u0456\u0441\u043B\u0430\u0442\u0438 \u0444\u0430\u0439\u043B\u0438",
|
||||
"Clipboard is synchronized": "\u0411\u0443\u0444\u0435\u0440 \u043E\u0431\u043C\u0456\u043D\u0443 \u0441\u0438\u043D\u0445\u0440\u043E\u043D\u0456\u0437\u043E\u0432\u0430\u043D\u043E",
|
||||
"Update client clipboard": "\u041E\u043D\u043E\u0432\u0438\u0442\u0438 \u0431\u0443\u0444\u0435\u0440 \u043E\u0431\u043C\u0456\u043D\u0443 \u043A\u043B\u0456\u0454\u043D\u0442\u0430",
|
||||
Untagged: "\u0411\u0435\u0437 \u043C\u0456\u0442\u043E\u043A"
|
||||
}, cs: {
|
||||
Status: "Stav",
|
||||
"Your Desktop": "Va\u0161e plocha",
|
||||
@@ -26792,7 +26831,8 @@ M\u016F\u017Eete se p\u0159ipojit k jin\xFDm za\u0159\xEDzen\xEDm, ale jin\xE1 z
|
||||
"Upload folder": "",
|
||||
"Upload files": "",
|
||||
"Clipboard is synchronized": "",
|
||||
"Update client clipboard": ""
|
||||
"Update client clipboard": "",
|
||||
Untagged: ""
|
||||
}, nl: {
|
||||
Status: "Status",
|
||||
"Your Desktop": "Uw Bureaublad",
|
||||
@@ -27455,7 +27495,8 @@ Als je toegang wilt krijgen tot een apparaat op een publieke server, voer dan "<
|
||||
"Upload folder": "Map uploaden",
|
||||
"Upload files": "Bestanden uploaden",
|
||||
"Clipboard is synchronized": "Klembord is gesynchroniseerd",
|
||||
"Update client clipboard": "Klembord van client bijwerken"
|
||||
"Update client clipboard": "Klembord van client bijwerken",
|
||||
Untagged: ""
|
||||
}, fr: {
|
||||
Status: "Statut",
|
||||
"Your Desktop": "Votre bureau",
|
||||
@@ -27601,7 +27642,7 @@ Als je toegang wilt krijgen tot een apparaat op een publieke server, voer dan "<
|
||||
"Failed to make direct connection to remote desktop": "Impossible d'\xE9tablir une connexion directe",
|
||||
"Set Password": "D\xE9finir le mot de passe",
|
||||
"OS Password": "Mot de passe du syst\xE8me d'exploitation",
|
||||
install_tip: "Vous utilisez une version non install\xE9e. En raison des restrictions UAC, en tant que terminal contr\xF4l\xE9, dans certains cas, il ne sera pas en mesure de contr\xF4ler la souris et le clavier ou d'enregistrer l'\xE9cran. Veuillez cliquer sur le bouton ci-dessous pour installer RustDesk au syst\xE8me pour \xE9viter la question ci-dessus.",
|
||||
install_tip: "RustDesk n'est pas install\xE9, ce qui peut limiter son utilisation \xE0 cause de l'UAC. Cliquez ci-dessous pour l'installer.",
|
||||
"Click to upgrade": "Cliquer pour mettre \xE0 niveau",
|
||||
"Click to download": "Cliquer pour t\xE9l\xE9charger",
|
||||
"Click to update": "Cliquer pour mettre \xE0 jour",
|
||||
@@ -28116,6 +28157,7 @@ Vous pouvez vous connecter \xE0 d\u2019autres appareils, mais les autres apparei
|
||||
"Upload folder": "",
|
||||
"Upload files": "",
|
||||
"Clipboard is synchronized": "",
|
||||
"Update client clipboard": ""
|
||||
"Update client clipboard": "",
|
||||
Untagged: ""
|
||||
}
|
||||
}
|
||||
|
||||
139287
resources/web2/main.dart.js
vendored
@@ -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
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ type Service struct {
|
||||
*OauthService
|
||||
*LoginLogService
|
||||
*AuditService
|
||||
*ShareRecordService
|
||||
}
|
||||
|
||||
func New() *Service {
|
||||
|
||||
49
service/shareRecord.go
Normal 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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||