Compare commits
37 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
46bb44f0ab | ||
|
|
2f1380f24a | ||
|
|
ece3328e94 | ||
|
|
fdd26d87be | ||
|
|
2ade0dda42 | ||
|
|
a87ae5cf65 | ||
|
|
fe7b8b53a6 | ||
|
|
b929f3efdb | ||
|
|
f847fc076f | ||
|
|
60d0a701ce | ||
|
|
0dedaf6824 | ||
|
|
ab231b3fed | ||
|
|
e7f28cca36 | ||
|
|
505e8aac4b | ||
|
|
746e2a6052 | ||
|
|
dc03d5d83d | ||
|
|
b770ab178d | ||
|
|
fd7e022e88 | ||
|
|
ac5df6826b | ||
|
|
91908859bc | ||
|
|
9c8822f857 | ||
|
|
9709da7fb6 | ||
|
|
1852f10131 | ||
|
|
77d7b43e21 | ||
|
|
7410b539a0 | ||
|
|
0403d71502 | ||
|
|
2af1d93a7d | ||
|
|
76281ad761 | ||
|
|
d1ec9b4916 | ||
|
|
448e7460a9 | ||
|
|
ee0cbabffc | ||
|
|
d6a5af890a | ||
|
|
dc313441e5 | ||
|
|
c75320f4f4 | ||
|
|
c788f78416 | ||
|
|
49cf954d4a | ||
|
|
014e3db54f |
12
.github/workflows/build.yml
vendored
12
.github/workflows/build.yml
vendored
@@ -380,7 +380,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Create and push manifest Docker Hub (:version)
|
- name: Create and push manifest Docker Hub (:version)
|
||||||
if: ${{ env.SKIP_DOCKER_HUB == 'false' }}
|
if: ${{ env.SKIP_DOCKER_HUB == 'false' }}
|
||||||
uses: Noelware/docker-manifest-action@master
|
uses: Noelware/docker-manifest-action@v0.2.3
|
||||||
with:
|
with:
|
||||||
base-image: ${{ env.BASE_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}
|
base-image: ${{ env.BASE_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}
|
||||||
extra-images: ${{ env.DOCKERHUB_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}-amd64,
|
extra-images: ${{ env.DOCKERHUB_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}-amd64,
|
||||||
@@ -390,7 +390,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Create and push manifest GHCR (:version)
|
- name: Create and push manifest GHCR (:version)
|
||||||
if: ${{ env.SKIP_GHCR == 'false' }}
|
if: ${{ env.SKIP_GHCR == 'false' }}
|
||||||
uses: Noelware/docker-manifest-action@master
|
uses: Noelware/docker-manifest-action@v0.2.3
|
||||||
with:
|
with:
|
||||||
base-image: ghcr.io/${{ env.BASE_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}
|
base-image: ghcr.io/${{ env.BASE_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}
|
||||||
extra-images: ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}-amd64,
|
extra-images: ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}-amd64,
|
||||||
@@ -401,7 +401,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Create and push manifest Docker Hub (:latest)
|
- name: Create and push manifest Docker Hub (:latest)
|
||||||
if: ${{ env.SKIP_DOCKER_HUB == 'false' }}
|
if: ${{ env.SKIP_DOCKER_HUB == 'false' }}
|
||||||
uses: Noelware/docker-manifest-action@master
|
uses: Noelware/docker-manifest-action@v0.2.3
|
||||||
with:
|
with:
|
||||||
base-image: ${{ env.BASE_IMAGE_NAMESPACE }}/rustdesk-api:latest
|
base-image: ${{ env.BASE_IMAGE_NAMESPACE }}/rustdesk-api:latest
|
||||||
extra-images: ${{ env.DOCKERHUB_IMAGE_NAMESPACE }}/rustdesk-api:latest-amd64,
|
extra-images: ${{ env.DOCKERHUB_IMAGE_NAMESPACE }}/rustdesk-api:latest-amd64,
|
||||||
@@ -411,7 +411,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Create and push manifest GHCR (:latest)
|
- name: Create and push manifest GHCR (:latest)
|
||||||
if: ${{ env.SKIP_GHCR == 'false' }}
|
if: ${{ env.SKIP_GHCR == 'false' }}
|
||||||
uses: Noelware/docker-manifest-action@master
|
uses: Noelware/docker-manifest-action@v0.2.3
|
||||||
with:
|
with:
|
||||||
base-image: ghcr.io/${{ env.BASE_IMAGE_NAMESPACE }}/rustdesk-api:latest
|
base-image: ghcr.io/${{ env.BASE_IMAGE_NAMESPACE }}/rustdesk-api:latest
|
||||||
extra-images: ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:latest-amd64,
|
extra-images: ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:latest-amd64,
|
||||||
@@ -422,7 +422,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Create and push Full S6 manifest Docker Hub (:version)
|
- name: Create and push Full S6 manifest Docker Hub (:version)
|
||||||
if: ${{ env.SKIP_DOCKER_HUB == 'false' }}
|
if: ${{ env.SKIP_DOCKER_HUB == 'false' }}
|
||||||
uses: Noelware/docker-manifest-action@master
|
uses: Noelware/docker-manifest-action@v0.2.3
|
||||||
with:
|
with:
|
||||||
base-image: ${{ env.BASE_IMAGE_NAMESPACE }}/rustdesk-api:full-s6
|
base-image: ${{ env.BASE_IMAGE_NAMESPACE }}/rustdesk-api:full-s6
|
||||||
extra-images: ${{ env.DOCKERHUB_IMAGE_NAMESPACE }}/rustdesk-api:full-s6-amd64,
|
extra-images: ${{ env.DOCKERHUB_IMAGE_NAMESPACE }}/rustdesk-api:full-s6-amd64,
|
||||||
@@ -433,7 +433,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Create and push Full S6 manifest GHCR (:latest)
|
- name: Create and push Full S6 manifest GHCR (:latest)
|
||||||
if: ${{ env.SKIP_GHCR == 'false' }}
|
if: ${{ env.SKIP_GHCR == 'false' }}
|
||||||
uses: Noelware/docker-manifest-action@master
|
uses: Noelware/docker-manifest-action@v0.2.3
|
||||||
with:
|
with:
|
||||||
base-image: ghcr.io/${{ env.BASE_IMAGE_NAMESPACE }}/rustdesk-api:full-s6
|
base-image: ghcr.io/${{ env.BASE_IMAGE_NAMESPACE }}/rustdesk-api:full-s6
|
||||||
extra-images: ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:full-s6-amd64,
|
extra-images: ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:full-s6-amd64,
|
||||||
|
|||||||
4
.github/workflows/build_test.yml
vendored
4
.github/workflows/build_test.yml
vendored
@@ -317,7 +317,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Create and push manifest Docker Hub (:version)
|
- name: Create and push manifest Docker Hub (:version)
|
||||||
if: ${{ env.SKIP_DOCKER_HUB == 'false' }}
|
if: ${{ env.SKIP_DOCKER_HUB == 'false' }}
|
||||||
uses: Noelware/docker-manifest-action@master
|
uses: Noelware/docker-manifest-action@v0.2.3
|
||||||
with:
|
with:
|
||||||
base-image: ${{ env.BASE_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}
|
base-image: ${{ env.BASE_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}
|
||||||
extra-images: ${{ env.DOCKERHUB_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}-amd64,
|
extra-images: ${{ env.DOCKERHUB_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}-amd64,
|
||||||
@@ -327,7 +327,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Create and push manifest GHCR (:version)
|
- name: Create and push manifest GHCR (:version)
|
||||||
if: ${{ env.SKIP_GHCR == 'false' }}
|
if: ${{ env.SKIP_GHCR == 'false' }}
|
||||||
uses: Noelware/docker-manifest-action@master
|
uses: Noelware/docker-manifest-action@v0.2.3
|
||||||
with:
|
with:
|
||||||
base-image: ghcr.io/${{ env.BASE_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}
|
base-image: ghcr.io/${{ env.BASE_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}
|
||||||
extra-images: ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}-amd64,
|
extra-images: ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}-amd64,
|
||||||
|
|||||||
73
README.md
73
README.md
@@ -4,12 +4,12 @@
|
|||||||
|
|
||||||
本项目使用 Go 实现了 RustDesk 的 API,并包含了 Web Admin 和 Web 客户端。RustDesk 是一个远程桌面软件,提供了自托管的解决方案。
|
本项目使用 Go 实现了 RustDesk 的 API,并包含了 Web Admin 和 Web 客户端。RustDesk 是一个远程桌面软件,提供了自托管的解决方案。
|
||||||
|
|
||||||
<div align=center>
|
<div align=center>
|
||||||
<img src="https://img.shields.io/badge/golang-1.22-blue"/>
|
<img src="https://img.shields.io/badge/golang-1.22-blue"/>
|
||||||
<img src="https://img.shields.io/badge/gin-v1.9.0-lightBlue"/>
|
<img src="https://img.shields.io/badge/gin-v1.9.0-lightBlue"/>
|
||||||
<img src="https://img.shields.io/badge/gorm-v1.25.7-green"/>
|
<img src="https://img.shields.io/badge/gorm-v1.25.7-green"/>
|
||||||
<img src="https://img.shields.io/badge/swag-v1.16.3-yellow"/>
|
<img src="https://img.shields.io/badge/swag-v1.16.3-yellow"/>
|
||||||
<img src="https://img.shields.io/badge/i18n-7-green"/>
|
<img src="https://goreportcard.com/badge/github.com/lejianwen/rustdesk-api/v2"/>
|
||||||
<img src="https://github.com/lejianwen/rustdesk-api/actions/workflows/build.yml/badge.svg"/>
|
<img src="https://github.com/lejianwen/rustdesk-api/actions/workflows/build.yml/badge.svg"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -108,8 +108,7 @@
|
|||||||
* 可以官方指令
|
* 可以官方指令
|
||||||
* 可以添加自定义指令
|
* 可以添加自定义指令
|
||||||
* 可以执行自定义指令
|
* 可以执行自定义指令
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
11. **LDAP 支持**, 当在API Server上设置了LDAP(已测试AD和LDAP),可以通过LDAP中的用户信息进行登录 https://github.com/lejianwen/rustdesk-api/issues/114 ,如果LDAP验证失败,返回本地用户
|
11. **LDAP 支持**, 当在API Server上设置了LDAP(已测试AD和LDAP),可以通过LDAP中的用户信息进行登录 https://github.com/lejianwen/rustdesk-api/issues/114 ,如果LDAP验证失败,返回本地用户
|
||||||
|
|
||||||
@@ -146,69 +145,11 @@
|
|||||||
|
|
||||||
### 相关配置
|
### 相关配置
|
||||||
|
|
||||||
|
* [配置文件](./conf/config.yaml)
|
||||||
* 参考`conf/config.yaml`配置文件,修改相关配置。
|
* 参考`conf/config.yaml`配置文件,修改相关配置。
|
||||||
* 如果`gorm.type`是`sqlite`,则不需要配置mysql相关配置。
|
* 如果`gorm.type`是`sqlite`,则不需要配置mysql相关配置。
|
||||||
* 语言如果不设置默认为`zh-CN`
|
* 语言如果不设置默认为`zh-CN`
|
||||||
|
|
||||||
```yaml
|
|
||||||
lang: "en"
|
|
||||||
app:
|
|
||||||
web-client: 1 # 1:启用 0:禁用
|
|
||||||
register: false #是否开启注册
|
|
||||||
show-swagger: 0 #是否显示swagger文档
|
|
||||||
gin:
|
|
||||||
api-addr: "0.0.0.0:21114"
|
|
||||||
mode: "release"
|
|
||||||
resources-path: 'resources'
|
|
||||||
trust-proxy: ""
|
|
||||||
gorm:
|
|
||||||
type: "sqlite"
|
|
||||||
max-idle-conns: 10
|
|
||||||
max-open-conns: 100
|
|
||||||
mysql:
|
|
||||||
username: "root"
|
|
||||||
password: "111111"
|
|
||||||
addr: "192.168.1.66:3308"
|
|
||||||
dbname: "rustdesk"
|
|
||||||
rustdesk:
|
|
||||||
id-server: "192.168.1.66:21116"
|
|
||||||
relay-server: "192.168.1.66:21117"
|
|
||||||
api-server: "http://192.168.1.66:21114"
|
|
||||||
key: "123456789"
|
|
||||||
personal: 1
|
|
||||||
logger:
|
|
||||||
path: "./runtime/log.txt"
|
|
||||||
level: "warn" #trace,debug,info,warn,error,fatal
|
|
||||||
report-caller: true
|
|
||||||
proxy:
|
|
||||||
enable: false
|
|
||||||
host: ""
|
|
||||||
jwt:
|
|
||||||
key: ""
|
|
||||||
expire-duration: 360000
|
|
||||||
ldap:
|
|
||||||
enable: false
|
|
||||||
url: "ldap://ldap.example.com:389"
|
|
||||||
tls: false
|
|
||||||
tls-verify: false
|
|
||||||
base-dn: "dc=example,dc=com"
|
|
||||||
bind-dn: "cn=admin,dc=example,dc=com"
|
|
||||||
bind-password: "password"
|
|
||||||
|
|
||||||
user:
|
|
||||||
base-dn: "ou=users,dc=example,dc=com"
|
|
||||||
enable-attr: "" #The attribute name of the user for enabling, in AD it is "userAccountControl", empty means no enable attribute, all users are enabled
|
|
||||||
enable-attr-value: "" # The value of the enable attribute when the user is enabled. If you are using AD, just set random value, it will be ignored.
|
|
||||||
filter: "(cn=*)"
|
|
||||||
username: "uid" # The attribute name of the user for usernamem if you are using AD, it should be "sAMAccountName"
|
|
||||||
email: "mail"
|
|
||||||
first-name: "givenName"
|
|
||||||
last-name: "sn"
|
|
||||||
sync: false # If true, the user will be synchronized to the database when the user logs in. If false, the user will be synchronized to the database when the user be created.
|
|
||||||
admin-group: "cn=admin,dc=example,dc=com" # The group name of the admin group, if the user is in this group, the user will be an admin.
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
### 环境变量
|
### 环境变量
|
||||||
环境变量和配置文件`conf/config.yaml`中的配置一一对应,变量名前缀是`RUSTDESK_API`
|
环境变量和配置文件`conf/config.yaml`中的配置一一对应,变量名前缀是`RUSTDESK_API`
|
||||||
下面表格并未全部列出,可以参考`conf/config.yaml`中的配置。
|
下面表格并未全部列出,可以参考`conf/config.yaml`中的配置。
|
||||||
@@ -220,7 +161,8 @@ ldap:
|
|||||||
| RUSTDESK_API_APP_WEB_CLIENT | 是否启用web-client; 1:启用,0:不启用; 默认启用 | 1 |
|
| RUSTDESK_API_APP_WEB_CLIENT | 是否启用web-client; 1:启用,0:不启用; 默认启用 | 1 |
|
||||||
| RUSTDESK_API_APP_REGISTER | 是否开启注册; `true`, `false` 默认`false` | `false` |
|
| RUSTDESK_API_APP_REGISTER | 是否开启注册; `true`, `false` 默认`false` | `false` |
|
||||||
| RUSTDESK_API_APP_SHOW_SWAGGER | 是否可见swagger文档;`1`显示,`0`不显示,默认`0`不显示 | `1` |
|
| RUSTDESK_API_APP_SHOW_SWAGGER | 是否可见swagger文档;`1`显示,`0`不显示,默认`0`不显示 | `1` |
|
||||||
| RUSTDESK_API_APP_TOKEN_EXPIRE | token有效时长(秒) | `3600` |
|
| RUSTDESK_API_APP_TOKEN_EXPIRE | token有效时长 | `168h` |
|
||||||
|
| RUSTDESK_API_APP_DISABLE_PWD_LOGIN | 是否禁用密码登录; `true`, `false` 默认`false` | `false` |
|
||||||
| -----ADMIN配置----- | ---------- | ---------- |
|
| -----ADMIN配置----- | ---------- | ---------- |
|
||||||
| RUSTDESK_API_ADMIN_TITLE | 后台标题 | `RustDesk Api Admin` |
|
| RUSTDESK_API_ADMIN_TITLE | 后台标题 | `RustDesk Api Admin` |
|
||||||
| RUSTDESK_API_ADMIN_HELLO | 后台欢迎语,可以使用`html` | |
|
| RUSTDESK_API_ADMIN_HELLO | 后台欢迎语,可以使用`html` | |
|
||||||
@@ -244,12 +186,13 @@ ldap:
|
|||||||
| RUSTDESK_API_RUSTDESK_KEY | Rustdesk的key | 123456789 |
|
| RUSTDESK_API_RUSTDESK_KEY | Rustdesk的key | 123456789 |
|
||||||
| RUSTDESK_API_RUSTDESK_KEY_FILE | Rustdesk存放key的文件 | `./conf/data/id_ed25519.pub` |
|
| RUSTDESK_API_RUSTDESK_KEY_FILE | Rustdesk存放key的文件 | `./conf/data/id_ed25519.pub` |
|
||||||
| RUSTDESK_API_RUSTDESK_WEBCLIENT<br/>_MAGIC_QUERYONLINE | Web client v2 中是否启用新的在线状态查询方法; `1`:启用,`0`:不启用,默认不启用 | `0` |
|
| RUSTDESK_API_RUSTDESK_WEBCLIENT<br/>_MAGIC_QUERYONLINE | Web client v2 中是否启用新的在线状态查询方法; `1`:启用,`0`:不启用,默认不启用 | `0` |
|
||||||
|
| RUSTDESK_API_RUSTDESK_WS_HOST | 自定义Websocket Host | `wss://192.168.1.123:1234` |
|
||||||
| ----PROXY配置----- | ---------- | ---------- |
|
| ----PROXY配置----- | ---------- | ---------- |
|
||||||
| RUSTDESK_API_PROXY_ENABLE | 是否启用代理:`false`, `true` | `false` |
|
| RUSTDESK_API_PROXY_ENABLE | 是否启用代理:`false`, `true` | `false` |
|
||||||
| RUSTDESK_API_PROXY_HOST | 代理地址 | `http://127.0.0.1:1080` |
|
| RUSTDESK_API_PROXY_HOST | 代理地址 | `http://127.0.0.1:1080` |
|
||||||
| ----JWT配置---- | -------- | -------- |
|
| ----JWT配置---- | -------- | -------- |
|
||||||
| RUSTDESK_API_JWT_KEY | 自定义JWT KEY,为空则不启用JWT<br/>如果没使用`lejianwen/rustdesk-server`中的`MUST_LOGIN`,建议设置为空 | |
|
| RUSTDESK_API_JWT_KEY | 自定义JWT KEY,为空则不启用JWT<br/>如果没使用`lejianwen/rustdesk-server`中的`MUST_LOGIN`,建议设置为空 | |
|
||||||
| RUSTDESK_API_JWT_EXPIRE_DURATION | JWT有效时间 | 360000 |
|
| RUSTDESK_API_JWT_EXPIRE_DURATION | JWT有效时间 | `168h` |
|
||||||
|
|
||||||
|
|
||||||
### 运行
|
### 运行
|
||||||
|
|||||||
139
README_EN.md
139
README_EN.md
@@ -8,7 +8,7 @@ desktop software that provides self-hosted solutions.
|
|||||||
<img src="https://img.shields.io/badge/gin-v1.9.0-lightBlue"/>
|
<img src="https://img.shields.io/badge/gin-v1.9.0-lightBlue"/>
|
||||||
<img src="https://img.shields.io/badge/gorm-v1.25.7-green"/>
|
<img src="https://img.shields.io/badge/gorm-v1.25.7-green"/>
|
||||||
<img src="https://img.shields.io/badge/swag-v1.16.3-yellow"/>
|
<img src="https://img.shields.io/badge/swag-v1.16.3-yellow"/>
|
||||||
<img src="https://img.shields.io/badge/i18n-7-green"/>
|
<img src="https://goreportcard.com/badge/github.com/lejianwen/rustdesk-api/v2"/>
|
||||||
<img src="https://github.com/lejianwen/rustdesk-api/actions/workflows/build.yml/badge.svg"/>
|
<img src="https://github.com/lejianwen/rustdesk-api/actions/workflows/build.yml/badge.svg"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -109,8 +109,6 @@ displaying data.Frontend code is available at [rustdesk-api-web](https://github.
|
|||||||
* Custom commands can be added
|
* Custom commands can be added
|
||||||
* Custom commands can be executed
|
* Custom commands can be executed
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
11. **LDAP Support**, When you setup the LDAP(test for OpenLDAP and AD), you can login with the LDAP's user. https://github.com/lejianwen/rustdesk-api/issues/114 , if LDAP fail fallback local user
|
11. **LDAP Support**, When you setup the LDAP(test for OpenLDAP and AD), you can login with the LDAP's user. https://github.com/lejianwen/rustdesk-api/issues/114 , if LDAP fail fallback local user
|
||||||
|
|
||||||
### Web Client:
|
### Web Client:
|
||||||
@@ -145,110 +143,55 @@ displaying data.Frontend code is available at [rustdesk-api-web](https://github.
|
|||||||
|
|
||||||
### Configuration
|
### Configuration
|
||||||
|
|
||||||
|
* [Config File](./conf/config.yaml)
|
||||||
* Modify the configuration in `conf/config.yaml`.
|
* Modify the configuration in `conf/config.yaml`.
|
||||||
* If `gorm.type` is set to `sqlite`, MySQL-related configurations are not required.
|
* If `gorm.type` is set to `sqlite`, MySQL-related configurations are not required.
|
||||||
* Language support: `en` and `zh-CN` are supported. The default is `zh-CN`.
|
* Language support: `en` and `zh-CN` are supported. The default is `zh-CN`.
|
||||||
|
|
||||||
```yaml
|
|
||||||
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"
|
|
||||||
resources-path: 'resources'
|
|
||||||
trust-proxy: ""
|
|
||||||
gorm:
|
|
||||||
type: "sqlite"
|
|
||||||
max-idle-conns: 10
|
|
||||||
max-open-conns: 100
|
|
||||||
mysql:
|
|
||||||
username: "root"
|
|
||||||
password: "111111"
|
|
||||||
addr: "192.168.1.66:3308"
|
|
||||||
dbname: "rustdesk"
|
|
||||||
rustdesk:
|
|
||||||
id-server: "192.168.1.66:21116"
|
|
||||||
relay-server: "192.168.1.66:21117"
|
|
||||||
api-server: "http://192.168.1.66:21114"
|
|
||||||
key: "123456789"
|
|
||||||
personal: 1
|
|
||||||
logger:
|
|
||||||
path: "./runtime/log.txt"
|
|
||||||
level: "warn" #trace,debug,info,warn,error,fatal
|
|
||||||
report-caller: true
|
|
||||||
proxy:
|
|
||||||
enable: false
|
|
||||||
host: ""
|
|
||||||
jwt:
|
|
||||||
key: ""
|
|
||||||
expire-duration: 360000
|
|
||||||
ldap:
|
|
||||||
enable: false
|
|
||||||
url: "ldap://ldap.example.com:389"
|
|
||||||
tls: false
|
|
||||||
tls-verify: false
|
|
||||||
base-dn: "dc=example,dc=com"
|
|
||||||
bind-dn: "cn=admin,dc=example,dc=com"
|
|
||||||
bind-password: "password"
|
|
||||||
|
|
||||||
user:
|
|
||||||
base-dn: "ou=users,dc=example,dc=com"
|
|
||||||
enable-attr: "" #The attribute name of the user for enabling, in AD it is "userAccountControl", empty means no enable attribute, all users are enabled
|
|
||||||
enable-attr-value: "" # The value of the enable attribute when the user is enabled. If you are using AD, just set random value, it will be ignored.
|
|
||||||
filter: "(cn=*)"
|
|
||||||
username: "uid" # The attribute name of the user for usernamem if you are using AD, it should be "sAMAccountName"
|
|
||||||
email: "mail"
|
|
||||||
first-name: "givenName"
|
|
||||||
last-name: "sn"
|
|
||||||
sync: false # If true, the user will be synchronized to the database when the user logs in. If false, the user will be synchronized to the database when the user be created.
|
|
||||||
admin-group: "cn=admin,dc=example,dc=com" # The group name of the admin group, if the user is in this group, the user will be an admin.
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
### Environment Variables
|
### Environment Variables
|
||||||
The environment variables correspond one-to-one with the configurations in the `conf/config.yaml` file. The prefix for variable names is `RUSTDESK_API`.
|
The environment variables correspond one-to-one with the configurations in the `conf/config.yaml` file. The prefix for variable names is `RUSTDESK_API`.
|
||||||
The table below does not list all configurations. Please refer to the configurations in `conf/config.yaml`.
|
The table below does not list all configurations. Please refer to the configurations in `conf/config.yaml`.
|
||||||
|
|
||||||
| Variable Name | Description | Example |
|
| Variable Name | Description | Example |
|
||||||
|---------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------|
|
|--------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------|
|
||||||
| TZ | timezone | Asia/Shanghai |
|
| TZ | timezone | Asia/Shanghai |
|
||||||
| RUSTDESK_API_LANG | Language | `en`,`zh-CN` |
|
| RUSTDESK_API_LANG | Language | `en`,`zh-CN` |
|
||||||
| RUSTDESK_API_APP_WEB_CLIENT | web client on/off; 1: on, 0 off, default: 1 | 1 |
|
| RUSTDESK_API_APP_WEB_CLIENT | web client on/off; 1: on, 0 off, default: 1 | 1 |
|
||||||
| RUSTDESK_API_APP_REGISTER | register enable; `true`, `false`; default:`false` | `false` |
|
| RUSTDESK_API_APP_REGISTER | register enable; `true`, `false`; default:`false` | `false` |
|
||||||
| RUSTDESK_API_APP_SHOW_SWAGGER | swagger visible; 1: yes, 0: no; default: 0 | `0` |
|
| RUSTDESK_API_APP_SHOW_SWAGGER | swagger visible; 1: yes, 0: no; default: 0 | `0` |
|
||||||
| RUSTDESK_API_APP_TOKEN_EXPIRE | token expire duration(second) | `3600` |
|
| RUSTDESK_API_APP_TOKEN_EXPIRE | token expire duration | `168h` |
|
||||||
| ----- ADMIN Configuration----- | ---------- | ---------- |
|
| RUSTDESK_API_APP_DISABLE_PWD_LOGIN | disable password login | `false` |
|
||||||
| RUSTDESK_API_ADMIN_TITLE | Admin Title | `RustDesk Api Admin` |
|
| ----- ADMIN Configuration----- | ---------- | ---------- |
|
||||||
| RUSTDESK_API_ADMIN_HELLO | Admin welcome message, you can use `html` | |
|
| RUSTDESK_API_ADMIN_TITLE | Admin Title | `RustDesk Api Admin` |
|
||||||
| RUSTDESK_API_ADMIN_HELLO_FILE | Admin welcome message file,<br>will override `RUSTDESK_API_ADMIN_HELLO` | `./conf/admin/hello.html` |
|
| RUSTDESK_API_ADMIN_HELLO | Admin welcome message, you can use `html` | |
|
||||||
| ----- GIN Configuration ----- | --------------------------------------- | ----------------------------- |
|
| RUSTDESK_API_ADMIN_HELLO_FILE | Admin welcome message file,<br>will override `RUSTDESK_API_ADMIN_HELLO` | `./conf/admin/hello.html` |
|
||||||
| RUSTDESK_API_GIN_TRUST_PROXY | Trusted proxy IPs, separated by commas. | 192.168.1.2,192.168.1.3 |
|
| ----- GIN Configuration ----- | --------------------------------------- | ----------------------------- |
|
||||||
| ----- GORM Configuration ----- | --------------------------------------- | ----------------------------- |
|
| RUSTDESK_API_GIN_TRUST_PROXY | Trusted proxy IPs, separated by commas. | 192.168.1.2,192.168.1.3 |
|
||||||
| RUSTDESK_API_GORM_TYPE | Database type (`sqlite` or `mysql`). Default is `sqlite`. | sqlite |
|
| ----- GORM Configuration ----- | --------------------------------------- | ----------------------------- |
|
||||||
| RUSTDESK_API_GORM_MAX_IDLE_CONNS | Maximum idle connections | 10 |
|
| RUSTDESK_API_GORM_TYPE | Database type (`sqlite` or `mysql`). Default is `sqlite`. | sqlite |
|
||||||
| RUSTDESK_API_GORM_MAX_OPEN_CONNS | Maximum open connections | 100 |
|
| RUSTDESK_API_GORM_MAX_IDLE_CONNS | Maximum idle connections | 10 |
|
||||||
| RUSTDESK_API_RUSTDESK_PERSONAL | Open Personal Api 1:Enable,0:Disable | 1 |
|
| RUSTDESK_API_GORM_MAX_OPEN_CONNS | Maximum open connections | 100 |
|
||||||
| ----- MYSQL Configuration ----- | --------------------------------------- | ----------------------------- |
|
| RUSTDESK_API_RUSTDESK_PERSONAL | Open Personal Api 1:Enable,0:Disable | 1 |
|
||||||
| RUSTDESK_API_MYSQL_USERNAME | MySQL username | root |
|
| ----- MYSQL Configuration ----- | --------------------------------------- | ----------------------------- |
|
||||||
| RUSTDESK_API_MYSQL_PASSWORD | MySQL password | 111111 |
|
| RUSTDESK_API_MYSQL_USERNAME | MySQL username | root |
|
||||||
| RUSTDESK_API_MYSQL_ADDR | MySQL address | 192.168.1.66:3306 |
|
| RUSTDESK_API_MYSQL_PASSWORD | MySQL password | 111111 |
|
||||||
| RUSTDESK_API_MYSQL_DBNAME | MySQL database name | rustdesk |
|
| RUSTDESK_API_MYSQL_ADDR | MySQL address | 192.168.1.66:3306 |
|
||||||
| ----- RUSTDESK Configuration ----- | --------------------------------------- | ----------------------------- |
|
| RUSTDESK_API_MYSQL_DBNAME | MySQL database name | rustdesk |
|
||||||
| RUSTDESK_API_RUSTDESK_ID_SERVER | Rustdesk ID server address | 192.168.1.66:21116 |
|
| ----- RUSTDESK Configuration ----- | --------------------------------------- | ----------------------------- |
|
||||||
| RUSTDESK_API_RUSTDESK_RELAY_SERVER | Rustdesk relay server address | 192.168.1.66:21117 |
|
| RUSTDESK_API_RUSTDESK_ID_SERVER | Rustdesk ID server address | 192.168.1.66:21116 |
|
||||||
| RUSTDESK_API_RUSTDESK_API_SERVER | Rustdesk API server address | http://192.168.1.66:21114 |
|
| RUSTDESK_API_RUSTDESK_RELAY_SERVER | Rustdesk relay server address | 192.168.1.66:21117 |
|
||||||
| RUSTDESK_API_RUSTDESK_KEY | Rustdesk key | 123456789 |
|
| RUSTDESK_API_RUSTDESK_API_SERVER | Rustdesk API server address | http://192.168.1.66:21114 |
|
||||||
| RUSTDESK_API_RUSTDESK_KEY_FILE | Rustdesk key file | `./conf/data/id_ed25519.pub` |
|
| RUSTDESK_API_RUSTDESK_KEY | Rustdesk key | 123456789 |
|
||||||
| RUSTDESK_API_RUSTDESK<br/>_WEBCLIENT_MAGIC_QUERYONLINE | New online query method is enabled in the web client v2; '1': Enabled, '0': Disabled, not enabled by default | `0` |
|
| RUSTDESK_API_RUSTDESK_KEY_FILE | Rustdesk key file | `./conf/data/id_ed25519.pub` |
|
||||||
| ---- PROXY ----- | --------------- | ---------- |
|
| RUSTDESK_API_RUSTDESK<br/>_WEBCLIENT_MAGIC_QUERYONLINE | New online query method is enabled in the web client v2; '1': Enabled, '0': Disabled, not enabled by default | `0` |
|
||||||
| RUSTDESK_API_PROXY_ENABLE | proxy_enable :`false`, `true` | `false` |
|
| RUSTDESK_API_RUSTDESK_WS_HOST | Custom Websocket Host | `wss://192.168.1.123:1234` |
|
||||||
| RUSTDESK_API_PROXY_HOST | proxy_host | `http://127.0.0.1:1080` |
|
| ---- PROXY ----- | --------------- | ---------- |
|
||||||
| ----JWT---- | -------- | -------- |
|
| RUSTDESK_API_PROXY_ENABLE | proxy_enable :`false`, `true` | `false` |
|
||||||
| RUSTDESK_API_JWT_KEY | Custom JWT KEY, if empty JWT is not enabled.<br/>If `MUST_LOGIN` from `lejianwen/rustdesk-server` is not used, it is recommended to leave it empty. | |
|
| RUSTDESK_API_PROXY_HOST | proxy_host | `http://127.0.0.1:1080` |
|
||||||
| RUSTDESK_API_JWT_EXPIRE_DURATION | JWT expire duration | 360000 |
|
| ----JWT---- | -------- | -------- |
|
||||||
|
| RUSTDESK_API_JWT_KEY | Custom JWT KEY, if empty JWT is not enabled.<br/>If `MUST_LOGIN` from `lejianwen/rustdesk-server` is not used, it is recommended to leave it empty. | |
|
||||||
|
| RUSTDESK_API_JWT_EXPIRE_DURATION | JWT expire duration | `168h` |
|
||||||
|
|
||||||
### Installation Steps
|
### Installation Steps
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// @title 管理系统API
|
// @title 管理系统API
|
||||||
@@ -52,6 +51,10 @@ var resetPwdCmd = &cobra.Command{
|
|||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
pwd := args[0]
|
pwd := args[0]
|
||||||
admin := service.AllService.UserService.InfoById(1)
|
admin := service.AllService.UserService.InfoById(1)
|
||||||
|
if admin.Id == 0 {
|
||||||
|
global.Logger.Warn("user not found! ")
|
||||||
|
return
|
||||||
|
}
|
||||||
err := service.AllService.UserService.UpdatePassword(admin, pwd)
|
err := service.AllService.UserService.UpdatePassword(admin, pwd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
global.Logger.Error("reset password fail! ", err)
|
global.Logger.Error("reset password fail! ", err)
|
||||||
@@ -78,6 +81,10 @@ var resetUserPwdCmd = &cobra.Command{
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
u := service.AllService.UserService.InfoById(uint(uid))
|
u := service.AllService.UserService.InfoById(uint(uid))
|
||||||
|
if u.Id == 0 {
|
||||||
|
global.Logger.Warn("user not found! ")
|
||||||
|
return
|
||||||
|
}
|
||||||
err = service.AllService.UserService.UpdatePassword(u, pwd)
|
err = service.AllService.UserService.UpdatePassword(u, pwd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
global.Logger.Warn("reset password fail! ", err)
|
global.Logger.Warn("reset password fail! ", err)
|
||||||
@@ -145,7 +152,6 @@ func InitGlobal() {
|
|||||||
MaxOpenConns: global.Config.Gorm.MaxOpenConns,
|
MaxOpenConns: global.Config.Gorm.MaxOpenConns,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
DatabaseAutoUpdate()
|
|
||||||
|
|
||||||
//validator
|
//validator
|
||||||
global.ApiInitValidator()
|
global.ApiInitValidator()
|
||||||
@@ -162,13 +168,17 @@ func InitGlobal() {
|
|||||||
|
|
||||||
//jwt
|
//jwt
|
||||||
//fmt.Println(global.Config.Jwt.PrivateKey)
|
//fmt.Println(global.Config.Jwt.PrivateKey)
|
||||||
global.Jwt = jwt.NewJwt(global.Config.Jwt.Key, global.Config.Jwt.ExpireDuration*time.Second)
|
global.Jwt = jwt.NewJwt(global.Config.Jwt.Key, global.Config.Jwt.ExpireDuration)
|
||||||
|
|
||||||
//locker
|
//locker
|
||||||
global.Lock = lock.NewLocal()
|
global.Lock = lock.NewLocal()
|
||||||
|
|
||||||
|
//service
|
||||||
|
service.New(&global.Config, global.DB, global.Logger, global.Jwt, global.Lock)
|
||||||
|
|
||||||
|
DatabaseAutoUpdate()
|
||||||
}
|
}
|
||||||
func DatabaseAutoUpdate() {
|
func DatabaseAutoUpdate() {
|
||||||
version := 260
|
version := 262
|
||||||
|
|
||||||
db := global.DB
|
db := global.DB
|
||||||
|
|
||||||
@@ -212,6 +222,7 @@ func DatabaseAutoUpdate() {
|
|||||||
if v.Version < uint(version) {
|
if v.Version < uint(version) {
|
||||||
Migrate(uint(version))
|
Migrate(uint(version))
|
||||||
}
|
}
|
||||||
|
|
||||||
// 245迁移
|
// 245迁移
|
||||||
if v.Version < 245 {
|
if v.Version < 245 {
|
||||||
//oauths 表的 oauth_type 字段设置为 op同样的值
|
//oauths 表的 oauth_type 字段设置为 op同样的值
|
||||||
@@ -234,7 +245,7 @@ func DatabaseAutoUpdate() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
func Migrate(version uint) {
|
func Migrate(version uint) {
|
||||||
global.Logger.Info("migrating....", version)
|
global.Logger.Info("Migrating....", version)
|
||||||
err := global.DB.AutoMigrate(
|
err := global.DB.AutoMigrate(
|
||||||
&model.Version{},
|
&model.Version{},
|
||||||
&model.User{},
|
&model.User{},
|
||||||
@@ -252,6 +263,7 @@ func Migrate(version uint) {
|
|||||||
&model.AddressBookCollection{},
|
&model.AddressBookCollection{},
|
||||||
&model.AddressBookCollectionRule{},
|
&model.AddressBookCollectionRule{},
|
||||||
&model.ServerCmd{},
|
&model.ServerCmd{},
|
||||||
|
&model.DeviceGroup{},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
global.Logger.Error("migrate err :=>", err)
|
global.Logger.Error("migrate err :=>", err)
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ app:
|
|||||||
web-client: 1 # 1:启用 0:禁用
|
web-client: 1 # 1:启用 0:禁用
|
||||||
register: false #是否开启注册
|
register: false #是否开启注册
|
||||||
show-swagger: 0 # 1:启用 0:禁用
|
show-swagger: 0 # 1:启用 0:禁用
|
||||||
token-expire: 360000
|
token-expire: 168h
|
||||||
web-sso: true #web auth sso
|
web-sso: true #web auth sso
|
||||||
disable-pwd-login: false #禁用密码登录
|
disable-pwd-login: false #禁用密码登录
|
||||||
admin:
|
admin:
|
||||||
@@ -32,6 +32,7 @@ rustdesk:
|
|||||||
key-file: "/data/id_ed25519.pub"
|
key-file: "/data/id_ed25519.pub"
|
||||||
personal: 1
|
personal: 1
|
||||||
webclient-magic-queryonline: 0
|
webclient-magic-queryonline: 0
|
||||||
|
ws-host: "" #eg: wss://192.168.1.3:4443
|
||||||
logger:
|
logger:
|
||||||
path: "./runtime/log.txt"
|
path: "./runtime/log.txt"
|
||||||
level: "info" #trace,debug,info,warn,error,fatal
|
level: "info" #trace,debug,info,warn,error,fatal
|
||||||
@@ -41,11 +42,11 @@ proxy:
|
|||||||
host: "http://127.0.0.1:1080"
|
host: "http://127.0.0.1:1080"
|
||||||
jwt:
|
jwt:
|
||||||
key: ""
|
key: ""
|
||||||
expire-duration: 360000
|
expire-duration: 168h
|
||||||
ldap:
|
ldap:
|
||||||
enable: false
|
enable: false
|
||||||
url: "ldap://ldap.example.com:389"
|
url: "ldap://ldap.example.com:389"
|
||||||
tls: false
|
tls-ca-file: ""
|
||||||
tls-verify: false
|
tls-verify: false
|
||||||
base-dn: "dc=example,dc=com"
|
base-dn: "dc=example,dc=com"
|
||||||
bind-dn: "cn=admin,dc=example,dc=com"
|
bind-dn: "cn=admin,dc=example,dc=com"
|
||||||
@@ -63,21 +64,3 @@ ldap:
|
|||||||
sync: false # If true, the user will be synchronized to the database when the user logs in. If false, the user will be synchronized to the database when the user be created.
|
sync: false # If true, the user will be synchronized to the database when the user logs in. If false, the user will be synchronized to the database when the user be created.
|
||||||
admin-group: "cn=admin,dc=example,dc=com" # The group name of the admin group, if the user is in this group, the user will be an admin.
|
admin-group: "cn=admin,dc=example,dc=com" # The group name of the admin group, if the user is in this group, the user will be an admin.
|
||||||
|
|
||||||
redis:
|
|
||||||
addr: "127.0.0.1:6379"
|
|
||||||
password: ""
|
|
||||||
db: 0
|
|
||||||
cache:
|
|
||||||
type: "file"
|
|
||||||
file-dir: "./runtime/cache"
|
|
||||||
redis-addr: "127.0.0.1:6379"
|
|
||||||
redis-pwd: ""
|
|
||||||
redis-db: 0
|
|
||||||
oss:
|
|
||||||
access-key-id: ""
|
|
||||||
access-key-secret: ""
|
|
||||||
host: ""
|
|
||||||
callback-url: ""
|
|
||||||
expire-time: 30
|
|
||||||
max-byte: 10240
|
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -13,12 +14,12 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type App struct {
|
type App struct {
|
||||||
WebClient int `mapstructure:"web-client"`
|
WebClient int `mapstructure:"web-client"`
|
||||||
Register bool `mapstructure:"register"`
|
Register bool `mapstructure:"register"`
|
||||||
ShowSwagger int `mapstructure:"show-swagger"`
|
ShowSwagger int `mapstructure:"show-swagger"`
|
||||||
TokenExpire int `mapstructure:"token-expire"`
|
TokenExpire time.Duration `mapstructure:"token-expire"`
|
||||||
WebSso bool `mapstructure:"web-sso"`
|
WebSso bool `mapstructure:"web-sso"`
|
||||||
DisablePwdLogin bool `mapstructure:"disable-pwd-login"`
|
DisablePwdLogin bool `mapstructure:"disable-pwd-login"`
|
||||||
}
|
}
|
||||||
type Admin struct {
|
type Admin struct {
|
||||||
Title string `mapstructure:"title"`
|
Title string `mapstructure:"title"`
|
||||||
@@ -73,7 +74,7 @@ func Init(rowVal *Config, path string) *viper.Viper {
|
|||||||
})
|
})
|
||||||
*/
|
*/
|
||||||
if err := v.Unmarshal(rowVal); err != nil {
|
if err := v.Unmarshal(rowVal); err != nil {
|
||||||
fmt.Println(err)
|
panic(fmt.Errorf("Fatal error config: %s \n", err))
|
||||||
}
|
}
|
||||||
rowVal.Rustdesk.LoadKeyFile()
|
rowVal.Rustdesk.LoadKeyFile()
|
||||||
rowVal.Rustdesk.ParsePort()
|
rowVal.Rustdesk.ParsePort()
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ type LdapUser struct {
|
|||||||
type Ldap struct {
|
type Ldap struct {
|
||||||
Enable bool `mapstructure:"enable"`
|
Enable bool `mapstructure:"enable"`
|
||||||
Url string `mapstructure:"url"`
|
Url string `mapstructure:"url"`
|
||||||
TLS bool `mapstructure:"tls"`
|
TlsCaFile string `mapstructure:"tls-ca-file"`
|
||||||
TlsVerify bool `mapstructure:"tls-verify"`
|
TlsVerify bool `mapstructure:"tls-verify"`
|
||||||
BaseDn string `mapstructure:"base-dn"`
|
BaseDn string `mapstructure:"base-dn"`
|
||||||
BindDn string `mapstructure:"bind-dn"`
|
BindDn string `mapstructure:"bind-dn"`
|
||||||
|
|||||||
@@ -21,7 +21,8 @@ type Rustdesk struct {
|
|||||||
KeyFile string `mapstructure:"key-file"`
|
KeyFile string `mapstructure:"key-file"`
|
||||||
Personal int `mapstructure:"personal"`
|
Personal int `mapstructure:"personal"`
|
||||||
//webclient-magic-queryonline
|
//webclient-magic-queryonline
|
||||||
WebclientMagicQueryonline int `mapstructure:"webclient-magic-queryonline"`
|
WebclientMagicQueryonline int `mapstructure:"webclient-magic-queryonline"`
|
||||||
|
WsHost string `mapstructure:"ws-host"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rd *Rustdesk) LoadKeyFile() {
|
func (rd *Rustdesk) LoadKeyFile() {
|
||||||
|
|||||||
@@ -1407,6 +1407,280 @@ const docTemplateadmin = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/admin/device_group/create": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"token": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "创建设备群组",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"设备群组"
|
||||||
|
],
|
||||||
|
"summary": "创建设备群组",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "设备群组信息",
|
||||||
|
"name": "body",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/admin.DeviceGroupForm"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/response.Response"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"data": {
|
||||||
|
"$ref": "#/definitions/model.DeviceGroup"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.Response"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/admin/device_group/delete": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"token": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "设备群组删除",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"设备群组"
|
||||||
|
],
|
||||||
|
"summary": "设备群组删除",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "群组信息",
|
||||||
|
"name": "body",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/admin.DeviceGroupForm"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.Response"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.Response"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/admin/device_group/detail/{id}": {
|
||||||
|
"get": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"token": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "设备群组详情",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"设备群组"
|
||||||
|
],
|
||||||
|
"summary": "设备群组详情",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "ID",
|
||||||
|
"name": "id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/response.Response"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"data": {
|
||||||
|
"$ref": "#/definitions/model.Group"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.Response"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/admin/device_group/list": {
|
||||||
|
"get": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"token": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "群组列表",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"群组"
|
||||||
|
],
|
||||||
|
"summary": "群组列表",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "页码",
|
||||||
|
"name": "page",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "页大小",
|
||||||
|
"name": "page_size",
|
||||||
|
"in": "query"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/response.Response"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"data": {
|
||||||
|
"$ref": "#/definitions/model.GroupList"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.Response"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/admin/device_group/update": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"token": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "设备群组编辑",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"设备群组"
|
||||||
|
],
|
||||||
|
"summary": "设备群组编辑",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "群组信息",
|
||||||
|
"name": "body",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/admin.DeviceGroupForm"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/response.Response"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"data": {
|
||||||
|
"$ref": "#/definitions/model.Group"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.Response"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/admin/file/oss_token": {
|
"/admin/file/oss_token": {
|
||||||
"get": {
|
"get": {
|
||||||
"security": [
|
"security": [
|
||||||
@@ -1783,7 +2057,7 @@ const docTemplateadmin = `{
|
|||||||
"in": "body",
|
"in": "body",
|
||||||
"required": true,
|
"required": true,
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "#/definitions/github_com_lejianwen_rustdesk-api_http_request_admin.Login"
|
"$ref": "#/definitions/github_com_lejianwen_rustdesk-api_v2_http_request_admin.Login"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -5219,6 +5493,20 @@ const docTemplateadmin = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"admin.DeviceGroupForm": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"name"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"admin.GroupForm": {
|
"admin.GroupForm": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
@@ -5306,6 +5594,12 @@ const docTemplateadmin = `{
|
|||||||
"op": {
|
"op": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"pkce_enable": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"pkce_method": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"redirect_url": {
|
"redirect_url": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@@ -5334,6 +5628,9 @@ const docTemplateadmin = `{
|
|||||||
"cpu": {
|
"cpu": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"group_id": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
"hostname": {
|
"hostname": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@@ -5521,7 +5818,7 @@ const docTemplateadmin = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"github_com_lejianwen_rustdesk-api_http_request_admin.Login": {
|
"github_com_lejianwen_rustdesk-api_v2_http_request_admin.Login": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
"password",
|
"password",
|
||||||
@@ -5842,6 +6139,23 @@ const docTemplateadmin = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"model.DeviceGroup": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"created_at": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"id": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"model.Group": {
|
"model.Group": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
@@ -5973,6 +6287,12 @@ const docTemplateadmin = `{
|
|||||||
"op": {
|
"op": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"pkce_enable": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"pkce_method": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"redirect_url": {
|
"redirect_url": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@@ -6013,6 +6333,9 @@ const docTemplateadmin = `{
|
|||||||
"created_at": {
|
"created_at": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"group_id": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
"hostname": {
|
"hostname": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1400,6 +1400,280 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/admin/device_group/create": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"token": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "创建设备群组",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"设备群组"
|
||||||
|
],
|
||||||
|
"summary": "创建设备群组",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "设备群组信息",
|
||||||
|
"name": "body",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/admin.DeviceGroupForm"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/response.Response"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"data": {
|
||||||
|
"$ref": "#/definitions/model.DeviceGroup"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.Response"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/admin/device_group/delete": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"token": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "设备群组删除",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"设备群组"
|
||||||
|
],
|
||||||
|
"summary": "设备群组删除",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "群组信息",
|
||||||
|
"name": "body",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/admin.DeviceGroupForm"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.Response"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.Response"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/admin/device_group/detail/{id}": {
|
||||||
|
"get": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"token": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "设备群组详情",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"设备群组"
|
||||||
|
],
|
||||||
|
"summary": "设备群组详情",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "ID",
|
||||||
|
"name": "id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/response.Response"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"data": {
|
||||||
|
"$ref": "#/definitions/model.Group"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.Response"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/admin/device_group/list": {
|
||||||
|
"get": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"token": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "群组列表",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"群组"
|
||||||
|
],
|
||||||
|
"summary": "群组列表",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "页码",
|
||||||
|
"name": "page",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "页大小",
|
||||||
|
"name": "page_size",
|
||||||
|
"in": "query"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/response.Response"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"data": {
|
||||||
|
"$ref": "#/definitions/model.GroupList"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.Response"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/admin/device_group/update": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"token": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "设备群组编辑",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"设备群组"
|
||||||
|
],
|
||||||
|
"summary": "设备群组编辑",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "群组信息",
|
||||||
|
"name": "body",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/admin.DeviceGroupForm"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/response.Response"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"data": {
|
||||||
|
"$ref": "#/definitions/model.Group"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.Response"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/admin/file/oss_token": {
|
"/admin/file/oss_token": {
|
||||||
"get": {
|
"get": {
|
||||||
"security": [
|
"security": [
|
||||||
@@ -1776,7 +2050,7 @@
|
|||||||
"in": "body",
|
"in": "body",
|
||||||
"required": true,
|
"required": true,
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "#/definitions/github_com_lejianwen_rustdesk-api_http_request_admin.Login"
|
"$ref": "#/definitions/github_com_lejianwen_rustdesk-api_v2_http_request_admin.Login"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -5212,6 +5486,20 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"admin.DeviceGroupForm": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"name"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"admin.GroupForm": {
|
"admin.GroupForm": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
@@ -5299,6 +5587,12 @@
|
|||||||
"op": {
|
"op": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"pkce_enable": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"pkce_method": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"redirect_url": {
|
"redirect_url": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@@ -5327,6 +5621,9 @@
|
|||||||
"cpu": {
|
"cpu": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"group_id": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
"hostname": {
|
"hostname": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@@ -5514,7 +5811,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"github_com_lejianwen_rustdesk-api_http_request_admin.Login": {
|
"github_com_lejianwen_rustdesk-api_v2_http_request_admin.Login": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
"password",
|
"password",
|
||||||
@@ -5835,6 +6132,23 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"model.DeviceGroup": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"created_at": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"id": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"model.Group": {
|
"model.Group": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
@@ -5966,6 +6280,12 @@
|
|||||||
"op": {
|
"op": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"pkce_enable": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"pkce_method": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"redirect_url": {
|
"redirect_url": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@@ -6006,6 +6326,9 @@
|
|||||||
"created_at": {
|
"created_at": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"group_id": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
"hostname": {
|
"hostname": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -77,6 +77,15 @@ definitions:
|
|||||||
- new_password
|
- new_password
|
||||||
- old_password
|
- old_password
|
||||||
type: object
|
type: object
|
||||||
|
admin.DeviceGroupForm:
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: integer
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- name
|
||||||
|
type: object
|
||||||
admin.GroupForm:
|
admin.GroupForm:
|
||||||
properties:
|
properties:
|
||||||
id:
|
id:
|
||||||
@@ -130,6 +139,10 @@ definitions:
|
|||||||
type: string
|
type: string
|
||||||
op:
|
op:
|
||||||
type: string
|
type: string
|
||||||
|
pkce_enable:
|
||||||
|
type: boolean
|
||||||
|
pkce_method:
|
||||||
|
type: string
|
||||||
redirect_url:
|
redirect_url:
|
||||||
type: string
|
type: string
|
||||||
scopes:
|
scopes:
|
||||||
@@ -153,6 +166,8 @@ definitions:
|
|||||||
properties:
|
properties:
|
||||||
cpu:
|
cpu:
|
||||||
type: string
|
type: string
|
||||||
|
group_id:
|
||||||
|
type: integer
|
||||||
hostname:
|
hostname:
|
||||||
type: string
|
type: string
|
||||||
id:
|
id:
|
||||||
@@ -278,7 +293,7 @@ definitions:
|
|||||||
required:
|
required:
|
||||||
- ids
|
- ids
|
||||||
type: object
|
type: object
|
||||||
github_com_lejianwen_rustdesk-api_http_request_admin.Login:
|
github_com_lejianwen_rustdesk-api_v2_http_request_admin.Login:
|
||||||
properties:
|
properties:
|
||||||
captcha:
|
captcha:
|
||||||
type: string
|
type: string
|
||||||
@@ -492,6 +507,17 @@ definitions:
|
|||||||
total:
|
total:
|
||||||
type: integer
|
type: integer
|
||||||
type: object
|
type: object
|
||||||
|
model.DeviceGroup:
|
||||||
|
properties:
|
||||||
|
created_at:
|
||||||
|
type: string
|
||||||
|
id:
|
||||||
|
type: integer
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
updated_at:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
model.Group:
|
model.Group:
|
||||||
properties:
|
properties:
|
||||||
created_at:
|
created_at:
|
||||||
@@ -579,6 +605,10 @@ definitions:
|
|||||||
type: string
|
type: string
|
||||||
op:
|
op:
|
||||||
type: string
|
type: string
|
||||||
|
pkce_enable:
|
||||||
|
type: boolean
|
||||||
|
pkce_method:
|
||||||
|
type: string
|
||||||
redirect_url:
|
redirect_url:
|
||||||
type: string
|
type: string
|
||||||
scopes:
|
scopes:
|
||||||
@@ -605,6 +635,8 @@ definitions:
|
|||||||
type: string
|
type: string
|
||||||
created_at:
|
created_at:
|
||||||
type: string
|
type: string
|
||||||
|
group_id:
|
||||||
|
type: integer
|
||||||
hostname:
|
hostname:
|
||||||
type: string
|
type: string
|
||||||
id:
|
id:
|
||||||
@@ -1610,6 +1642,167 @@ paths:
|
|||||||
summary: RUSTDESK服务配置
|
summary: RUSTDESK服务配置
|
||||||
tags:
|
tags:
|
||||||
- ADMIN
|
- ADMIN
|
||||||
|
/admin/device_group/create:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 创建设备群组
|
||||||
|
parameters:
|
||||||
|
- description: 设备群组信息
|
||||||
|
in: body
|
||||||
|
name: body
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/admin.DeviceGroupForm'
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/definitions/response.Response'
|
||||||
|
- properties:
|
||||||
|
data:
|
||||||
|
$ref: '#/definitions/model.DeviceGroup'
|
||||||
|
type: object
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Response'
|
||||||
|
security:
|
||||||
|
- token: []
|
||||||
|
summary: 创建设备群组
|
||||||
|
tags:
|
||||||
|
- 设备群组
|
||||||
|
/admin/device_group/delete:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 设备群组删除
|
||||||
|
parameters:
|
||||||
|
- description: 群组信息
|
||||||
|
in: body
|
||||||
|
name: body
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/admin.DeviceGroupForm'
|
||||||
|
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/device_group/detail/{id}:
|
||||||
|
get:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 设备群组详情
|
||||||
|
parameters:
|
||||||
|
- description: ID
|
||||||
|
in: path
|
||||||
|
name: id
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/definitions/response.Response'
|
||||||
|
- properties:
|
||||||
|
data:
|
||||||
|
$ref: '#/definitions/model.Group'
|
||||||
|
type: object
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Response'
|
||||||
|
security:
|
||||||
|
- token: []
|
||||||
|
summary: 设备群组详情
|
||||||
|
tags:
|
||||||
|
- 设备群组
|
||||||
|
/admin/device_group/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.GroupList'
|
||||||
|
type: object
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Response'
|
||||||
|
security:
|
||||||
|
- token: []
|
||||||
|
summary: 群组列表
|
||||||
|
tags:
|
||||||
|
- 群组
|
||||||
|
/admin/device_group/update:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 设备群组编辑
|
||||||
|
parameters:
|
||||||
|
- description: 群组信息
|
||||||
|
in: body
|
||||||
|
name: body
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/admin.DeviceGroupForm'
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/definitions/response.Response'
|
||||||
|
- properties:
|
||||||
|
data:
|
||||||
|
$ref: '#/definitions/model.Group'
|
||||||
|
type: object
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Response'
|
||||||
|
security:
|
||||||
|
- token: []
|
||||||
|
summary: 设备群组编辑
|
||||||
|
tags:
|
||||||
|
- 设备群组
|
||||||
/admin/file/oss_token:
|
/admin/file/oss_token:
|
||||||
get:
|
get:
|
||||||
consumes:
|
consumes:
|
||||||
@@ -1830,7 +2023,7 @@ paths:
|
|||||||
name: body
|
name: body
|
||||||
required: true
|
required: true
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/github_com_lejianwen_rustdesk-api_http_request_admin.Login'
|
$ref: '#/definitions/github_com_lejianwen_rustdesk-api_v2_http_request_admin.Login'
|
||||||
produces:
|
produces:
|
||||||
- application/json
|
- application/json
|
||||||
responses:
|
responses:
|
||||||
|
|||||||
@@ -767,6 +767,66 @@ const docTemplateapi = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/device-group/accessible": {
|
||||||
|
"get": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"BearerAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "机器",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"群组"
|
||||||
|
],
|
||||||
|
"summary": "设备",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "页码",
|
||||||
|
"name": "page",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "每页数量",
|
||||||
|
"name": "pageSize",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "状态",
|
||||||
|
"name": "status",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "accessible",
|
||||||
|
"name": "accessible",
|
||||||
|
"in": "query"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.DataResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.Response"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/heartbeat": {
|
"/heartbeat": {
|
||||||
"post": {
|
"post": {
|
||||||
"description": "心跳",
|
"description": "心跳",
|
||||||
|
|||||||
@@ -760,6 +760,66 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/device-group/accessible": {
|
||||||
|
"get": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"BearerAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "机器",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"群组"
|
||||||
|
],
|
||||||
|
"summary": "设备",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "页码",
|
||||||
|
"name": "page",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "每页数量",
|
||||||
|
"name": "pageSize",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "状态",
|
||||||
|
"name": "status",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "accessible",
|
||||||
|
"name": "accessible",
|
||||||
|
"in": "query"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.DataResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.Response"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/heartbeat": {
|
"/heartbeat": {
|
||||||
"post": {
|
"post": {
|
||||||
"description": "心跳",
|
"description": "心跳",
|
||||||
|
|||||||
@@ -671,6 +671,44 @@ paths:
|
|||||||
summary: 用户信息
|
summary: 用户信息
|
||||||
tags:
|
tags:
|
||||||
- 用户
|
- 用户
|
||||||
|
/device-group/accessible:
|
||||||
|
get:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 机器
|
||||||
|
parameters:
|
||||||
|
- description: 页码
|
||||||
|
in: query
|
||||||
|
name: page
|
||||||
|
type: integer
|
||||||
|
- description: 每页数量
|
||||||
|
in: query
|
||||||
|
name: pageSize
|
||||||
|
type: integer
|
||||||
|
- description: 状态
|
||||||
|
in: query
|
||||||
|
name: status
|
||||||
|
type: integer
|
||||||
|
- description: accessible
|
||||||
|
in: query
|
||||||
|
name: accessible
|
||||||
|
type: string
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.DataResponse'
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Response'
|
||||||
|
security:
|
||||||
|
- BearerAuth: []
|
||||||
|
summary: 设备
|
||||||
|
tags:
|
||||||
|
- 群组
|
||||||
/heartbeat:
|
/heartbeat:
|
||||||
post:
|
post:
|
||||||
consumes:
|
consumes:
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import (
|
|||||||
en_translations "github.com/go-playground/validator/v10/translations/en"
|
en_translations "github.com/go-playground/validator/v10/translations/en"
|
||||||
es_translations "github.com/go-playground/validator/v10/translations/es"
|
es_translations "github.com/go-playground/validator/v10/translations/es"
|
||||||
fr_translations "github.com/go-playground/validator/v10/translations/fr"
|
fr_translations "github.com/go-playground/validator/v10/translations/fr"
|
||||||
|
ko_translations "github.com/go-playground/validator/v10/translations/ko"
|
||||||
ru_translations "github.com/go-playground/validator/v10/translations/ru"
|
ru_translations "github.com/go-playground/validator/v10/translations/ru"
|
||||||
zh_translations "github.com/go-playground/validator/v10/translations/zh"
|
zh_translations "github.com/go-playground/validator/v10/translations/zh"
|
||||||
zh_tw_translations "github.com/go-playground/validator/v10/translations/zh_tw"
|
zh_tw_translations "github.com/go-playground/validator/v10/translations/zh_tw"
|
||||||
@@ -51,8 +52,7 @@ func ApiInitValidator() {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
//validate没有ko的翻译,使用zh的翻译
|
err = ko_translations.RegisterDefaultTranslations(validate, koTrans)
|
||||||
err = zh_translations.RegisterDefaultTranslations(validate, koTrans)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|||||||
15
go.mod
15
go.mod
@@ -10,7 +10,7 @@ require (
|
|||||||
github.com/gin-gonic/gin v1.9.0
|
github.com/gin-gonic/gin v1.9.0
|
||||||
github.com/go-playground/locales v0.14.1
|
github.com/go-playground/locales v0.14.1
|
||||||
github.com/go-playground/universal-translator v0.18.1
|
github.com/go-playground/universal-translator v0.18.1
|
||||||
github.com/go-playground/validator/v10 v10.11.2
|
github.com/go-playground/validator/v10 v10.26.0
|
||||||
github.com/go-redis/redis/v8 v8.11.4
|
github.com/go-redis/redis/v8 v8.11.4
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.1
|
github.com/golang-jwt/jwt/v5 v5.2.1
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
@@ -22,7 +22,7 @@ require (
|
|||||||
github.com/swaggo/gin-swagger v1.6.0
|
github.com/swaggo/gin-swagger v1.6.0
|
||||||
github.com/swaggo/swag v1.16.3
|
github.com/swaggo/swag v1.16.3
|
||||||
golang.org/x/oauth2 v0.23.0
|
golang.org/x/oauth2 v0.23.0
|
||||||
golang.org/x/text v0.21.0
|
golang.org/x/text v0.22.0
|
||||||
gorm.io/driver/mysql v1.5.7
|
gorm.io/driver/mysql v1.5.7
|
||||||
gorm.io/driver/sqlite v1.5.6
|
gorm.io/driver/sqlite v1.5.6
|
||||||
gorm.io/gorm v1.25.7
|
gorm.io/gorm v1.25.7
|
||||||
@@ -36,9 +36,12 @@ require (
|
|||||||
github.com/bytedance/sonic v1.8.0 // indirect
|
github.com/bytedance/sonic v1.8.0 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
||||||
|
github.com/coreos/go-oidc/v3 v3.12.0 // indirect
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
|
||||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||||
github.com/go-asn1-ber/asn1-ber v1.5.7 // indirect
|
github.com/go-asn1-ber/asn1-ber v1.5.7 // indirect
|
||||||
|
github.com/go-jose/go-jose/v4 v4.0.2 // indirect
|
||||||
github.com/go-ldap/ldap/v3 v3.4.10 // indirect
|
github.com/go-ldap/ldap/v3 v3.4.10 // indirect
|
||||||
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
||||||
github.com/go-openapi/jsonreference v0.19.6 // indirect
|
github.com/go-openapi/jsonreference v0.19.6 // indirect
|
||||||
@@ -54,7 +57,7 @@ require (
|
|||||||
github.com/josharian/intern v1.0.0 // indirect
|
github.com/josharian/intern v1.0.0 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.0.9 // indirect
|
github.com/klauspost/cpuid/v2 v2.0.9 // indirect
|
||||||
github.com/leodido/go-urn v1.2.1 // indirect
|
github.com/leodido/go-urn v1.4.0 // indirect
|
||||||
github.com/magiconair/properties v1.8.5 // indirect
|
github.com/magiconair/properties v1.8.5 // indirect
|
||||||
github.com/mailru/easyjson v0.7.7 // indirect
|
github.com/mailru/easyjson v0.7.7 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.17 // indirect
|
github.com/mattn/go-isatty v0.0.17 // indirect
|
||||||
@@ -73,10 +76,10 @@ require (
|
|||||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
github.com/ugorji/go/codec v1.2.9 // indirect
|
github.com/ugorji/go/codec v1.2.9 // indirect
|
||||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
|
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
|
||||||
golang.org/x/crypto v0.31.0 // indirect
|
golang.org/x/crypto v0.33.0 // indirect
|
||||||
golang.org/x/image v0.13.0 // indirect
|
golang.org/x/image v0.13.0 // indirect
|
||||||
golang.org/x/net v0.33.0 // indirect
|
golang.org/x/net v0.34.0 // indirect
|
||||||
golang.org/x/sys v0.28.0 // indirect
|
golang.org/x/sys v0.30.0 // indirect
|
||||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
|
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
|
||||||
google.golang.org/protobuf v1.33.0 // indirect
|
google.golang.org/protobuf v1.33.0 // indirect
|
||||||
gopkg.in/ini.v1 v1.63.2 // indirect
|
gopkg.in/ini.v1 v1.63.2 // indirect
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ func (abcr *AddressBookCollectionRule) CheckForm(t *model.AddressBookCollectionR
|
|||||||
//check to_id
|
//check to_id
|
||||||
if t.Type == model.ShareAddressBookRuleTypePersonal {
|
if t.Type == model.ShareAddressBookRuleTypePersonal {
|
||||||
if t.ToId == t.UserId {
|
if t.ToId == t.UserId {
|
||||||
return "ParamsError", false
|
return "CannotShareToSelf", false
|
||||||
}
|
}
|
||||||
tou := service.AllService.UserService.InfoById(t.ToId)
|
tou := service.AllService.UserService.InfoById(t.ToId)
|
||||||
if tou.Id == 0 {
|
if tou.Id == 0 {
|
||||||
@@ -135,7 +135,7 @@ func (abcr *AddressBookCollectionRule) CheckForm(t *model.AddressBookCollectionR
|
|||||||
return "ParamsError", false
|
return "ParamsError", false
|
||||||
}
|
}
|
||||||
// 重复检查
|
// 重复检查
|
||||||
ex := service.AllService.AddressBookService.RulePersonalInfoByToIdAndCid(t.ToId, t.CollectionId)
|
ex := service.AllService.AddressBookService.RuleInfoByToIdAndCid(t.Type, t.ToId, t.CollectionId)
|
||||||
if t.Id == 0 && ex.Id > 0 {
|
if t.Id == 0 && ex.Id > 0 {
|
||||||
return "ItemExists", false
|
return "ItemExists", false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/lejianwen/rustdesk-api/v2/global"
|
"github.com/lejianwen/rustdesk-api/v2/global"
|
||||||
"github.com/lejianwen/rustdesk-api/v2/http/response"
|
"github.com/lejianwen/rustdesk-api/v2/http/response"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/model"
|
||||||
"github.com/lejianwen/rustdesk-api/v2/service"
|
"github.com/lejianwen/rustdesk-api/v2/service"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -60,13 +61,22 @@ func (co *Config) AppConfig(c *gin.Context) {
|
|||||||
// @Security token
|
// @Security token
|
||||||
func (co *Config) AdminConfig(c *gin.Context) {
|
func (co *Config) AdminConfig(c *gin.Context) {
|
||||||
|
|
||||||
u := service.AllService.UserService.CurUser(c)
|
u := &model.User{}
|
||||||
if u == nil || u.Id == 0 {
|
token := c.GetHeader("api-token")
|
||||||
|
if token != "" {
|
||||||
|
u, _ = service.AllService.UserService.InfoByAccessToken(token)
|
||||||
|
if !service.AllService.UserService.CheckUserEnable(u) {
|
||||||
|
u.Id = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if u.Id == 0 {
|
||||||
response.Success(c, &gin.H{
|
response.Success(c, &gin.H{
|
||||||
"title": global.Config.Admin.Title,
|
"title": global.Config.Admin.Title,
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
hello := global.Config.Admin.Hello
|
hello := global.Config.Admin.Hello
|
||||||
helloFile := global.Config.Admin.HelloFile
|
helloFile := global.Config.Admin.HelloFile
|
||||||
if helloFile != "" {
|
if helloFile != "" {
|
||||||
|
|||||||
160
http/controller/admin/deviceGroup.go
Normal file
160
http/controller/admin/deviceGroup.go
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
package admin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/global"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/http/response"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/service"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DeviceGroup struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detail 设备群组
|
||||||
|
// @Tags 设备群组
|
||||||
|
// @Summary 设备群组详情
|
||||||
|
// @Description 设备群组详情
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param id path int true "ID"
|
||||||
|
// @Success 200 {object} response.Response{data=model.Group}
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/device_group/detail/{id} [get]
|
||||||
|
// @Security token
|
||||||
|
func (ct *DeviceGroup) Detail(c *gin.Context) {
|
||||||
|
id := c.Param("id")
|
||||||
|
iid, _ := strconv.Atoi(id)
|
||||||
|
u := service.AllService.GroupService.DeviceGroupInfoById(uint(iid))
|
||||||
|
if u.Id > 0 {
|
||||||
|
response.Success(c, u)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create 创建设备群组
|
||||||
|
// @Tags 设备群组
|
||||||
|
// @Summary 创建设备群组
|
||||||
|
// @Description 创建设备群组
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param body body admin.DeviceGroupForm true "设备群组信息"
|
||||||
|
// @Success 200 {object} response.Response{data=model.DeviceGroup}
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/device_group/create [post]
|
||||||
|
// @Security token
|
||||||
|
func (ct *DeviceGroup) Create(c *gin.Context) {
|
||||||
|
f := &admin.DeviceGroupForm{}
|
||||||
|
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 := f.ToDeviceGroup()
|
||||||
|
err := service.AllService.GroupService.DeviceGroupCreate(u)
|
||||||
|
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.GroupList}
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/device_group/list [get]
|
||||||
|
// @Security token
|
||||||
|
func (ct *DeviceGroup) 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
|
||||||
|
}
|
||||||
|
res := service.AllService.GroupService.DeviceGroupList(query.Page, query.PageSize, nil)
|
||||||
|
response.Success(c, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update 编辑
|
||||||
|
// @Tags 设备群组
|
||||||
|
// @Summary 设备群组编辑
|
||||||
|
// @Description 设备群组编辑
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param body body admin.DeviceGroupForm true "群组信息"
|
||||||
|
// @Success 200 {object} response.Response{data=model.Group}
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/device_group/update [post]
|
||||||
|
// @Security token
|
||||||
|
func (ct *DeviceGroup) Update(c *gin.Context) {
|
||||||
|
f := &admin.DeviceGroupForm{}
|
||||||
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if f.Id == 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
errList := global.Validator.ValidStruct(c, f)
|
||||||
|
if len(errList) > 0 {
|
||||||
|
response.Fail(c, 101, errList[0])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
u := f.ToDeviceGroup()
|
||||||
|
err := service.AllService.GroupService.DeviceGroupUpdate(u)
|
||||||
|
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.DeviceGroupForm true "群组信息"
|
||||||
|
// @Success 200 {object} response.Response
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/device_group/delete [post]
|
||||||
|
// @Security token
|
||||||
|
func (ct *DeviceGroup) Delete(c *gin.Context) {
|
||||||
|
f := &admin.DeviceGroupForm{}
|
||||||
|
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.GroupService.DeviceGroupInfoById(f.Id)
|
||||||
|
if u.Id > 0 {
|
||||||
|
err := service.AllService.GroupService.DeviceGroupDelete(u)
|
||||||
|
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"))
|
||||||
|
}
|
||||||
@@ -283,13 +283,13 @@ func (ct *Login) OidcAuth(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err, code, url := service.AllService.OauthService.BeginAuth(f.Op)
|
err, state, verifier, nonce, url := service.AllService.OauthService.BeginAuth(f.Op)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.Error(c, response.TranslateMsg(c, err.Error()))
|
response.Error(c, response.TranslateMsg(c, err.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
service.AllService.OauthService.SetOauthCache(code, &service.OauthCacheItem{
|
service.AllService.OauthService.SetOauthCache(state, &service.OauthCacheItem{
|
||||||
Action: service.OauthActionTypeLogin,
|
Action: service.OauthActionTypeLogin,
|
||||||
Op: f.Op,
|
Op: f.Op,
|
||||||
Id: f.Id,
|
Id: f.Id,
|
||||||
@@ -297,10 +297,12 @@ func (ct *Login) OidcAuth(c *gin.Context) {
|
|||||||
// DeviceOs: ct.Platform(c),
|
// DeviceOs: ct.Platform(c),
|
||||||
DeviceOs: f.DeviceInfo.Os,
|
DeviceOs: f.DeviceInfo.Os,
|
||||||
Uuid: f.Uuid,
|
Uuid: f.Uuid,
|
||||||
|
Verifier: verifier,
|
||||||
|
Nonce: nonce,
|
||||||
}, 5*60)
|
}, 5*60)
|
||||||
|
|
||||||
response.Success(c, gin.H{
|
response.Success(c, gin.H{
|
||||||
"code": code,
|
"code": state,
|
||||||
"url": url,
|
"url": url,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,21 +100,21 @@ func (abcr *AddressBookCollectionRule) CheckForm(u *model.User, t *model.Address
|
|||||||
//check to_id
|
//check to_id
|
||||||
if t.Type == model.ShareAddressBookRuleTypePersonal {
|
if t.Type == model.ShareAddressBookRuleTypePersonal {
|
||||||
if t.ToId == t.UserId {
|
if t.ToId == t.UserId {
|
||||||
return "ParamsError", false
|
return "CannotShareToSelf", false
|
||||||
}
|
}
|
||||||
tou := service.AllService.UserService.InfoById(t.ToId)
|
tou := service.AllService.UserService.InfoById(t.ToId)
|
||||||
if tou.Id == 0 {
|
if tou.Id == 0 {
|
||||||
return "ItemNotFound", false
|
return "ItemNotFound", false
|
||||||
}
|
}
|
||||||
//非管理员不能分享给非本组织用户
|
//非管理员不能分享给非本组织用户
|
||||||
if tou.GroupId != u.GroupId {
|
//if tou.GroupId != u.GroupId {
|
||||||
return "NoAccess", false
|
// return "NoAccess", false
|
||||||
}
|
//}
|
||||||
} else if t.Type == model.ShareAddressBookRuleTypeGroup {
|
} else if t.Type == model.ShareAddressBookRuleTypeGroup {
|
||||||
//非管理员不能分享给其他组
|
//非管理员不能分享给其他组
|
||||||
if t.ToId != u.GroupId {
|
//if t.ToId != u.GroupId {
|
||||||
return "NoAccess", false
|
// return "NoAccess", false
|
||||||
}
|
//}
|
||||||
|
|
||||||
tog := service.AllService.GroupService.InfoById(t.ToId)
|
tog := service.AllService.GroupService.InfoById(t.ToId)
|
||||||
if tog.Id == 0 {
|
if tog.Id == 0 {
|
||||||
@@ -124,7 +124,7 @@ func (abcr *AddressBookCollectionRule) CheckForm(u *model.User, t *model.Address
|
|||||||
return "ParamsError", false
|
return "ParamsError", false
|
||||||
}
|
}
|
||||||
// 重复检查
|
// 重复检查
|
||||||
ex := service.AllService.AddressBookService.RulePersonalInfoByToIdAndCid(t.ToId, t.CollectionId)
|
ex := service.AllService.AddressBookService.RuleInfoByToIdAndCid(t.Type, t.ToId, t.CollectionId)
|
||||||
if t.Id == 0 && ex.Id > 0 {
|
if t.Id == 0 && ex.Id > 0 {
|
||||||
return "ItemExists", false
|
return "ItemExists", false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,20 +43,22 @@ func (o *Oauth) ToBind(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err, code, url := service.AllService.OauthService.BeginAuth(f.Op)
|
err, state, verifier, nonce, url := service.AllService.OauthService.BeginAuth(f.Op)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.Error(c, response.TranslateMsg(c, err.Error()))
|
response.Error(c, response.TranslateMsg(c, err.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
service.AllService.OauthService.SetOauthCache(code, &service.OauthCacheItem{
|
service.AllService.OauthService.SetOauthCache(state, &service.OauthCacheItem{
|
||||||
Action: service.OauthActionTypeBind,
|
Action: service.OauthActionTypeBind,
|
||||||
Op: f.Op,
|
Op: f.Op,
|
||||||
UserId: u.Id,
|
UserId: u.Id,
|
||||||
|
Verifier: verifier,
|
||||||
|
Nonce: nonce,
|
||||||
}, 5*60)
|
}, 5*60)
|
||||||
|
|
||||||
response.Success(c, gin.H{
|
response.Success(c, gin.H{
|
||||||
"code": code,
|
"code": state,
|
||||||
"url": url,
|
"url": url,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -108,6 +108,12 @@ func (ct *Peer) List(c *gin.Context) {
|
|||||||
if query.Uuids != "" {
|
if query.Uuids != "" {
|
||||||
tx.Where("uuid in (?)", query.Uuids)
|
tx.Where("uuid in (?)", query.Uuids)
|
||||||
}
|
}
|
||||||
|
if query.Username != "" {
|
||||||
|
tx.Where("username like ?", "%"+query.Username+"%")
|
||||||
|
}
|
||||||
|
if query.Ip != "" {
|
||||||
|
tx.Where("last_online_ip like ?", "%"+query.Ip+"%")
|
||||||
|
}
|
||||||
})
|
})
|
||||||
response.Success(c, res)
|
response.Success(c, res)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -296,32 +296,12 @@ func (ct *User) MyOauth(c *gin.Context) {
|
|||||||
|
|
||||||
// groupUsers
|
// groupUsers
|
||||||
func (ct *User) GroupUsers(c *gin.Context) {
|
func (ct *User) GroupUsers(c *gin.Context) {
|
||||||
q := &admin.GroupUsersQuery{}
|
aG := service.AllService.GroupService.List(1, 999, nil)
|
||||||
if err := c.ShouldBindJSON(q); err != nil {
|
aU := service.AllService.UserService.List(1, 9999, nil)
|
||||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
response.Success(c, gin.H{
|
||||||
return
|
"groups": aG.Groups,
|
||||||
}
|
"users": aU.Users,
|
||||||
u := service.AllService.UserService.CurUser(c)
|
|
||||||
gid := u.GroupId
|
|
||||||
uid := u.Id
|
|
||||||
if service.AllService.UserService.IsAdmin(u) && q.UserId > 0 {
|
|
||||||
nu := service.AllService.UserService.InfoById(q.UserId)
|
|
||||||
gid = nu.GroupId
|
|
||||||
uid = q.UserId
|
|
||||||
}
|
|
||||||
res := service.AllService.UserService.List(1, 999, func(tx *gorm.DB) {
|
|
||||||
tx.Where("group_id = ?", gid)
|
|
||||||
})
|
})
|
||||||
var data []*adResp.GroupUsersPayload
|
|
||||||
for _, _u := range res.Users {
|
|
||||||
gup := &adResp.GroupUsersPayload{}
|
|
||||||
gup.FromUser(_u)
|
|
||||||
if _u.Id == uid {
|
|
||||||
gup.Status = 0
|
|
||||||
}
|
|
||||||
data = append(data, gup)
|
|
||||||
}
|
|
||||||
response.Success(c, data)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register
|
// Register
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ func (g *Group) Users(c *gin.Context) {
|
|||||||
userList = service.AllService.UserService.ListByGroupId(u.GroupId, q.Page, q.PageSize)
|
userList = service.AllService.UserService.ListByGroupId(u.GroupId, q.Page, q.PageSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
var data []*apiResp.UserPayload
|
data := make([]*apiResp.UserPayload, 0, len(userList.Users))
|
||||||
for _, user := range userList.Users {
|
for _, user := range userList.Users {
|
||||||
up := &apiResp.UserPayload{}
|
up := &apiResp.UserPayload{}
|
||||||
up.FromUser(user)
|
up.FromUser(user)
|
||||||
@@ -88,21 +88,30 @@ func (g *Group) Peers(c *gin.Context) {
|
|||||||
users = service.AllService.UserService.ListIdAndNameByGroupId(u.GroupId)
|
users = service.AllService.UserService.ListIdAndNameByGroupId(u.GroupId)
|
||||||
}
|
}
|
||||||
|
|
||||||
namesById := make(map[uint]string)
|
namesById := make(map[uint]string, len(users))
|
||||||
userIds := make([]uint, 0)
|
userIds := make([]uint, 0, len(users))
|
||||||
for _, user := range users {
|
for _, user := range users {
|
||||||
namesById[user.Id] = user.Username
|
namesById[user.Id] = user.Username
|
||||||
userIds = append(userIds, user.Id)
|
userIds = append(userIds, user.Id)
|
||||||
}
|
}
|
||||||
|
dGroupNameById := make(map[uint]string)
|
||||||
|
allGroup := service.AllService.GroupService.DeviceGroupList(1, 999, nil)
|
||||||
|
for _, group := range allGroup.DeviceGroups {
|
||||||
|
dGroupNameById[group.Id] = group.Name
|
||||||
|
}
|
||||||
peerList := service.AllService.PeerService.ListByUserIds(userIds, q.Page, q.PageSize)
|
peerList := service.AllService.PeerService.ListByUserIds(userIds, q.Page, q.PageSize)
|
||||||
var data []*apiResp.GroupPeerPayload
|
data := make([]*apiResp.GroupPeerPayload, 0, len(peerList.Peers))
|
||||||
for _, peer := range peerList.Peers {
|
for _, peer := range peerList.Peers {
|
||||||
uname, ok := namesById[peer.UserId]
|
uname, ok := namesById[peer.UserId]
|
||||||
if !ok {
|
if !ok {
|
||||||
uname = ""
|
uname = ""
|
||||||
}
|
}
|
||||||
|
dGroupName, ok2 := dGroupNameById[peer.GroupId]
|
||||||
|
if !ok2 {
|
||||||
|
dGroupName = ""
|
||||||
|
}
|
||||||
pp := &apiResp.GroupPeerPayload{}
|
pp := &apiResp.GroupPeerPayload{}
|
||||||
pp.FromPeer(peer, uname)
|
pp.FromPeer(peer, uname, dGroupName)
|
||||||
data = append(data, pp)
|
data = append(data, pp)
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -111,3 +120,31 @@ func (g *Group) Peers(c *gin.Context) {
|
|||||||
Data: data,
|
Data: data,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Device
|
||||||
|
// @Tags 群组
|
||||||
|
// @Summary 设备
|
||||||
|
// @Description 机器
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param page query int false "页码"
|
||||||
|
// @Param pageSize query int false "每页数量"
|
||||||
|
// @Param status query int false "状态"
|
||||||
|
// @Param accessible query string false "accessible"
|
||||||
|
// @Success 200 {object} response.DataResponse
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /device-group/accessible [get]
|
||||||
|
// @Security BearerAuth
|
||||||
|
func (g *Group) Device(c *gin.Context) {
|
||||||
|
u := service.AllService.UserService.CurUser(c)
|
||||||
|
if !service.AllService.UserService.IsAdmin(u) {
|
||||||
|
response.Error(c, "Permission denied")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
allGroup := service.AllService.GroupService.DeviceGroupList(1, 999, nil)
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, response.DataResponse{
|
||||||
|
Total: 0,
|
||||||
|
Data: allGroup.DeviceGroups,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import (
|
|||||||
"github.com/lejianwen/rustdesk-api/v2/model"
|
"github.com/lejianwen/rustdesk-api/v2/model"
|
||||||
"github.com/lejianwen/rustdesk-api/v2/service"
|
"github.com/lejianwen/rustdesk-api/v2/service"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -56,7 +55,7 @@ func (i *Index) Heartbeat(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
//如果在40s以内则不更新
|
//如果在40s以内则不更新
|
||||||
if time.Now().Unix()-peer.LastOnlineTime > 40 {
|
if time.Now().Unix()-peer.LastOnlineTime >= 30 {
|
||||||
upp := &model.Peer{RowId: peer.RowId, LastOnlineTime: time.Now().Unix(), LastOnlineIp: c.ClientIP()}
|
upp := &model.Peer{RowId: peer.RowId, LastOnlineTime: time.Now().Unix(), LastOnlineIp: c.ClientIP()}
|
||||||
service.AllService.PeerService.Update(upp)
|
service.AllService.PeerService.Update(upp)
|
||||||
}
|
}
|
||||||
@@ -74,13 +73,9 @@ func (i *Index) Heartbeat(c *gin.Context) {
|
|||||||
// @Router /version [get]
|
// @Router /version [get]
|
||||||
func (i *Index) Version(c *gin.Context) {
|
func (i *Index) Version(c *gin.Context) {
|
||||||
//读取resources/version文件
|
//读取resources/version文件
|
||||||
v, err := os.ReadFile("resources/version")
|
v := service.AllService.AppService.GetAppVersion()
|
||||||
if err != nil {
|
|
||||||
response.Fail(c, 101, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
response.Success(
|
response.Success(
|
||||||
c,
|
c,
|
||||||
string(v),
|
v,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import (
|
|||||||
apiResp "github.com/lejianwen/rustdesk-api/v2/http/response/api"
|
apiResp "github.com/lejianwen/rustdesk-api/v2/http/response/api"
|
||||||
"github.com/lejianwen/rustdesk-api/v2/model"
|
"github.com/lejianwen/rustdesk-api/v2/model"
|
||||||
"github.com/lejianwen/rustdesk-api/v2/service"
|
"github.com/lejianwen/rustdesk-api/v2/service"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/utils"
|
||||||
|
"github.com/nicksnyder/go-i18n/v2/i18n"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -32,15 +34,14 @@ func (o *Oauth) OidcAuth(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
oauthService := service.AllService.OauthService
|
oauthService := service.AllService.OauthService
|
||||||
var code string
|
|
||||||
var url string
|
err, state, verifier, nonce, url := oauthService.BeginAuth(f.Op)
|
||||||
err, code, url = oauthService.BeginAuth(f.Op)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.Error(c, response.TranslateMsg(c, err.Error()))
|
response.Error(c, response.TranslateMsg(c, err.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
service.AllService.OauthService.SetOauthCache(code, &service.OauthCacheItem{
|
service.AllService.OauthService.SetOauthCache(state, &service.OauthCacheItem{
|
||||||
Action: service.OauthActionTypeLogin,
|
Action: service.OauthActionTypeLogin,
|
||||||
Id: f.Id,
|
Id: f.Id,
|
||||||
Op: f.Op,
|
Op: f.Op,
|
||||||
@@ -48,10 +49,12 @@ func (o *Oauth) OidcAuth(c *gin.Context) {
|
|||||||
DeviceName: f.DeviceInfo.Name,
|
DeviceName: f.DeviceInfo.Name,
|
||||||
DeviceOs: f.DeviceInfo.Os,
|
DeviceOs: f.DeviceInfo.Os,
|
||||||
DeviceType: f.DeviceInfo.Type,
|
DeviceType: f.DeviceInfo.Type,
|
||||||
|
Verifier: verifier,
|
||||||
|
Nonce: nonce,
|
||||||
}, 5*60)
|
}, 5*60)
|
||||||
//fmt.Println("code url", code, url)
|
//fmt.Println("code url", code, url)
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
"code": code,
|
"code": state,
|
||||||
"url": url,
|
"url": url,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -143,7 +146,10 @@ func (o *Oauth) OidcAuthQuery(c *gin.Context) {
|
|||||||
func (o *Oauth) OauthCallback(c *gin.Context) {
|
func (o *Oauth) OauthCallback(c *gin.Context) {
|
||||||
state := c.Query("state")
|
state := c.Query("state")
|
||||||
if state == "" {
|
if state == "" {
|
||||||
c.String(http.StatusInternalServerError, response.TranslateParamMsg(c, "ParamIsEmpty", "state"))
|
c.HTML(http.StatusOK, "oauth_fail.html", gin.H{
|
||||||
|
"message": "ParamIsEmpty",
|
||||||
|
"sub_message": "state",
|
||||||
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
cacheKey := state
|
cacheKey := state
|
||||||
@@ -151,17 +157,24 @@ func (o *Oauth) OauthCallback(c *gin.Context) {
|
|||||||
//从缓存中获取
|
//从缓存中获取
|
||||||
oauthCache := oauthService.GetOauthCache(cacheKey)
|
oauthCache := oauthService.GetOauthCache(cacheKey)
|
||||||
if oauthCache == nil {
|
if oauthCache == nil {
|
||||||
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "OauthExpired"))
|
c.HTML(http.StatusOK, "oauth_fail.html", gin.H{
|
||||||
|
"message": "OauthExpired",
|
||||||
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
nonce := oauthCache.Nonce
|
||||||
op := oauthCache.Op
|
op := oauthCache.Op
|
||||||
action := oauthCache.Action
|
action := oauthCache.Action
|
||||||
|
verifier := oauthCache.Verifier
|
||||||
var user *model.User
|
var user *model.User
|
||||||
// 获取用户信息
|
// 获取用户信息
|
||||||
code := c.Query("code")
|
code := c.Query("code")
|
||||||
err, oauthUser := oauthService.Callback(code, op)
|
err, oauthUser := oauthService.Callback(code, verifier, op, nonce)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "OauthFailed")+response.TranslateMsg(c, err.Error()))
|
c.HTML(http.StatusOK, "oauth_fail.html", gin.H{
|
||||||
|
"message": "OauthFailed",
|
||||||
|
"sub_message": err.Error(),
|
||||||
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
userId := oauthCache.UserId
|
userId := oauthCache.UserId
|
||||||
@@ -172,28 +185,38 @@ func (o *Oauth) OauthCallback(c *gin.Context) {
|
|||||||
// 检查此openid是否已经绑定过
|
// 检查此openid是否已经绑定过
|
||||||
utr := oauthService.UserThirdInfo(op, openid)
|
utr := oauthService.UserThirdInfo(op, openid)
|
||||||
if utr.UserId > 0 {
|
if utr.UserId > 0 {
|
||||||
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "OauthHasBindOtherUser"))
|
c.HTML(http.StatusOK, "oauth_fail.html", gin.H{
|
||||||
|
"message": "OauthHasBindOtherUser",
|
||||||
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
//绑定
|
//绑定
|
||||||
user = service.AllService.UserService.InfoById(userId)
|
user = service.AllService.UserService.InfoById(userId)
|
||||||
if user == nil {
|
if user == nil {
|
||||||
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "ItemNotFound"))
|
c.HTML(http.StatusOK, "oauth_fail.html", gin.H{
|
||||||
|
"message": "ItemNotFound",
|
||||||
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
//绑定
|
//绑定
|
||||||
err := oauthService.BindOauthUser(userId, oauthUser, op)
|
err := oauthService.BindOauthUser(userId, oauthUser, op)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "BindFail"))
|
c.HTML(http.StatusOK, "oauth_fail.html", gin.H{
|
||||||
|
"message": "BindFail",
|
||||||
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.String(http.StatusOK, response.TranslateMsg(c, "BindSuccess"))
|
c.HTML(http.StatusOK, "oauth_success.html", gin.H{
|
||||||
|
"message": "BindSuccess",
|
||||||
|
})
|
||||||
return
|
return
|
||||||
|
|
||||||
} else if action == service.OauthActionTypeLogin {
|
} else if action == service.OauthActionTypeLogin {
|
||||||
//登录
|
//登录
|
||||||
if userId != 0 {
|
if userId != 0 {
|
||||||
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "OauthHasBeenSuccess"))
|
c.HTML(http.StatusOK, "oauth_fail.html", gin.H{
|
||||||
|
"message": "OauthHasBeenSuccess",
|
||||||
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
user = service.AllService.UserService.InfoByOauthId(op, openid)
|
user = service.AllService.UserService.InfoByOauthId(op, openid)
|
||||||
@@ -210,7 +233,9 @@ func (o *Oauth) OauthCallback(c *gin.Context) {
|
|||||||
//自动注册
|
//自动注册
|
||||||
err, user = service.AllService.UserService.RegisterByOauth(oauthUser, op)
|
err, user = service.AllService.UserService.RegisterByOauth(oauthUser, op)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.String(http.StatusInternalServerError, response.TranslateMsg(c, err.Error()))
|
c.HTML(http.StatusOK, "oauth_fail.html", gin.H{
|
||||||
|
"message": err.Error(),
|
||||||
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -230,11 +255,51 @@ func (o *Oauth) OauthCallback(c *gin.Context) {
|
|||||||
c.Redirect(http.StatusFound, url)
|
c.Redirect(http.StatusFound, url)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.String(http.StatusOK, response.TranslateMsg(c, "OauthSuccess"))
|
c.HTML(http.StatusOK, "oauth_success.html", gin.H{
|
||||||
|
"message": "OauthSuccess",
|
||||||
|
})
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "ParamsError"))
|
c.HTML(http.StatusOK, "oauth_fail.html", gin.H{
|
||||||
|
"message": "ParamsError",
|
||||||
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MessageParams struct {
|
||||||
|
Lang string `json:"lang" form:"lang"`
|
||||||
|
Title string `json:"title" form:"title"`
|
||||||
|
Msg string `json:"msg" form:"msg"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Oauth) Message(c *gin.Context) {
|
||||||
|
mp := &MessageParams{}
|
||||||
|
if err := c.ShouldBindQuery(mp); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
localizer := global.Localizer(mp.Lang)
|
||||||
|
res := ""
|
||||||
|
if mp.Title != "" {
|
||||||
|
title, err := localizer.LocalizeMessage(&i18n.Message{
|
||||||
|
ID: mp.Title,
|
||||||
|
})
|
||||||
|
if err == nil {
|
||||||
|
res = utils.StringConcat(";title='", title, "';")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if mp.Msg != "" {
|
||||||
|
msg, err := localizer.LocalizeMessage(&i18n.Message{
|
||||||
|
ID: mp.Msg,
|
||||||
|
})
|
||||||
|
if err == nil {
|
||||||
|
res = utils.StringConcat(res, "msg = '", msg, "';")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//返回js内容
|
||||||
|
c.Header("Content-Type", "application/javascript")
|
||||||
|
c.String(http.StatusOK, res)
|
||||||
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ func (p *Peer) SysInfo(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
fpe := f.ToPeer()
|
fpe := f.ToPeer()
|
||||||
pe := service.AllService.PeerService.FindById(f.Id)
|
pe := service.AllService.PeerService.FindByUuid(f.Uuid)
|
||||||
if pe.RowId == 0 {
|
if pe.RowId == 0 {
|
||||||
pe = f.ToPeer()
|
pe = f.ToPeer()
|
||||||
pe.UserId = service.AllService.UserService.FindLatestUserIdFromLoginLogByUuid(pe.Uuid)
|
pe.UserId = service.AllService.UserService.FindLatestUserIdFromLoginLogByUuid(pe.Uuid)
|
||||||
@@ -56,3 +56,9 @@ func (p *Peer) SysInfo(c *gin.Context) {
|
|||||||
//直接响应文本
|
//直接响应文本
|
||||||
c.String(http.StatusOK, "SYSINFO_UPDATED")
|
c.String(http.StatusOK, "SYSINFO_UPDATED")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Peer) SysInfoVer(c *gin.Context) {
|
||||||
|
//读取resources/version文件
|
||||||
|
v := service.AllService.AppService.GetAppVersion()
|
||||||
|
c.String(http.StatusOK, v)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
package web
|
package web
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/lejianwen/rustdesk-api/v2/global"
|
"github.com/lejianwen/rustdesk-api/v2/global"
|
||||||
"strconv"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Index struct {
|
type Index struct {
|
||||||
@@ -15,13 +15,21 @@ func (i *Index) Index(c *gin.Context) {
|
|||||||
|
|
||||||
func (i *Index) ConfigJs(c *gin.Context) {
|
func (i *Index) ConfigJs(c *gin.Context) {
|
||||||
apiServer := global.Config.Rustdesk.ApiServer
|
apiServer := global.Config.Rustdesk.ApiServer
|
||||||
magicQueryonline := strconv.Itoa(global.Config.Rustdesk.WebclientMagicQueryonline)
|
magicQueryonline := global.Config.Rustdesk.WebclientMagicQueryonline
|
||||||
tmp := `
|
tmp := fmt.Sprintf(`localStorage.setItem('api-server', '%v');
|
||||||
localStorage.setItem('api-server', "` + apiServer + `")
|
const ws2_prefix = 'wc-';
|
||||||
const ws2_prefix = 'wc-'
|
localStorage.setItem(ws2_prefix+'api-server', '%v');
|
||||||
localStorage.setItem(ws2_prefix+'api-server', "` + apiServer + `")
|
|
||||||
|
|
||||||
window.webclient_magic_queryonline = ` + magicQueryonline + ``
|
window.webclient_magic_queryonline = %d;
|
||||||
|
window.ws_host = '%v';
|
||||||
|
`, apiServer, apiServer, magicQueryonline, global.Config.Rustdesk.WsHost)
|
||||||
|
// tmp := `
|
||||||
|
//localStorage.setItem('api-server', "` + apiServer + `")
|
||||||
|
//const ws2_prefix = 'wc-'
|
||||||
|
//localStorage.setItem(ws2_prefix+'api-server', "` + apiServer + `")
|
||||||
|
//
|
||||||
|
//window.webclient_magic_queryonline = ` + magicQueryonline + ``
|
||||||
|
|
||||||
|
c.Header("Content-Type", "application/javascript")
|
||||||
c.String(200, tmp)
|
c.String(200, tmp)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,3 +22,15 @@ func (gf *GroupForm) ToGroup() *model.Group {
|
|||||||
group.Type = gf.Type
|
group.Type = gf.Type
|
||||||
return group
|
return group
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type DeviceGroupForm struct {
|
||||||
|
Id uint `json:"id"`
|
||||||
|
Name string `json:"name" validate:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gf *DeviceGroupForm) ToDeviceGroup() *model.DeviceGroup {
|
||||||
|
group := &model.DeviceGroup{}
|
||||||
|
group.Id = gf.Id
|
||||||
|
group.Name = gf.Name
|
||||||
|
return group
|
||||||
|
}
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ type OauthForm struct {
|
|||||||
ClientSecret string `json:"client_secret" validate:"required"`
|
ClientSecret string `json:"client_secret" validate:"required"`
|
||||||
RedirectUrl string `json:"redirect_url" validate:"required"`
|
RedirectUrl string `json:"redirect_url" validate:"required"`
|
||||||
AutoRegister *bool `json:"auto_register"`
|
AutoRegister *bool `json:"auto_register"`
|
||||||
|
PkceEnable *bool `json:"pkce_enable"`
|
||||||
|
PkceMethod string `json:"pkce_method"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (of *OauthForm) ToOauth() *model.Oauth {
|
func (of *OauthForm) ToOauth() *model.Oauth {
|
||||||
@@ -36,6 +38,8 @@ func (of *OauthForm) ToOauth() *model.Oauth {
|
|||||||
AutoRegister: of.AutoRegister,
|
AutoRegister: of.AutoRegister,
|
||||||
Issuer: of.Issuer,
|
Issuer: of.Issuer,
|
||||||
Scopes: of.Scopes,
|
Scopes: of.Scopes,
|
||||||
|
PkceEnable: of.PkceEnable,
|
||||||
|
PkceMethod: of.PkceMethod,
|
||||||
}
|
}
|
||||||
oa.Id = of.Id
|
oa.Id = of.Id
|
||||||
return oa
|
return oa
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ type PeerForm struct {
|
|||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
Uuid string `json:"uuid"`
|
Uuid string `json:"uuid"`
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
|
GroupId uint `json:"group_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type PeerBatchDeleteForm struct {
|
type PeerBatchDeleteForm struct {
|
||||||
@@ -30,6 +31,7 @@ func (f *PeerForm) ToPeer() *model.Peer {
|
|||||||
Username: f.Username,
|
Username: f.Username,
|
||||||
Uuid: f.Uuid,
|
Uuid: f.Uuid,
|
||||||
Version: f.Version,
|
Version: f.Version,
|
||||||
|
GroupId: f.GroupId,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,6 +41,8 @@ type PeerQuery struct {
|
|||||||
Id string `json:"id" form:"id"`
|
Id string `json:"id" form:"id"`
|
||||||
Hostname string `json:"hostname" form:"hostname"`
|
Hostname string `json:"hostname" form:"hostname"`
|
||||||
Uuids string `json:"uuids" form:"uuids"`
|
Uuids string `json:"uuids" form:"uuids"`
|
||||||
|
Ip string `json:"ip" form:"ip"`
|
||||||
|
Username string `json:"username" form:"username"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type SimpleDataQuery struct {
|
type SimpleDataQuery struct {
|
||||||
|
|||||||
@@ -40,14 +40,14 @@ type LoginForm struct {
|
|||||||
|
|
||||||
type UserListQuery struct {
|
type UserListQuery struct {
|
||||||
Page uint `json:"page" form:"page" validate:"required" label:"页码"`
|
Page uint `json:"page" form:"page" validate:"required" label:"页码"`
|
||||||
PageSize uint `json:"page_size" form:"page_size" validate:"required" label:"每页数量"`
|
PageSize uint `json:"pageSize" form:"pageSize" validate:"required" label:"每页数量"`
|
||||||
Status int `json:"status" form:"status" label:"状态"`
|
Status int `json:"status" form:"status" label:"状态"`
|
||||||
Accessible string `json:"accessible" form:"accessible"`
|
Accessible string `json:"accessible" form:"accessible"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type PeerListQuery struct {
|
type PeerListQuery struct {
|
||||||
Page uint `json:"page" form:"page" validate:"required" label:"页码"`
|
Page uint `json:"page" form:"page" validate:"required" label:"页码"`
|
||||||
PageSize uint `json:"page_size" form:"page_size" validate:"required" label:"每页数量"`
|
PageSize uint `json:"pageSize" form:"pageSize" validate:"required" label:"每页数量"`
|
||||||
Status int `json:"status" form:"status" label:"状态"`
|
Status int `json:"status" form:"status" label:"状态"`
|
||||||
Accessible string `json:"accessible" form:"accessible"`
|
Accessible string `json:"accessible" form:"accessible"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,15 +22,3 @@ type UserOauthItem struct {
|
|||||||
Op string `json:"op"`
|
Op string `json:"op"`
|
||||||
Status int `json:"status"`
|
Status int `json:"status"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type GroupUsersPayload struct {
|
|
||||||
Id uint `json:"id"`
|
|
||||||
Username string `json:"username"`
|
|
||||||
Status int `json:"status"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *GroupUsersPayload) FromUser(user *model.User) {
|
|
||||||
g.Id = user.Id
|
|
||||||
g.Username = user.Username
|
|
||||||
g.Status = 1
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -32,12 +32,13 @@ https://github.com/rustdesk/rustdesk/blob/master/flutter/lib/common/hbbs/hbbs.da
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
type GroupPeerPayload struct {
|
type GroupPeerPayload struct {
|
||||||
Id string `json:"id"`
|
Id string `json:"id"`
|
||||||
Info *PeerPayloadInfo `json:"info"`
|
Info *PeerPayloadInfo `json:"info"`
|
||||||
Status int `json:"status"`
|
Status int `json:"status"`
|
||||||
User string `json:"user"`
|
User string `json:"user"`
|
||||||
UserName string `json:"user_name"`
|
UserName string `json:"user_name"`
|
||||||
Note string `json:"note"`
|
Note string `json:"note"`
|
||||||
|
DeviceGroupName string `json:"device_group_name"`
|
||||||
}
|
}
|
||||||
type PeerPayloadInfo struct {
|
type PeerPayloadInfo struct {
|
||||||
DeviceName string `json:"device_name"`
|
DeviceName string `json:"device_name"`
|
||||||
@@ -59,7 +60,7 @@ func (gpp *GroupPeerPayload) FromAddressBook(a *model.AddressBook, username stri
|
|||||||
gpp.UserName = username
|
gpp.UserName = username
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gpp *GroupPeerPayload) FromPeer(p *model.Peer, username string) {
|
func (gpp *GroupPeerPayload) FromPeer(p *model.Peer, username string, dGroupName string) {
|
||||||
gpp.Id = p.Id
|
gpp.Id = p.Id
|
||||||
gpp.Info = &PeerPayloadInfo{
|
gpp.Info = &PeerPayloadInfo{
|
||||||
DeviceName: p.Hostname,
|
DeviceName: p.Hostname,
|
||||||
@@ -68,4 +69,5 @@ func (gpp *GroupPeerPayload) FromPeer(p *model.Peer, username string) {
|
|||||||
}
|
}
|
||||||
gpp.Note = ""
|
gpp.Note = ""
|
||||||
gpp.UserName = username
|
gpp.UserName = username
|
||||||
|
gpp.DeviceGroupName = dGroupName
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ func Init(g *gin.Engine) {
|
|||||||
MyBind(adg)
|
MyBind(adg)
|
||||||
|
|
||||||
RustdeskCmdBind(adg)
|
RustdeskCmdBind(adg)
|
||||||
|
DeviceGroupBind(adg)
|
||||||
//访问静态文件
|
//访问静态文件
|
||||||
//g.StaticFS("/upload", http.Dir(global.Config.Gin.ResourcesPath+"/upload"))
|
//g.StaticFS("/upload", http.Dir(global.Config.Gin.ResourcesPath+"/upload"))
|
||||||
}
|
}
|
||||||
@@ -106,6 +106,18 @@ func GroupBind(rg *gin.RouterGroup) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func DeviceGroupBind(rg *gin.RouterGroup) {
|
||||||
|
aR := rg.Group("/device_group").Use(middleware.AdminPrivilege())
|
||||||
|
{
|
||||||
|
cont := &admin.DeviceGroup{}
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TagBind(rg *gin.RouterGroup) {
|
func TagBind(rg *gin.RouterGroup) {
|
||||||
aR := rg.Group("/tag").Use(middleware.AdminPrivilege())
|
aR := rg.Group("/tag").Use(middleware.AdminPrivilege())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ func ApiInit(g *gin.Engine) {
|
|||||||
if global.Config.App.ShowSwagger == 1 {
|
if global.Config.App.ShowSwagger == 1 {
|
||||||
g.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler, ginSwagger.InstanceName("api")))
|
g.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler, ginSwagger.InstanceName("api")))
|
||||||
}
|
}
|
||||||
|
// 加载 HTML 模板
|
||||||
|
g.LoadHTMLGlob("resources/templates/*")
|
||||||
|
|
||||||
frg := g.Group("/api")
|
frg := g.Group("/api")
|
||||||
|
|
||||||
@@ -46,11 +48,13 @@ func ApiInit(g *gin.Engine) {
|
|||||||
//api/oauth/callback
|
//api/oauth/callback
|
||||||
frg.GET("/oauth/callback", o.OauthCallback)
|
frg.GET("/oauth/callback", o.OauthCallback)
|
||||||
frg.GET("/oauth/login", o.OauthCallback)
|
frg.GET("/oauth/login", o.OauthCallback)
|
||||||
|
frg.GET("/oauth/msg", o.Message)
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
pe := &api.Peer{}
|
pe := &api.Peer{}
|
||||||
//提交系统信息
|
//提交系统信息
|
||||||
frg.POST("/sysinfo", pe.SysInfo)
|
frg.POST("/sysinfo", pe.SysInfo)
|
||||||
|
frg.POST("/sysinfo_ver", pe.SysInfoVer)
|
||||||
}
|
}
|
||||||
|
|
||||||
if global.Config.App.WebClient == 1 {
|
if global.Config.App.WebClient == 1 {
|
||||||
@@ -79,6 +83,8 @@ func ApiInit(g *gin.Engine) {
|
|||||||
gr := &api.Group{}
|
gr := &api.Group{}
|
||||||
frg.GET("/users", gr.Users)
|
frg.GET("/users", gr.Users)
|
||||||
frg.GET("/peers", gr.Peers)
|
frg.GET("/peers", gr.Peers)
|
||||||
|
// /api/device-group/accessible?current=1&pageSize=100
|
||||||
|
frg.GET("/device-group/accessible", gr.Device)
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -88,6 +94,7 @@ func ApiInit(g *gin.Engine) {
|
|||||||
//更新地址
|
//更新地址
|
||||||
frg.POST("/ab", ab.UpAb)
|
frg.POST("/ab", ab.UpAb)
|
||||||
}
|
}
|
||||||
|
|
||||||
PersonalRoutes(frg)
|
PersonalRoutes(frg)
|
||||||
//访问静态文件
|
//访问静态文件
|
||||||
g.StaticFS("/upload", http.Dir(global.Config.Gin.ResourcesPath+"/public/upload"))
|
g.StaticFS("/upload", http.Dir(global.Config.Gin.ResourcesPath+"/public/upload"))
|
||||||
|
|||||||
@@ -16,3 +16,14 @@ type GroupList struct {
|
|||||||
Groups []*Group `json:"list"`
|
Groups []*Group `json:"list"`
|
||||||
Pagination
|
Pagination
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type DeviceGroup struct {
|
||||||
|
IdModel
|
||||||
|
Name string `json:"name" gorm:"default:'';not null;"`
|
||||||
|
TimeModel
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeviceGroupList struct {
|
||||||
|
DeviceGroups []*DeviceGroup `json:"list"`
|
||||||
|
Pagination
|
||||||
|
}
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ const (
|
|||||||
OauthTypeGoogle string = "google"
|
OauthTypeGoogle string = "google"
|
||||||
OauthTypeOidc string = "oidc"
|
OauthTypeOidc string = "oidc"
|
||||||
OauthTypeWebauth string = "webauth"
|
OauthTypeWebauth string = "webauth"
|
||||||
|
PKCEMethodS256 string = "S256"
|
||||||
|
PKCEMethodPlain string = "plain"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Validate the oauth type
|
// Validate the oauth type
|
||||||
@@ -41,6 +43,8 @@ type Oauth struct {
|
|||||||
AutoRegister *bool `json:"auto_register"`
|
AutoRegister *bool `json:"auto_register"`
|
||||||
Scopes string `json:"scopes"`
|
Scopes string `json:"scopes"`
|
||||||
Issuer string `json:"issuer"`
|
Issuer string `json:"issuer"`
|
||||||
|
PkceEnable *bool `json:"pkce_enable"`
|
||||||
|
PkceMethod string `json:"pkce_method"`
|
||||||
TimeModel
|
TimeModel
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,6 +72,13 @@ func (oa *Oauth) FormatOauthInfo() error {
|
|||||||
if oauthType == OauthTypeGoogle && issuer == "" {
|
if oauthType == OauthTypeGoogle && issuer == "" {
|
||||||
oa.Issuer = IssuerGoogle
|
oa.Issuer = IssuerGoogle
|
||||||
}
|
}
|
||||||
|
if oa.PkceEnable == nil {
|
||||||
|
oa.PkceEnable = new(bool)
|
||||||
|
*oa.PkceEnable = false
|
||||||
|
}
|
||||||
|
if oa.PkceMethod == "" {
|
||||||
|
oa.PkceMethod = PKCEMethodS256
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ type Peer struct {
|
|||||||
User *User `json:"user,omitempty"`
|
User *User `json:"user,omitempty"`
|
||||||
LastOnlineTime int64 `json:"last_online_time" gorm:"default:0;not null;"`
|
LastOnlineTime int64 `json:"last_online_time" gorm:"default:0;not null;"`
|
||||||
LastOnlineIp string `json:"last_online_ip" gorm:"default:'';not null;"`
|
LastOnlineIp string `json:"last_online_ip" gorm:"default:'';not null;"`
|
||||||
|
GroupId uint `json:"group_id" gorm:"default:0;not null;index"`
|
||||||
TimeModel
|
TimeModel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -137,4 +137,9 @@ other = "Captcha error."
|
|||||||
[PwdLoginDisabled]
|
[PwdLoginDisabled]
|
||||||
description = "Password login disabled."
|
description = "Password login disabled."
|
||||||
one = "Password login disabled."
|
one = "Password login disabled."
|
||||||
other = "Password login disabled."
|
other = "Password login disabled."
|
||||||
|
|
||||||
|
[CannotShareToSelf]
|
||||||
|
description = "Cannot share to self."
|
||||||
|
one = "Cannot share to self."
|
||||||
|
other = "Cannot share to self."
|
||||||
@@ -146,4 +146,9 @@ other = "Error de captcha."
|
|||||||
[PwdLoginDisabled]
|
[PwdLoginDisabled]
|
||||||
description = "Password login disabled."
|
description = "Password login disabled."
|
||||||
one = "Inicio de sesión con contraseña deshabilitado."
|
one = "Inicio de sesión con contraseña deshabilitado."
|
||||||
other = "Inicio de sesión con contraseña deshabilitado."
|
other = "Inicio de sesión con contraseña deshabilitado."
|
||||||
|
|
||||||
|
[CannotShareToSelf]
|
||||||
|
description = "Cannot share to self."
|
||||||
|
one = "No se puede compartir con uno mismo."
|
||||||
|
other = "No se puede compartir con uno mismo."
|
||||||
@@ -146,4 +146,9 @@ other = "Erreur de captcha."
|
|||||||
[PwdLoginDisabled]
|
[PwdLoginDisabled]
|
||||||
description = "Password login disabled."
|
description = "Password login disabled."
|
||||||
one = "Connexion par mot de passe désactivée."
|
one = "Connexion par mot de passe désactivée."
|
||||||
other = "Connexion par mot de passe désactivée."
|
other = "Connexion par mot de passe désactivée."
|
||||||
|
|
||||||
|
[CannotShareToSelf]
|
||||||
|
description = "Cannot share to self."
|
||||||
|
one = "Impossible de partager avec soi-même."
|
||||||
|
other = "Impossible de partager avec soi-même."
|
||||||
@@ -141,3 +141,8 @@ other = "Captcha 오류."
|
|||||||
description = "Password login disabled."
|
description = "Password login disabled."
|
||||||
one = "비밀번호 로그인이 비활성화되었습니다."
|
one = "비밀번호 로그인이 비활성화되었습니다."
|
||||||
other = "비밀번호 로그인이 비활성화되었습니다."
|
other = "비밀번호 로그인이 비활성화되었습니다."
|
||||||
|
|
||||||
|
[CannotShareToSelf]
|
||||||
|
description = "Cannot share to self."
|
||||||
|
one = "자기 자신에게 공유할 수 없습니다."
|
||||||
|
other = "자기 자신에게 공유할 수 없습니다."
|
||||||
@@ -146,4 +146,9 @@ other = "Ошибка капчи."
|
|||||||
[PwdLoginDisabled]
|
[PwdLoginDisabled]
|
||||||
description = "Password login disabled."
|
description = "Password login disabled."
|
||||||
one = "Вход по паролю отключен."
|
one = "Вход по паролю отключен."
|
||||||
other = "Вход по паролю отключен."
|
other = "Вход по паролю отключен."
|
||||||
|
|
||||||
|
[CannotShareToSelf]
|
||||||
|
description = "Cannot share to self."
|
||||||
|
one = "Нельзя поделиться с собой."
|
||||||
|
other = "Нельзя поделиться с собой."
|
||||||
@@ -139,4 +139,9 @@ other = "验证码错误。"
|
|||||||
[PwdLoginDisabled]
|
[PwdLoginDisabled]
|
||||||
description = "Password login disabled."
|
description = "Password login disabled."
|
||||||
one = "密码登录已禁用。"
|
one = "密码登录已禁用。"
|
||||||
other = "密码登录已禁用。"
|
other = "密码登录已禁用。"
|
||||||
|
|
||||||
|
[CannotShareToSelf]
|
||||||
|
description = "Cannot share to self."
|
||||||
|
one = "不能共享给自己。"
|
||||||
|
other = "不能共享给自己。"
|
||||||
@@ -140,3 +140,8 @@ other = "驗證碼錯誤。"
|
|||||||
description = "Password login disabled."
|
description = "Password login disabled."
|
||||||
one = "密碼登錄已禁用。"
|
one = "密碼登錄已禁用。"
|
||||||
other = "密碼登錄已禁用。"
|
other = "密碼登錄已禁用。"
|
||||||
|
|
||||||
|
[CannotShareToSelf]
|
||||||
|
description = "Cannot share to self."
|
||||||
|
one = "無法共享給自己。"
|
||||||
|
other = "無法共享給自己。"
|
||||||
0
resources/public/upload/.gitkeep
Normal file
0
resources/public/upload/.gitkeep
Normal file
81
resources/templates/oauth_fail.html
Normal file
81
resources/templates/oauth_fail.html
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>OauthFailed - RustDesk API</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
margin: 0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.success-container {
|
||||||
|
text-align: center;
|
||||||
|
background: white;
|
||||||
|
padding: 2rem;
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||||
|
max-width: 400px;
|
||||||
|
width: 90%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkmark {
|
||||||
|
color: #ba363a;
|
||||||
|
font-size: 4rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
color: #666;
|
||||||
|
line-height: 1.6;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.return-link {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 10px 20px;
|
||||||
|
background-color: #ba363a;
|
||||||
|
color: white;
|
||||||
|
text-decoration: none;
|
||||||
|
border-radius: 5px;
|
||||||
|
transition: background-color 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.return-link:hover {
|
||||||
|
background-color: #ba363a;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<link rel="stylesheet" href="https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/font-awesome/6.0.0/css/all.min.css">
|
||||||
|
<script>
|
||||||
|
var lang = navigator.language || navigator.userLanguage || 'zh-CN';
|
||||||
|
var title = 'OauthFailed'
|
||||||
|
var msg = '{{.message}}'
|
||||||
|
var btn = 'Close'
|
||||||
|
document.writeln('<script src="/api/oauth/msg?lang=' + lang + '&msg=' + msg + '&title=OauthFailed"><\/script>');
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="success-container">
|
||||||
|
<i class="fas fa-triangle-exclamation checkmark"></i>
|
||||||
|
<h1 id="h1"></h1>
|
||||||
|
<p id="msg"></p>
|
||||||
|
<a href="javascript:window.close()" class="return-link" id="btn">Close</a>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
document.title = title + ' - RustDesk API';
|
||||||
|
document.getElementById('h1').innerText = title;
|
||||||
|
document.getElementById('msg').innerText = msg;
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
82
resources/templates/oauth_success.html
Normal file
82
resources/templates/oauth_success.html
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>OauthSuccess - RustDesk API</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
margin: 0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.success-container {
|
||||||
|
text-align: center;
|
||||||
|
background: white;
|
||||||
|
padding: 2rem;
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||||
|
max-width: 400px;
|
||||||
|
width: 90%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkmark {
|
||||||
|
color: #4CAF50;
|
||||||
|
font-size: 4rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
color: #666;
|
||||||
|
line-height: 1.6;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.return-link {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 10px 20px;
|
||||||
|
background-color: #4CAF50;
|
||||||
|
color: white;
|
||||||
|
text-decoration: none;
|
||||||
|
border-radius: 5px;
|
||||||
|
transition: background-color 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.return-link:hover {
|
||||||
|
background-color: #45a049;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script>
|
||||||
|
var lang = navigator.language || navigator.userLanguage || 'zh-CN';
|
||||||
|
var title = 'OauthSuccess'
|
||||||
|
var msg = '{{.message}}'
|
||||||
|
var btn = 'Close'
|
||||||
|
document.writeln('<script src="/api/oauth/msg?lang=' + lang + '&msg=' + msg + '&title=OauthSuccess"><\/script>');
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="success-container">
|
||||||
|
<i class="fas fa-check-circle checkmark"></i>
|
||||||
|
<h1 id="h1"></h1>
|
||||||
|
<!-- <p>您已成功授权访问您的账户。</p>-->
|
||||||
|
<!-- <p>现在可以关闭本页面或返回应用继续操作。</p>-->
|
||||||
|
<a href="javascript:window.close()" class="return-link">Close</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.title = title + ' - RustDesk API';
|
||||||
|
document.getElementById('h1').innerText = title;
|
||||||
|
document.getElementById('msg').innerText = msg;
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
16
resources/web2/assets/FontManifest.json
vendored
16
resources/web2/assets/FontManifest.json
vendored
@@ -38,5 +38,21 @@
|
|||||||
"asset": "assets/address_book.ttf"
|
"asset": "assets/address_book.ttf"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"family": "DeviceGroup",
|
||||||
|
"fonts": [
|
||||||
|
{
|
||||||
|
"asset": "assets/device_group.ttf"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"family": "More",
|
||||||
|
"fonts": [
|
||||||
|
{
|
||||||
|
"asset": "assets/more.ttf"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
BIN
resources/web2/assets/assets/device_group.ttf
vendored
Normal file
BIN
resources/web2/assets/assets/device_group.ttf
vendored
Normal file
Binary file not shown.
BIN
resources/web2/assets/assets/more.ttf
vendored
Normal file
BIN
resources/web2/assets/assets/more.ttf
vendored
Normal file
Binary file not shown.
Binary file not shown.
513
resources/web2/index.html
vendored
513
resources/web2/index.html
vendored
@@ -1,6 +1,6 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<!--
|
<!--
|
||||||
If you are serving your web app in a path other than the root, change the
|
If you are serving your web app in a path other than the root, change the
|
||||||
href value below to reflect the base path you are serving from.
|
href value below to reflect the base path you are serving from.
|
||||||
@@ -16,195 +16,196 @@
|
|||||||
-->
|
-->
|
||||||
<base href="/webclient2/" />
|
<base href="/webclient2/" />
|
||||||
|
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8"/>
|
||||||
<meta content="IE=Edge" http-equiv="X-UA-Compatible" />
|
<meta content="IE=Edge" http-equiv="X-UA-Compatible"/>
|
||||||
<meta name="description" content="Remote Desktop." />
|
<meta name="description" content="Remote Desktop."/>
|
||||||
|
|
||||||
<!-- iOS meta tags & icons -->
|
<!-- iOS meta tags & icons -->
|
||||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
<meta name="apple-mobile-web-app-capable" content="yes"/>
|
||||||
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
|
<meta name="apple-mobile-web-app-status-bar-style" content="black"/>
|
||||||
<meta name="apple-mobile-web-app-title" content="RustDesk" />
|
<meta name="apple-mobile-web-app-title" content="RustDesk"/>
|
||||||
<link rel="apple-touch-icon" href="icons/Icon-192.png?v=1a7ad736" />
|
<link rel="apple-touch-icon" href="icons/Icon-192.png?v=1a7ad736"/>
|
||||||
|
|
||||||
<!-- Favicon -->
|
<!-- Favicon -->
|
||||||
<link rel="icon" type="image/svg+xml" href="favicon.svg?v=8fcccd9a" />
|
<link rel="icon" type="image/svg+xml" href="favicon.svg?v=8fcccd9a"/>
|
||||||
|
|
||||||
<title>RustDesk</title>
|
<title>RustDesk</title>
|
||||||
<script src="/webclient-config/index.js"></script>
|
<script src="/webclient-config/index.js"></script>
|
||||||
<link rel="manifest" href="manifest.json" />
|
<link rel="manifest" href="manifest.json"/>
|
||||||
<script type="module" crossorigin src="js/dist/index.js?v=cabfd933"></script>
|
<script type="module" crossorigin src="js/dist/index.js?v=ddbe54f1"></script>
|
||||||
<link rel="modulepreload" href="js/dist/vendor.js?v=0b990c6e" />
|
<link rel="modulepreload" href="js/dist/vendor.js?v=0b990c6e"/>
|
||||||
<style>
|
<style>
|
||||||
html,
|
html,
|
||||||
body,
|
body,
|
||||||
#root {
|
#root {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
|
||||||
#root {
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-size: 100% auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.loading-title {
|
|
||||||
font-size: 1.1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.loading-sub-title {
|
|
||||||
margin-top: 20px;
|
|
||||||
font-size: 1rem;
|
|
||||||
color: #888;
|
|
||||||
}
|
|
||||||
|
|
||||||
.page-loading-warp {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
padding: 26px;
|
|
||||||
}
|
|
||||||
.ant-spin {
|
|
||||||
position: absolute;
|
|
||||||
display: none;
|
|
||||||
-webkit-box-sizing: border-box;
|
|
||||||
box-sizing: border-box;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
color: rgba(0, 0, 0, 0.65);
|
|
||||||
color: #1890ff;
|
|
||||||
font-size: 14px;
|
|
||||||
font-variant: tabular-nums;
|
|
||||||
line-height: 1.5;
|
|
||||||
text-align: center;
|
|
||||||
list-style: none;
|
|
||||||
opacity: 0;
|
|
||||||
-webkit-transition: -webkit-transform 0.3s
|
|
||||||
cubic-bezier(0.78, 0.14, 0.15, 0.86);
|
|
||||||
transition: -webkit-transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
|
|
||||||
transition: transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
|
|
||||||
transition: transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86),
|
|
||||||
-webkit-transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
|
|
||||||
-webkit-font-feature-settings: "tnum";
|
|
||||||
font-feature-settings: "tnum";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-spin-spinning {
|
|
||||||
position: static;
|
|
||||||
display: inline-block;
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-spin-dot {
|
|
||||||
position: relative;
|
|
||||||
display: inline-block;
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
font-size: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-spin-dot-item {
|
|
||||||
position: absolute;
|
|
||||||
display: block;
|
|
||||||
width: 9px;
|
|
||||||
height: 9px;
|
|
||||||
background-color: #1890ff;
|
|
||||||
border-radius: 100%;
|
|
||||||
-webkit-transform: scale(0.75);
|
|
||||||
-ms-transform: scale(0.75);
|
|
||||||
transform: scale(0.75);
|
|
||||||
-webkit-transform-origin: 50% 50%;
|
|
||||||
-ms-transform-origin: 50% 50%;
|
|
||||||
transform-origin: 50% 50%;
|
|
||||||
opacity: 0.3;
|
|
||||||
-webkit-animation: antspinmove 1s infinite linear alternate;
|
|
||||||
animation: antSpinMove 1s infinite linear alternate;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-spin-dot-item:nth-child(1) {
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-spin-dot-item:nth-child(2) {
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
-webkit-animation-delay: 0.4s;
|
|
||||||
animation-delay: 0.4s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-spin-dot-item:nth-child(3) {
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
-webkit-animation-delay: 0.8s;
|
|
||||||
animation-delay: 0.8s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-spin-dot-item:nth-child(4) {
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
-webkit-animation-delay: 1.2s;
|
|
||||||
animation-delay: 1.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-spin-dot-spin {
|
|
||||||
-webkit-transform: rotate(45deg);
|
|
||||||
-ms-transform: rotate(45deg);
|
|
||||||
transform: rotate(45deg);
|
|
||||||
-webkit-animation: antrotate 1.2s infinite linear;
|
|
||||||
animation: antRotate 1.2s infinite linear;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-spin-lg .ant-spin-dot {
|
|
||||||
width: 32px;
|
|
||||||
height: 32px;
|
|
||||||
font-size: 32px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-spin-lg .ant-spin-dot i {
|
|
||||||
width: 14px;
|
|
||||||
height: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media all and (-ms-high-contrast: none), (-ms-high-contrast: active) {
|
|
||||||
.ant-spin-blur {
|
|
||||||
background: #fff;
|
|
||||||
opacity: 0.5;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@-webkit-keyframes antSpinMove {
|
#root {
|
||||||
to {
|
background-repeat: no-repeat;
|
||||||
opacity: 1;
|
background-size: 100% auto;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes antSpinMove {
|
.loading-title {
|
||||||
to {
|
font-size: 1.1rem;
|
||||||
opacity: 1;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@-webkit-keyframes antRotate {
|
.loading-sub-title {
|
||||||
to {
|
margin-top: 20px;
|
||||||
-webkit-transform: rotate(405deg);
|
font-size: 1rem;
|
||||||
transform: rotate(405deg);
|
color: #888;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes antRotate {
|
.page-loading-warp {
|
||||||
to {
|
display: flex;
|
||||||
-webkit-transform: rotate(405deg);
|
align-items: center;
|
||||||
transform: rotate(405deg);
|
justify-content: center;
|
||||||
|
padding: 26px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-spin {
|
||||||
|
position: absolute;
|
||||||
|
display: none;
|
||||||
|
-webkit-box-sizing: border-box;
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
color: rgba(0, 0, 0, 0.65);
|
||||||
|
color: #1890ff;
|
||||||
|
font-size: 14px;
|
||||||
|
font-variant: tabular-nums;
|
||||||
|
line-height: 1.5;
|
||||||
|
text-align: center;
|
||||||
|
list-style: none;
|
||||||
|
opacity: 0;
|
||||||
|
-webkit-transition: -webkit-transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
|
||||||
|
transition: -webkit-transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
|
||||||
|
transition: transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
|
||||||
|
transition: transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86),
|
||||||
|
-webkit-transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
|
||||||
|
-webkit-font-feature-settings: "tnum";
|
||||||
|
font-feature-settings: "tnum";
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-spin-spinning {
|
||||||
|
position: static;
|
||||||
|
display: inline-block;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-spin-dot {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-spin-dot-item {
|
||||||
|
position: absolute;
|
||||||
|
display: block;
|
||||||
|
width: 9px;
|
||||||
|
height: 9px;
|
||||||
|
background-color: #1890ff;
|
||||||
|
border-radius: 100%;
|
||||||
|
-webkit-transform: scale(0.75);
|
||||||
|
-ms-transform: scale(0.75);
|
||||||
|
transform: scale(0.75);
|
||||||
|
-webkit-transform-origin: 50% 50%;
|
||||||
|
-ms-transform-origin: 50% 50%;
|
||||||
|
transform-origin: 50% 50%;
|
||||||
|
opacity: 0.3;
|
||||||
|
-webkit-animation: antspinmove 1s infinite linear alternate;
|
||||||
|
animation: antSpinMove 1s infinite linear alternate;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-spin-dot-item:nth-child(1) {
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-spin-dot-item:nth-child(2) {
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
-webkit-animation-delay: 0.4s;
|
||||||
|
animation-delay: 0.4s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-spin-dot-item:nth-child(3) {
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
-webkit-animation-delay: 0.8s;
|
||||||
|
animation-delay: 0.8s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-spin-dot-item:nth-child(4) {
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
-webkit-animation-delay: 1.2s;
|
||||||
|
animation-delay: 1.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-spin-dot-spin {
|
||||||
|
-webkit-transform: rotate(45deg);
|
||||||
|
-ms-transform: rotate(45deg);
|
||||||
|
transform: rotate(45deg);
|
||||||
|
-webkit-animation: antrotate 1.2s infinite linear;
|
||||||
|
animation: antRotate 1.2s infinite linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-spin-lg .ant-spin-dot {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
font-size: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-spin-lg .ant-spin-dot i {
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media all and (-ms-high-contrast: none), (-ms-high-contrast: active) {
|
||||||
|
.ant-spin-blur {
|
||||||
|
background: #fff;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@-webkit-keyframes antSpinMove {
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes antSpinMove {
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@-webkit-keyframes antRotate {
|
||||||
|
to {
|
||||||
|
-webkit-transform: rotate(405deg);
|
||||||
|
transform: rotate(405deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes antRotate {
|
||||||
|
to {
|
||||||
|
-webkit-transform: rotate(405deg);
|
||||||
|
transform: rotate(405deg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div id="root">
|
<div id="root">
|
||||||
<div
|
<div
|
||||||
id="div-background"
|
id="div-background"
|
||||||
style="
|
style="
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -212,117 +213,119 @@
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
min-height: 420px;
|
min-height: 420px;
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<img src="./favicon.svg?v=8fcccd9a" alt="logo" width="256" />
|
<img src="./favicon.svg?v=8fcccd9a" alt="logo" width="256"/>
|
||||||
<div class="page-loading-warp">
|
<div class="page-loading-warp">
|
||||||
<div class="ant-spin ant-spin-lg ant-spin-spinning">
|
<div class="ant-spin ant-spin-lg ant-spin-spinning">
|
||||||
<span class="ant-spin-dot ant-spin-dot-spin">
|
<span class="ant-spin-dot ant-spin-dot-spin">
|
||||||
<i class="ant-spin-dot-item"></i>
|
<i class="ant-spin-dot-item"></i>
|
||||||
<i class="ant-spin-dot-item"></i>
|
<i class="ant-spin-dot-item"></i>
|
||||||
<i class="ant-spin-dot-item"></i><i class="ant-spin-dot-item"></i>
|
<i class="ant-spin-dot-item"></i><i class="ant-spin-dot-item"></i>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
style="display: flex; align-items: center; justify-content: center"
|
style="display: flex; align-items: center; justify-content: center"
|
||||||
>
|
>
|
||||||
<img src="./favicon.svg?v=8fcccd9a" width="32" style="margin-right: 8px" />
|
<img src="./favicon.svg?v=8fcccd9a" width="32" style="margin-right: 8px"/>
|
||||||
<span id="span-text">RustDesk Web Client V2 Preview</span>
|
<span id="span-text">RustDesk Web Client V2 Preview</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<!-- This script installs service_worker.js to provide PWA functionality to
|
</div>
|
||||||
application. For more information, see:
|
<!-- This script installs service_worker.js to provide PWA functionality to
|
||||||
https://developers.google.com/web/fundamentals/primers/service-workers -->
|
application. For more information, see:
|
||||||
<script>
|
https://developers.google.com/web/fundamentals/primers/service-workers -->
|
||||||
const systemTheme = window.matchMedia("(prefers-color-scheme: dark)")
|
<script>
|
||||||
|
const systemTheme = window.matchMedia("(prefers-color-scheme: dark)")
|
||||||
.matches
|
.matches
|
||||||
? "dark"
|
? "dark"
|
||||||
: "light";
|
: "light";
|
||||||
const myTheme = localStorage.getItem("wc-option:local:theme");
|
const myTheme = localStorage.getItem("wc-option:local:theme");
|
||||||
const them = myTheme || systemTheme;
|
const them = myTheme || systemTheme;
|
||||||
|
|
||||||
const divBackground = document.querySelector("#div-background");
|
const divBackground = document.querySelector("#div-background");
|
||||||
if (divBackground) {
|
if (divBackground) {
|
||||||
divBackground.style.backgroundColor = them === "dark" ? "#000" : "#fff";
|
divBackground.style.backgroundColor = them === "dark" ? "#000" : "#fff";
|
||||||
}
|
}
|
||||||
const spanConsole = document.querySelector("#span-text");
|
const spanConsole = document.querySelector("#span-text");
|
||||||
if (spanConsole) {
|
if (spanConsole) {
|
||||||
spanConsole.style.color = them === "dark" ? "#fff" : "#000";
|
spanConsole.style.color = them === "dark" ? "#fff" : "#000";
|
||||||
}
|
}
|
||||||
|
|
||||||
const serviceWorkerVersion = "3267265270";
|
const serviceWorkerVersion = "461457302";
|
||||||
var scriptLoaded = false;
|
var scriptLoaded = false;
|
||||||
function loadMainDartJs() {
|
|
||||||
|
function loadMainDartJs() {
|
||||||
if (scriptLoaded) {
|
if (scriptLoaded) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
scriptLoaded = true;
|
scriptLoaded = true;
|
||||||
var scriptTag = document.createElement("script");
|
var scriptTag = document.createElement("script");
|
||||||
scriptTag.src = "main.dart.js?v=060a626e";
|
scriptTag.src = "main.dart.js?v=6d16cb80";
|
||||||
scriptTag.type = "application/javascript";
|
scriptTag.type = "application/javascript";
|
||||||
document.body.append(scriptTag);
|
document.body.append(scriptTag);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("serviceWorker" in navigator) {
|
if ("serviceWorker" in navigator) {
|
||||||
// Service workers are supported. Use them.
|
// Service workers are supported. Use them.
|
||||||
window.addEventListener("load", function () {
|
window.addEventListener("load", function () {
|
||||||
// Wait for registration to finish before dropping the <script> tag.
|
// Wait for registration to finish before dropping the <script> tag.
|
||||||
// Otherwise, the browser will load the script multiple times,
|
// Otherwise, the browser will load the script multiple times,
|
||||||
// potentially different versions.
|
// potentially different versions.
|
||||||
var serviceWorkerUrl =
|
var serviceWorkerUrl =
|
||||||
"flutter_service_worker.js?v=" + serviceWorkerVersion;
|
"flutter_service_worker.js?v=" + serviceWorkerVersion;
|
||||||
navigator.serviceWorker.register(serviceWorkerUrl).then((reg) => {
|
navigator.serviceWorker.register(serviceWorkerUrl).then((reg) => {
|
||||||
function waitForActivation(serviceWorker) {
|
function waitForActivation(serviceWorker) {
|
||||||
serviceWorker.addEventListener("statechange", () => {
|
serviceWorker.addEventListener("statechange", () => {
|
||||||
if (serviceWorker.state == "activated") {
|
if (serviceWorker.state == "activated") {
|
||||||
console.log("Installed new service worker.");
|
console.log("Installed new service worker.");
|
||||||
loadMainDartJs();
|
loadMainDartJs();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
|
||||||
if (!reg.active && (reg.installing || reg.waiting)) {
|
|
||||||
// No active web worker and we have installed or are installing
|
|
||||||
// one for the first time. Simply wait for it to activate.
|
|
||||||
waitForActivation(reg.installing || reg.waiting);
|
|
||||||
} else if (!reg.active.scriptURL.endsWith(serviceWorkerVersion)) {
|
|
||||||
// When the app updates the serviceWorkerVersion changes, so we
|
|
||||||
// need to ask the service worker to update.
|
|
||||||
console.log("New service worker available.");
|
|
||||||
reg.update();
|
|
||||||
waitForActivation(reg.installing);
|
|
||||||
} else {
|
|
||||||
// Existing service worker is still good.
|
|
||||||
console.log("Loading app from service worker.");
|
|
||||||
loadMainDartJs();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// If service worker doesn't succeed in a reasonable amount of time,
|
if (!reg.active && (reg.installing || reg.waiting)) {
|
||||||
// fallback to plaint <script> tag.
|
// No active web worker and we have installed or are installing
|
||||||
setTimeout(() => {
|
// one for the first time. Simply wait for it to activate.
|
||||||
if (!scriptLoaded) {
|
waitForActivation(reg.installing || reg.waiting);
|
||||||
console.warn(
|
} else if (!reg.active.scriptURL.endsWith(serviceWorkerVersion)) {
|
||||||
"Failed to load app from service worker. Falling back to plain <script> tag."
|
// When the app updates the serviceWorkerVersion changes, so we
|
||||||
);
|
// need to ask the service worker to update.
|
||||||
loadMainDartJs();
|
console.log("New service worker available.");
|
||||||
}
|
reg.update();
|
||||||
}, 4000);
|
waitForActivation(reg.installing);
|
||||||
|
} else {
|
||||||
|
// Existing service worker is still good.
|
||||||
|
console.log("Loading app from service worker.");
|
||||||
|
loadMainDartJs();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// If service worker doesn't succeed in a reasonable amount of time,
|
||||||
|
// fallback to plaint <script> tag.
|
||||||
|
setTimeout(() => {
|
||||||
|
if (!scriptLoaded) {
|
||||||
|
console.warn(
|
||||||
|
"Failed to load app from service worker. Falling back to plain <script> tag."
|
||||||
|
);
|
||||||
|
loadMainDartJs();
|
||||||
|
}
|
||||||
|
}, 4000);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// Service workers not supported. Just drop the <script> tag.
|
// Service workers not supported. Just drop the <script> tag.
|
||||||
loadMainDartJs();
|
loadMainDartJs();
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<script src="libs/stream/ponyfill.min.js"></script>
|
<script src="libs/stream/ponyfill.min.js"></script>
|
||||||
<script src="libs/stream/StreamSaver.min.js"></script>
|
<script src="libs/stream/StreamSaver.min.js"></script>
|
||||||
<script src="libs/firebase-app.js?8.10.1"></script>
|
<script src="libs/firebase-app.js?8.10.1"></script>
|
||||||
<script src="libs/firebase-analytics.js?8.10.1"></script>
|
<script src="libs/firebase-analytics.js?8.10.1"></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
// Your web app's Firebase configuration
|
// Your web app's Firebase configuration
|
||||||
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
|
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
|
||||||
const firebaseConfig = {
|
const firebaseConfig = {
|
||||||
apiKey: "AIzaSyCgehIZk1aFP0E7wZtYRRqrfvNiNAF39-A",
|
apiKey: "AIzaSyCgehIZk1aFP0E7wZtYRRqrfvNiNAF39-A",
|
||||||
authDomain: "rustdesk.firebaseapp.com",
|
authDomain: "rustdesk.firebaseapp.com",
|
||||||
databaseURL: "https://rustdesk.firebaseio.com",
|
databaseURL: "https://rustdesk.firebaseio.com",
|
||||||
@@ -331,11 +334,11 @@
|
|||||||
messagingSenderId: "768133699366",
|
messagingSenderId: "768133699366",
|
||||||
appId: "1:768133699366:web:d50faf0792cb208d7993e7",
|
appId: "1:768133699366:web:d50faf0792cb208d7993e7",
|
||||||
measurementId: "G-9PEH85N6ZQ",
|
measurementId: "G-9PEH85N6ZQ",
|
||||||
};
|
};
|
||||||
|
|
||||||
// Initialize Firebase
|
// Initialize Firebase
|
||||||
firebase.initializeApp(firebaseConfig);
|
firebase.initializeApp(firebaseConfig);
|
||||||
firebase.analytics();
|
firebase.analytics();
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
6568
resources/web2/js/dist/index.js
vendored
6568
resources/web2/js/dist/index.js
vendored
File diff suppressed because it is too large
Load Diff
7419
resources/web2/js/dist/lang.js
vendored
7419
resources/web2/js/dist/lang.js
vendored
File diff suppressed because it is too large
Load Diff
8
resources/web2/js/dist/ljw.js
vendored
8
resources/web2/js/dist/ljw.js
vendored
@@ -1,5 +1,11 @@
|
|||||||
window._gwen = {}
|
window._gwen = {}
|
||||||
window._gwen.kv = {}
|
window._gwen.kv = {}
|
||||||
|
|
||||||
|
//fix 语言
|
||||||
|
if(!localStorage.getItem('wc-option:local:lang') && navigator.language){
|
||||||
|
localStorage.setItem('wc-option:local:lang', navigator.language.toLowerCase())
|
||||||
|
}
|
||||||
|
|
||||||
const storage_prefix = 'wc-'
|
const storage_prefix = 'wc-'
|
||||||
const apiserver = localStorage.getItem('wc-api-server')
|
const apiserver = localStorage.getItem('wc-api-server')
|
||||||
|
|
||||||
@@ -46,7 +52,7 @@ if (share_token) {
|
|||||||
password: peer.tmppwd,
|
password: peer.tmppwd,
|
||||||
}*/
|
}*/
|
||||||
//修改location
|
//修改location
|
||||||
window.location.href = `/webclient2/#/${peer.info.id}?password=${peer.tmppwd}`
|
window.location.href = `/webclient2/#/${peer.info.id}?password=${encodeURIComponent(peer.tmppwd)}`
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
163526
resources/web2/main.dart.js
vendored
163526
resources/web2/main.dart.js
vendored
File diff suppressed because one or more lines are too long
@@ -3,7 +3,6 @@ package service
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/lejianwen/rustdesk-api/v2/global"
|
|
||||||
"github.com/lejianwen/rustdesk-api/v2/model"
|
"github.com/lejianwen/rustdesk-api/v2/model"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -14,24 +13,24 @@ type AddressBookService struct {
|
|||||||
|
|
||||||
func (s *AddressBookService) Info(id string) *model.AddressBook {
|
func (s *AddressBookService) Info(id string) *model.AddressBook {
|
||||||
p := &model.AddressBook{}
|
p := &model.AddressBook{}
|
||||||
global.DB.Where("id = ?", id).First(p)
|
DB.Where("id = ?", id).First(p)
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *AddressBookService) InfoByUserIdAndId(userid uint, id string) *model.AddressBook {
|
func (s *AddressBookService) InfoByUserIdAndId(userid uint, id string) *model.AddressBook {
|
||||||
p := &model.AddressBook{}
|
p := &model.AddressBook{}
|
||||||
global.DB.Where("user_id = ? and id = ?", userid, id).First(p)
|
DB.Where("user_id = ? and id = ?", userid, id).First(p)
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *AddressBookService) InfoByUserIdAndIdAndCid(userid uint, id string, cid uint) *model.AddressBook {
|
func (s *AddressBookService) InfoByUserIdAndIdAndCid(userid uint, id string, cid uint) *model.AddressBook {
|
||||||
p := &model.AddressBook{}
|
p := &model.AddressBook{}
|
||||||
global.DB.Where("user_id = ? and id = ? and collection_id = ?", userid, id, cid).First(p)
|
DB.Where("user_id = ? and id = ? and collection_id = ?", userid, id, cid).First(p)
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
func (s *AddressBookService) InfoByRowId(id uint) *model.AddressBook {
|
func (s *AddressBookService) InfoByRowId(id uint) *model.AddressBook {
|
||||||
p := &model.AddressBook{}
|
p := &model.AddressBook{}
|
||||||
global.DB.Where("row_id = ?", id).First(p)
|
DB.Where("row_id = ?", id).First(p)
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
func (s *AddressBookService) ListByUserId(userId, page, pageSize uint) (res *model.AddressBookList) {
|
func (s *AddressBookService) ListByUserId(userId, page, pageSize uint) (res *model.AddressBookList) {
|
||||||
@@ -49,14 +48,14 @@ func (s *AddressBookService) ListByUserIds(userIds []uint, page, pageSize uint)
|
|||||||
|
|
||||||
// AddAddressBook
|
// AddAddressBook
|
||||||
func (s *AddressBookService) AddAddressBook(ab *model.AddressBook) error {
|
func (s *AddressBookService) AddAddressBook(ab *model.AddressBook) error {
|
||||||
return global.DB.Create(ab).Error
|
return DB.Create(ab).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateAddressBook
|
// UpdateAddressBook
|
||||||
func (s *AddressBookService) UpdateAddressBook(abs []*model.AddressBook, userId uint) error {
|
func (s *AddressBookService) UpdateAddressBook(abs []*model.AddressBook, userId uint) error {
|
||||||
//比较peers和数据库中的数据,如果peers中的数据在数据库中不存在,则添加,如果存在则更新,如果数据库中的数据在peers中不存在,则删除
|
//比较peers和数据库中的数据,如果peers中的数据在数据库中不存在,则添加,如果存在则更新,如果数据库中的数据在peers中不存在,则删除
|
||||||
// 开始事务
|
// 开始事务
|
||||||
tx := global.DB.Begin()
|
tx := DB.Begin()
|
||||||
//1. 获取数据库中的数据
|
//1. 获取数据库中的数据
|
||||||
var dbABs []*model.AddressBook
|
var dbABs []*model.AddressBook
|
||||||
tx.Where("user_id = ?", userId).Find(&dbABs)
|
tx.Where("user_id = ?", userId).Find(&dbABs)
|
||||||
@@ -107,7 +106,7 @@ func (s *AddressBookService) List(page, pageSize uint, where func(tx *gorm.DB))
|
|||||||
res = &model.AddressBookList{}
|
res = &model.AddressBookList{}
|
||||||
res.Page = int64(page)
|
res.Page = int64(page)
|
||||||
res.PageSize = int64(pageSize)
|
res.PageSize = int64(pageSize)
|
||||||
tx := global.DB.Model(&model.AddressBook{})
|
tx := DB.Model(&model.AddressBook{})
|
||||||
if where != nil {
|
if where != nil {
|
||||||
where(tx)
|
where(tx)
|
||||||
}
|
}
|
||||||
@@ -129,38 +128,38 @@ func (s *AddressBookService) FromPeer(peer *model.Peer) (a *model.AddressBook) {
|
|||||||
|
|
||||||
// Create 创建
|
// Create 创建
|
||||||
func (s *AddressBookService) Create(u *model.AddressBook) error {
|
func (s *AddressBookService) Create(u *model.AddressBook) error {
|
||||||
res := global.DB.Create(u).Error
|
res := DB.Create(u).Error
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
func (s *AddressBookService) Delete(u *model.AddressBook) error {
|
func (s *AddressBookService) Delete(u *model.AddressBook) error {
|
||||||
return global.DB.Delete(u).Error
|
return DB.Delete(u).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update 更新
|
// Update 更新
|
||||||
func (s *AddressBookService) Update(u *model.AddressBook) error {
|
func (s *AddressBookService) Update(u *model.AddressBook) error {
|
||||||
return global.DB.Model(u).Updates(u).Error
|
return DB.Model(u).Updates(u).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateByMap 更新
|
// UpdateByMap 更新
|
||||||
func (s *AddressBookService) UpdateByMap(u *model.AddressBook, data map[string]interface{}) error {
|
func (s *AddressBookService) UpdateByMap(u *model.AddressBook, data map[string]interface{}) error {
|
||||||
return global.DB.Model(u).Updates(data).Error
|
return DB.Model(u).Updates(data).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateAll 更新
|
// UpdateAll 更新
|
||||||
func (s *AddressBookService) UpdateAll(u *model.AddressBook) error {
|
func (s *AddressBookService) UpdateAll(u *model.AddressBook) error {
|
||||||
return global.DB.Model(u).Select("*").Omit("created_at").Updates(u).Error
|
return DB.Model(u).Select("*").Omit("created_at").Updates(u).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
// ShareByWebClient 分享
|
// ShareByWebClient 分享
|
||||||
func (s *AddressBookService) ShareByWebClient(m *model.ShareRecord) error {
|
func (s *AddressBookService) ShareByWebClient(m *model.ShareRecord) error {
|
||||||
m.ShareToken = uuid.New().String()
|
m.ShareToken = uuid.New().String()
|
||||||
return global.DB.Create(m).Error
|
return DB.Create(m).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
// SharedPeer
|
// SharedPeer
|
||||||
func (s *AddressBookService) SharedPeer(shareToken string) *model.ShareRecord {
|
func (s *AddressBookService) SharedPeer(shareToken string) *model.ShareRecord {
|
||||||
m := &model.ShareRecord{}
|
m := &model.ShareRecord{}
|
||||||
global.DB.Where("share_token = ?", shareToken).First(m)
|
DB.Where("share_token = ?", shareToken).First(m)
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,7 +189,7 @@ func (s *AddressBookService) ListCollection(page, pageSize uint, where func(tx *
|
|||||||
res = &model.AddressBookCollectionList{}
|
res = &model.AddressBookCollectionList{}
|
||||||
res.Page = int64(page)
|
res.Page = int64(page)
|
||||||
res.PageSize = int64(pageSize)
|
res.PageSize = int64(pageSize)
|
||||||
tx := global.DB.Model(&model.AddressBookCollection{})
|
tx := DB.Model(&model.AddressBookCollection{})
|
||||||
if where != nil {
|
if where != nil {
|
||||||
where(tx)
|
where(tx)
|
||||||
}
|
}
|
||||||
@@ -200,7 +199,7 @@ func (s *AddressBookService) ListCollection(page, pageSize uint, where func(tx *
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
func (s *AddressBookService) ListCollectionByIds(ids []uint) (res []*model.AddressBookCollection) {
|
func (s *AddressBookService) ListCollectionByIds(ids []uint) (res []*model.AddressBookCollection) {
|
||||||
global.DB.Where("id in ?", ids).Find(&res)
|
DB.Where("id in ?", ids).Find(&res)
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,20 +211,20 @@ func (s *AddressBookService) ListCollectionByUserId(userId uint) (res *model.Add
|
|||||||
}
|
}
|
||||||
func (s *AddressBookService) CollectionInfoById(id uint) *model.AddressBookCollection {
|
func (s *AddressBookService) CollectionInfoById(id uint) *model.AddressBookCollection {
|
||||||
p := &model.AddressBookCollection{}
|
p := &model.AddressBookCollection{}
|
||||||
global.DB.Where("id = ?", id).First(p)
|
DB.Where("id = ?", id).First(p)
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *AddressBookService) CollectionReadRules(user *model.User) (res []*model.AddressBookCollectionRule) {
|
func (s *AddressBookService) CollectionReadRules(user *model.User) (res []*model.AddressBookCollectionRule) {
|
||||||
// personalRules
|
// personalRules
|
||||||
var personalRules []*model.AddressBookCollectionRule
|
var personalRules []*model.AddressBookCollectionRule
|
||||||
tx2 := global.DB.Model(&model.AddressBookCollectionRule{})
|
tx2 := DB.Model(&model.AddressBookCollectionRule{})
|
||||||
tx2.Where("type = ? and to_id = ? and rule > 0", model.ShareAddressBookRuleTypePersonal, user.Id).Find(&personalRules)
|
tx2.Where("type = ? and to_id = ? and rule > 0", model.ShareAddressBookRuleTypePersonal, user.Id).Find(&personalRules)
|
||||||
res = append(res, personalRules...)
|
res = append(res, personalRules...)
|
||||||
|
|
||||||
//group
|
//group
|
||||||
var groupRules []*model.AddressBookCollectionRule
|
var groupRules []*model.AddressBookCollectionRule
|
||||||
tx3 := global.DB.Model(&model.AddressBookCollectionRule{})
|
tx3 := DB.Model(&model.AddressBookCollectionRule{})
|
||||||
tx3.Where("type = ? and to_id = ? and rule > 0", model.ShareAddressBookRuleTypeGroup, user.GroupId).Find(&groupRules)
|
tx3.Where("type = ? and to_id = ? and rule > 0", model.ShareAddressBookRuleTypeGroup, user.GroupId).Find(&groupRules)
|
||||||
res = append(res, groupRules...)
|
res = append(res, groupRules...)
|
||||||
return
|
return
|
||||||
@@ -238,7 +237,7 @@ func (s *AddressBookService) UserMaxRule(user *model.User, uid, cid uint) int {
|
|||||||
}
|
}
|
||||||
max := 0
|
max := 0
|
||||||
personalRules := &model.AddressBookCollectionRule{}
|
personalRules := &model.AddressBookCollectionRule{}
|
||||||
tx := global.DB.Model(personalRules)
|
tx := DB.Model(personalRules)
|
||||||
tx.Where("type = ? and collection_id = ? and to_id = ?", model.ShareAddressBookRuleTypePersonal, cid, user.Id).First(&personalRules)
|
tx.Where("type = ? and collection_id = ? and to_id = ?", model.ShareAddressBookRuleTypePersonal, cid, user.Id).First(&personalRules)
|
||||||
if personalRules.Id != 0 {
|
if personalRules.Id != 0 {
|
||||||
max = personalRules.Rule
|
max = personalRules.Rule
|
||||||
@@ -248,7 +247,7 @@ func (s *AddressBookService) UserMaxRule(user *model.User, uid, cid uint) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
groupRules := &model.AddressBookCollectionRule{}
|
groupRules := &model.AddressBookCollectionRule{}
|
||||||
tx2 := global.DB.Model(groupRules)
|
tx2 := DB.Model(groupRules)
|
||||||
tx2.Where("type = ? and collection_id = ? and to_id = ?", model.ShareAddressBookRuleTypeGroup, cid, user.GroupId).First(&groupRules)
|
tx2.Where("type = ? and collection_id = ? and to_id = ?", model.ShareAddressBookRuleTypeGroup, cid, user.GroupId).First(&groupRules)
|
||||||
if groupRules.Id != 0 {
|
if groupRules.Id != 0 {
|
||||||
if groupRules.Rule > max {
|
if groupRules.Rule > max {
|
||||||
@@ -272,16 +271,16 @@ func (s *AddressBookService) CheckUserFullControlPrivilege(user *model.User, uid
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *AddressBookService) CreateCollection(t *model.AddressBookCollection) error {
|
func (s *AddressBookService) CreateCollection(t *model.AddressBookCollection) error {
|
||||||
return global.DB.Create(t).Error
|
return DB.Create(t).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *AddressBookService) UpdateCollection(t *model.AddressBookCollection) error {
|
func (s *AddressBookService) UpdateCollection(t *model.AddressBookCollection) error {
|
||||||
return global.DB.Model(t).Updates(t).Error
|
return DB.Model(t).Updates(t).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *AddressBookService) DeleteCollection(t *model.AddressBookCollection) error {
|
func (s *AddressBookService) DeleteCollection(t *model.AddressBookCollection) error {
|
||||||
//删除集合下的所有规则、地址簿,再删除集合
|
//删除集合下的所有规则、地址簿,再删除集合
|
||||||
tx := global.DB.Begin()
|
tx := DB.Begin()
|
||||||
tx.Where("collection_id = ?", t.Id).Delete(&model.AddressBookCollectionRule{})
|
tx.Where("collection_id = ?", t.Id).Delete(&model.AddressBookCollectionRule{})
|
||||||
tx.Where("collection_id = ?", t.Id).Delete(&model.AddressBook{})
|
tx.Where("collection_id = ?", t.Id).Delete(&model.AddressBook{})
|
||||||
tx.Delete(t)
|
tx.Delete(t)
|
||||||
@@ -290,23 +289,26 @@ func (s *AddressBookService) DeleteCollection(t *model.AddressBookCollection) er
|
|||||||
|
|
||||||
func (s *AddressBookService) RuleInfoById(u uint) *model.AddressBookCollectionRule {
|
func (s *AddressBookService) RuleInfoById(u uint) *model.AddressBookCollectionRule {
|
||||||
p := &model.AddressBookCollectionRule{}
|
p := &model.AddressBookCollectionRule{}
|
||||||
global.DB.Where("id = ?", u).First(p)
|
DB.Where("id = ?", u).First(p)
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
func (s *AddressBookService) RulePersonalInfoByToIdAndCid(toid, cid uint) *model.AddressBookCollectionRule {
|
func (s *AddressBookService) RulePersonalInfoByToIdAndCid(toid, cid uint) *model.AddressBookCollectionRule {
|
||||||
|
return s.RuleInfoByToIdAndCid(model.ShareAddressBookRuleTypePersonal, toid, cid)
|
||||||
|
}
|
||||||
|
func (s *AddressBookService) RuleInfoByToIdAndCid(t int, toid, cid uint) *model.AddressBookCollectionRule {
|
||||||
p := &model.AddressBookCollectionRule{}
|
p := &model.AddressBookCollectionRule{}
|
||||||
global.DB.Where("type = ? and to_id = ? and collection_id = ?", model.ShareAddressBookRuleTypePersonal, toid, cid).First(p)
|
DB.Where("type = ? and to_id = ? and collection_id = ?", t, toid, cid).First(p)
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
func (s *AddressBookService) CreateRule(t *model.AddressBookCollectionRule) error {
|
func (s *AddressBookService) CreateRule(t *model.AddressBookCollectionRule) error {
|
||||||
return global.DB.Create(t).Error
|
return DB.Create(t).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *AddressBookService) ListRules(page uint, size uint, f func(tx *gorm.DB)) *model.AddressBookCollectionRuleList {
|
func (s *AddressBookService) ListRules(page uint, size uint, f func(tx *gorm.DB)) *model.AddressBookCollectionRuleList {
|
||||||
res := &model.AddressBookCollectionRuleList{}
|
res := &model.AddressBookCollectionRuleList{}
|
||||||
res.Page = int64(page)
|
res.Page = int64(page)
|
||||||
res.PageSize = int64(size)
|
res.PageSize = int64(size)
|
||||||
tx := global.DB.Model(&model.AddressBookCollectionRule{})
|
tx := DB.Model(&model.AddressBookCollectionRule{})
|
||||||
if f != nil {
|
if f != nil {
|
||||||
f(tx)
|
f(tx)
|
||||||
}
|
}
|
||||||
@@ -317,11 +319,11 @@ func (s *AddressBookService) ListRules(page uint, size uint, f func(tx *gorm.DB)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *AddressBookService) UpdateRule(t *model.AddressBookCollectionRule) error {
|
func (s *AddressBookService) UpdateRule(t *model.AddressBookCollectionRule) error {
|
||||||
return global.DB.Model(t).Updates(t).Error
|
return DB.Model(t).Updates(t).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *AddressBookService) DeleteRule(t *model.AddressBookCollectionRule) error {
|
func (s *AddressBookService) DeleteRule(t *model.AddressBookCollectionRule) error {
|
||||||
return global.DB.Delete(t).Error
|
return DB.Delete(t).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckCollectionOwner 检查Collection的所有者
|
// CheckCollectionOwner 检查Collection的所有者
|
||||||
@@ -336,5 +338,5 @@ func (s *AddressBookService) BatchUpdateTags(abs []*model.AddressBook, tags []st
|
|||||||
ids = append(ids, ab.RowId)
|
ids = append(ids, ab.RowId)
|
||||||
}
|
}
|
||||||
tagsv, _ := json.Marshal(tags)
|
tagsv, _ := json.Marshal(tags)
|
||||||
return global.DB.Model(&model.AddressBook{}).Where("row_id in ?", ids).Update("tags", tagsv).Error
|
return DB.Model(&model.AddressBook{}).Where("row_id in ?", ids).Update("tags", tagsv).Error
|
||||||
}
|
}
|
||||||
|
|||||||
28
service/app.go
Normal file
28
service/app.go
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AppService struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
var version = ""
|
||||||
|
|
||||||
|
var once = &sync.Once{}
|
||||||
|
|
||||||
|
func (a *AppService) GetAppVersion() string {
|
||||||
|
if version != "" {
|
||||||
|
return version
|
||||||
|
}
|
||||||
|
once.Do(func() {
|
||||||
|
v, err := os.ReadFile("resources/version")
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
version = string(v)
|
||||||
|
|
||||||
|
})
|
||||||
|
return version
|
||||||
|
}
|
||||||
33
service/app_test.go
Normal file
33
service/app_test.go
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestGetAppVersion
|
||||||
|
func TestGetAppVersion(t *testing.T) {
|
||||||
|
s := &AppService{}
|
||||||
|
v := s.GetAppVersion()
|
||||||
|
// 打印结果
|
||||||
|
t.Logf("App Version: %s", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMultipleGetAppVersion(t *testing.T) {
|
||||||
|
s := &AppService{}
|
||||||
|
//并发测试
|
||||||
|
// 使用 WaitGroup 等待所有 goroutine 完成
|
||||||
|
wg := sync.WaitGroup{}
|
||||||
|
wg.Add(10) // 启动 10 个 goroutine
|
||||||
|
// 启动 10 个 goroutine
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
go func() {
|
||||||
|
defer wg.Done() // 完成后减少计数
|
||||||
|
v := s.GetAppVersion()
|
||||||
|
// 打印结果
|
||||||
|
t.Logf("App Version: %s", v)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
// 等待所有 goroutine 完成
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/lejianwen/rustdesk-api/v2/global"
|
|
||||||
"github.com/lejianwen/rustdesk-api/v2/model"
|
"github.com/lejianwen/rustdesk-api/v2/model"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
@@ -13,7 +12,7 @@ func (as *AuditService) AuditConnList(page, pageSize uint, where func(tx *gorm.D
|
|||||||
res = &model.AuditConnList{}
|
res = &model.AuditConnList{}
|
||||||
res.Page = int64(page)
|
res.Page = int64(page)
|
||||||
res.PageSize = int64(pageSize)
|
res.PageSize = int64(pageSize)
|
||||||
tx := global.DB.Model(&model.AuditConn{})
|
tx := DB.Model(&model.AuditConn{})
|
||||||
if where != nil {
|
if where != nil {
|
||||||
where(tx)
|
where(tx)
|
||||||
}
|
}
|
||||||
@@ -25,36 +24,36 @@ func (as *AuditService) AuditConnList(page, pageSize uint, where func(tx *gorm.D
|
|||||||
|
|
||||||
// Create 创建
|
// Create 创建
|
||||||
func (as *AuditService) CreateAuditConn(u *model.AuditConn) error {
|
func (as *AuditService) CreateAuditConn(u *model.AuditConn) error {
|
||||||
res := global.DB.Create(u).Error
|
res := DB.Create(u).Error
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
func (as *AuditService) DeleteAuditConn(u *model.AuditConn) error {
|
func (as *AuditService) DeleteAuditConn(u *model.AuditConn) error {
|
||||||
return global.DB.Delete(u).Error
|
return DB.Delete(u).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update 更新
|
// Update 更新
|
||||||
func (as *AuditService) UpdateAuditConn(u *model.AuditConn) error {
|
func (as *AuditService) UpdateAuditConn(u *model.AuditConn) error {
|
||||||
return global.DB.Model(u).Updates(u).Error
|
return DB.Model(u).Updates(u).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
// InfoByPeerIdAndConnId
|
// InfoByPeerIdAndConnId
|
||||||
func (as *AuditService) InfoByPeerIdAndConnId(peerId string, connId int64) (res *model.AuditConn) {
|
func (as *AuditService) InfoByPeerIdAndConnId(peerId string, connId int64) (res *model.AuditConn) {
|
||||||
res = &model.AuditConn{}
|
res = &model.AuditConn{}
|
||||||
global.DB.Where("peer_id = ? and conn_id = ?", peerId, connId).First(res)
|
DB.Where("peer_id = ? and conn_id = ?", peerId, connId).First(res)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConnInfoById
|
// ConnInfoById
|
||||||
func (as *AuditService) ConnInfoById(id uint) (res *model.AuditConn) {
|
func (as *AuditService) ConnInfoById(id uint) (res *model.AuditConn) {
|
||||||
res = &model.AuditConn{}
|
res = &model.AuditConn{}
|
||||||
global.DB.Where("id = ?", id).First(res)
|
DB.Where("id = ?", id).First(res)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// FileInfoById
|
// FileInfoById
|
||||||
func (as *AuditService) FileInfoById(id uint) (res *model.AuditFile) {
|
func (as *AuditService) FileInfoById(id uint) (res *model.AuditFile) {
|
||||||
res = &model.AuditFile{}
|
res = &model.AuditFile{}
|
||||||
global.DB.Where("id = ?", id).First(res)
|
DB.Where("id = ?", id).First(res)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,7 +61,7 @@ func (as *AuditService) AuditFileList(page, pageSize uint, where func(tx *gorm.D
|
|||||||
res = &model.AuditFileList{}
|
res = &model.AuditFileList{}
|
||||||
res.Page = int64(page)
|
res.Page = int64(page)
|
||||||
res.PageSize = int64(pageSize)
|
res.PageSize = int64(pageSize)
|
||||||
tx := global.DB.Model(&model.AuditFile{})
|
tx := DB.Model(&model.AuditFile{})
|
||||||
if where != nil {
|
if where != nil {
|
||||||
where(tx)
|
where(tx)
|
||||||
}
|
}
|
||||||
@@ -74,22 +73,22 @@ func (as *AuditService) AuditFileList(page, pageSize uint, where func(tx *gorm.D
|
|||||||
|
|
||||||
// CreateAuditFile
|
// CreateAuditFile
|
||||||
func (as *AuditService) CreateAuditFile(u *model.AuditFile) error {
|
func (as *AuditService) CreateAuditFile(u *model.AuditFile) error {
|
||||||
res := global.DB.Create(u).Error
|
res := DB.Create(u).Error
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
func (as *AuditService) DeleteAuditFile(u *model.AuditFile) error {
|
func (as *AuditService) DeleteAuditFile(u *model.AuditFile) error {
|
||||||
return global.DB.Delete(u).Error
|
return DB.Delete(u).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update 更新
|
// Update 更新
|
||||||
func (as *AuditService) UpdateAuditFile(u *model.AuditFile) error {
|
func (as *AuditService) UpdateAuditFile(u *model.AuditFile) error {
|
||||||
return global.DB.Model(u).Updates(u).Error
|
return DB.Model(u).Updates(u).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (as *AuditService) BatchDeleteAuditConn(ids []uint) error {
|
func (as *AuditService) BatchDeleteAuditConn(ids []uint) error {
|
||||||
return global.DB.Where("id in (?)", ids).Delete(&model.AuditConn{}).Error
|
return DB.Where("id in (?)", ids).Delete(&model.AuditConn{}).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (as *AuditService) BatchDeleteAuditFile(ids []uint) error {
|
func (as *AuditService) BatchDeleteAuditFile(ids []uint) error {
|
||||||
return global.DB.Where("id in (?)", ids).Delete(&model.AuditFile{}).Error
|
return DB.Where("id in (?)", ids).Delete(&model.AuditFile{}).Error
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/lejianwen/rustdesk-api/v2/global"
|
|
||||||
"github.com/lejianwen/rustdesk-api/v2/model"
|
"github.com/lejianwen/rustdesk-api/v2/model"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
@@ -12,7 +11,7 @@ type GroupService struct {
|
|||||||
// InfoById 根据用户id取用户信息
|
// InfoById 根据用户id取用户信息
|
||||||
func (us *GroupService) InfoById(id uint) *model.Group {
|
func (us *GroupService) InfoById(id uint) *model.Group {
|
||||||
u := &model.Group{}
|
u := &model.Group{}
|
||||||
global.DB.Where("id = ?", id).First(u)
|
DB.Where("id = ?", id).First(u)
|
||||||
return u
|
return u
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -20,7 +19,7 @@ func (us *GroupService) List(page, pageSize uint, where func(tx *gorm.DB)) (res
|
|||||||
res = &model.GroupList{}
|
res = &model.GroupList{}
|
||||||
res.Page = int64(page)
|
res.Page = int64(page)
|
||||||
res.PageSize = int64(pageSize)
|
res.PageSize = int64(pageSize)
|
||||||
tx := global.DB.Model(&model.Group{})
|
tx := DB.Model(&model.Group{})
|
||||||
if where != nil {
|
if where != nil {
|
||||||
where(tx)
|
where(tx)
|
||||||
}
|
}
|
||||||
@@ -32,14 +31,47 @@ func (us *GroupService) List(page, pageSize uint, where func(tx *gorm.DB)) (res
|
|||||||
|
|
||||||
// Create 创建
|
// Create 创建
|
||||||
func (us *GroupService) Create(u *model.Group) error {
|
func (us *GroupService) Create(u *model.Group) error {
|
||||||
res := global.DB.Create(u).Error
|
res := DB.Create(u).Error
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
func (us *GroupService) Delete(u *model.Group) error {
|
func (us *GroupService) Delete(u *model.Group) error {
|
||||||
return global.DB.Delete(u).Error
|
return DB.Delete(u).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update 更新
|
// Update 更新
|
||||||
func (us *GroupService) Update(u *model.Group) error {
|
func (us *GroupService) Update(u *model.Group) error {
|
||||||
return global.DB.Model(u).Updates(u).Error
|
return DB.Model(u).Updates(u).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeviceGroupInfoById 根据用户id取用户信息
|
||||||
|
func (us *GroupService) DeviceGroupInfoById(id uint) *model.DeviceGroup {
|
||||||
|
u := &model.DeviceGroup{}
|
||||||
|
DB.Where("id = ?", id).First(u)
|
||||||
|
return u
|
||||||
|
}
|
||||||
|
|
||||||
|
func (us *GroupService) DeviceGroupList(page, pageSize uint, where func(tx *gorm.DB)) (res *model.DeviceGroupList) {
|
||||||
|
res = &model.DeviceGroupList{}
|
||||||
|
res.Page = int64(page)
|
||||||
|
res.PageSize = int64(pageSize)
|
||||||
|
tx := DB.Model(&model.DeviceGroup{})
|
||||||
|
if where != nil {
|
||||||
|
where(tx)
|
||||||
|
}
|
||||||
|
tx.Count(&res.Total)
|
||||||
|
tx.Scopes(Paginate(page, pageSize))
|
||||||
|
tx.Find(&res.DeviceGroups)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (us *GroupService) DeviceGroupCreate(u *model.DeviceGroup) error {
|
||||||
|
res := DB.Create(u).Error
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
func (us *GroupService) DeviceGroupDelete(u *model.DeviceGroup) error {
|
||||||
|
return DB.Delete(u).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (us *GroupService) DeviceGroupUpdate(u *model.DeviceGroup) error {
|
||||||
|
return DB.Model(u).Updates(u).Error
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,19 +2,23 @@ package service
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/go-ldap/ldap/v3"
|
"github.com/go-ldap/ldap/v3"
|
||||||
|
|
||||||
"github.com/lejianwen/rustdesk-api/v2/config"
|
"github.com/lejianwen/rustdesk-api/v2/config"
|
||||||
"github.com/lejianwen/rustdesk-api/v2/global"
|
|
||||||
"github.com/lejianwen/rustdesk-api/v2/model"
|
"github.com/lejianwen/rustdesk-api/v2/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
ErrUrlParseFailed = errors.New("UrlParseFailed")
|
||||||
|
ErrFileReadFailed = errors.New("FileReadFailed")
|
||||||
ErrLdapNotEnabled = errors.New("LdapNotEnabled")
|
ErrLdapNotEnabled = errors.New("LdapNotEnabled")
|
||||||
ErrLdapUserDisabled = errors.New("UserDisabledAtLdap")
|
ErrLdapUserDisabled = errors.New("UserDisabledAtLdap")
|
||||||
ErrLdapUserNotFound = errors.New("UserNotFound")
|
ErrLdapUserNotFound = errors.New("UserNotFound")
|
||||||
@@ -26,6 +30,7 @@ var (
|
|||||||
ErrLdapBindFailed = errors.New("LdapBindFailed")
|
ErrLdapBindFailed = errors.New("LdapBindFailed")
|
||||||
ErrLdapToLocalUserFailed = errors.New("LdapToLocalUserFailed")
|
ErrLdapToLocalUserFailed = errors.New("LdapToLocalUserFailed")
|
||||||
ErrLdapCreateUserFailed = errors.New("LdapCreateUserFailed")
|
ErrLdapCreateUserFailed = errors.New("LdapCreateUserFailed")
|
||||||
|
ErrLdapPasswordNotMatch = errors.New("PasswordNotMatch")
|
||||||
)
|
)
|
||||||
|
|
||||||
// LdapService is responsible for LDAP authentication and user synchronization.
|
// LdapService is responsible for LDAP authentication and user synchronization.
|
||||||
@@ -68,21 +73,38 @@ func (lu *LdapUser) ToUser(u *model.User) *model.User {
|
|||||||
|
|
||||||
// connectAndBind creates an LDAP connection, optionally starts TLS, and then binds using the provided credentials.
|
// connectAndBind creates an LDAP connection, optionally starts TLS, and then binds using the provided credentials.
|
||||||
func (ls *LdapService) connectAndBind(cfg *config.Ldap, username, password string) (*ldap.Conn, error) {
|
func (ls *LdapService) connectAndBind(cfg *config.Ldap, username, password string) (*ldap.Conn, error) {
|
||||||
conn, err := ldap.DialURL(cfg.Url)
|
u, err := url.Parse(cfg.Url)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Join(ErrUrlParseFailed, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var conn *ldap.Conn
|
||||||
|
if u.Scheme == "ldaps" {
|
||||||
|
// WARNING: InsecureSkipVerify: true is not recommended for production
|
||||||
|
tlsConfig := &tls.Config{InsecureSkipVerify: !cfg.TlsVerify}
|
||||||
|
if cfg.TlsCaFile != "" {
|
||||||
|
caCert, err := os.ReadFile(cfg.TlsCaFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Join(ErrFileReadFailed, err)
|
||||||
|
}
|
||||||
|
caCertPool := x509.NewCertPool()
|
||||||
|
if !caCertPool.AppendCertsFromPEM(caCert) {
|
||||||
|
return nil, errors.Join(ErrLdapTlsFailed, errors.New("failed to append CA certificate"))
|
||||||
|
}
|
||||||
|
tlsConfig.RootCAs = caCertPool
|
||||||
|
}
|
||||||
|
conn, err = ldap.DialURL(cfg.Url, ldap.DialWithTLSConfig(tlsConfig))
|
||||||
|
} else {
|
||||||
|
conn, err = ldap.DialURL(cfg.Url)
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Join(ErrLdapConnectFailed, err)
|
return nil, errors.Join(ErrLdapConnectFailed, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg.TLS {
|
|
||||||
// WARNING: InsecureSkipVerify: true is not recommended for production
|
|
||||||
if err = conn.StartTLS(&tls.Config{InsecureSkipVerify: !cfg.TlsVerify}); err != nil {
|
|
||||||
conn.Close()
|
|
||||||
return nil, errors.Join(ErrLdapTlsFailed, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bind as the "service" user
|
// Bind as the "service" user
|
||||||
if err = conn.Bind(username, password); err != nil {
|
if err = conn.Bind(username, password); err != nil {
|
||||||
|
fmt.Println("Bind failed")
|
||||||
conn.Close()
|
conn.Close()
|
||||||
return nil, errors.Join(ErrLdapBindService, err)
|
return nil, errors.Join(ErrLdapBindService, err)
|
||||||
}
|
}
|
||||||
@@ -98,7 +120,7 @@ func (ls *LdapService) connectAndBindAdmin(cfg *config.Ldap) (*ldap.Conn, error)
|
|||||||
func (ls *LdapService) verifyCredentials(cfg *config.Ldap, username, password string) error {
|
func (ls *LdapService) verifyCredentials(cfg *config.Ldap, username, password string) error {
|
||||||
ldapConn, err := ls.connectAndBind(cfg, username, password)
|
ldapConn, err := ls.connectAndBind(cfg, username, password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return ErrLdapPasswordNotMatch
|
||||||
}
|
}
|
||||||
defer ldapConn.Close()
|
defer ldapConn.Close()
|
||||||
return nil
|
return nil
|
||||||
@@ -114,7 +136,11 @@ func (ls *LdapService) Authenticate(username, password string) (*model.User, err
|
|||||||
if !ldapUser.Enabled {
|
if !ldapUser.Enabled {
|
||||||
return nil, ErrLdapUserDisabled
|
return nil, ErrLdapUserDisabled
|
||||||
}
|
}
|
||||||
cfg := &global.Config.Ldap
|
cfg := &Config.Ldap
|
||||||
|
err = ls.verifyCredentials(cfg, ldapUser.Dn, password)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
user, err := ls.mapToLocalUser(cfg, ldapUser)
|
user, err := ls.mapToLocalUser(cfg, ldapUser)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Join(ErrLdapToLocalUserFailed, err)
|
return nil, errors.Join(ErrLdapToLocalUserFailed, err)
|
||||||
@@ -135,7 +161,7 @@ func (ls *LdapService) mapToLocalUser(cfg *config.Ldap, lu *LdapUser) (*model.Us
|
|||||||
// If needed, you can set a random password here.
|
// If needed, you can set a random password here.
|
||||||
newUser.IsAdmin = &isAdmin
|
newUser.IsAdmin = &isAdmin
|
||||||
newUser.GroupId = 1
|
newUser.GroupId = 1
|
||||||
if err := global.DB.Create(newUser).Error; err != nil {
|
if err := DB.Create(newUser).Error; err != nil {
|
||||||
return nil, errors.Join(ErrLdapCreateUserFailed, err)
|
return nil, errors.Join(ErrLdapCreateUserFailed, err)
|
||||||
}
|
}
|
||||||
return userService.InfoByUsername(lu.Username), nil
|
return userService.InfoByUsername(lu.Username), nil
|
||||||
@@ -164,7 +190,7 @@ func (ls *LdapService) mapToLocalUser(cfg *config.Ldap, lu *LdapUser) (*model.Us
|
|||||||
// IsUsernameExists checks if a username exists in LDAP (can be useful for local registration checks).
|
// IsUsernameExists checks if a username exists in LDAP (can be useful for local registration checks).
|
||||||
func (ls *LdapService) IsUsernameExists(username string) bool {
|
func (ls *LdapService) IsUsernameExists(username string) bool {
|
||||||
|
|
||||||
cfg := &global.Config.Ldap
|
cfg := &Config.Ldap
|
||||||
if !cfg.Enable {
|
if !cfg.Enable {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -177,7 +203,7 @@ func (ls *LdapService) IsUsernameExists(username string) bool {
|
|||||||
|
|
||||||
// IsEmailExists checks if an email exists in LDAP (can be useful for local registration checks).
|
// IsEmailExists checks if an email exists in LDAP (can be useful for local registration checks).
|
||||||
func (ls *LdapService) IsEmailExists(email string) bool {
|
func (ls *LdapService) IsEmailExists(email string) bool {
|
||||||
cfg := &global.Config.Ldap
|
cfg := &Config.Ldap
|
||||||
if !cfg.Enable {
|
if !cfg.Enable {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -190,7 +216,7 @@ func (ls *LdapService) IsEmailExists(email string) bool {
|
|||||||
|
|
||||||
// GetUserInfoByUsernameLdap returns the user info from LDAP for the given username.
|
// GetUserInfoByUsernameLdap returns the user info from LDAP for the given username.
|
||||||
func (ls *LdapService) GetUserInfoByUsernameLdap(username string) (*LdapUser, error) {
|
func (ls *LdapService) GetUserInfoByUsernameLdap(username string) (*LdapUser, error) {
|
||||||
cfg := &global.Config.Ldap
|
cfg := &Config.Ldap
|
||||||
if !cfg.Enable {
|
if !cfg.Enable {
|
||||||
return nil, ErrLdapNotEnabled
|
return nil, ErrLdapNotEnabled
|
||||||
}
|
}
|
||||||
@@ -210,12 +236,12 @@ func (ls *LdapService) GetUserInfoByUsernameLocal(username string) (*model.User,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return &model.User{}, err
|
return &model.User{}, err
|
||||||
}
|
}
|
||||||
return ls.mapToLocalUser(&global.Config.Ldap, ldapUser)
|
return ls.mapToLocalUser(&Config.Ldap, ldapUser)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUserInfoByEmailLdap returns the user info from LDAP for the given email.
|
// GetUserInfoByEmailLdap returns the user info from LDAP for the given email.
|
||||||
func (ls *LdapService) GetUserInfoByEmailLdap(email string) (*LdapUser, error) {
|
func (ls *LdapService) GetUserInfoByEmailLdap(email string) (*LdapUser, error) {
|
||||||
cfg := &global.Config.Ldap
|
cfg := &Config.Ldap
|
||||||
if !cfg.Enable {
|
if !cfg.Enable {
|
||||||
return nil, ErrLdapNotEnabled
|
return nil, ErrLdapNotEnabled
|
||||||
}
|
}
|
||||||
@@ -235,7 +261,7 @@ func (ls *LdapService) GetUserInfoByEmailLocal(email string) (*model.User, error
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return &model.User{}, err
|
return &model.User{}, err
|
||||||
}
|
}
|
||||||
return ls.mapToLocalUser(&global.Config.Ldap, ldapUser)
|
return ls.mapToLocalUser(&Config.Ldap, ldapUser)
|
||||||
}
|
}
|
||||||
|
|
||||||
// usernameSearchResult returns the search result for the given username.
|
// usernameSearchResult returns the search result for the given username.
|
||||||
@@ -453,12 +479,12 @@ func (ls *LdapService) isUserEnabled(cfg *config.Ldap, ldapUser *LdapUser) bool
|
|||||||
|
|
||||||
// Account is disabled if the ACCOUNTDISABLE flag (0x2) is set
|
// Account is disabled if the ACCOUNTDISABLE flag (0x2) is set
|
||||||
const ACCOUNTDISABLE = 0x2
|
const ACCOUNTDISABLE = 0x2
|
||||||
ldapUser.Enabled = (userAccountControl&ACCOUNTDISABLE == 0)
|
ldapUser.Enabled = userAccountControl&ACCOUNTDISABLE == 0
|
||||||
return ldapUser.Enabled
|
return ldapUser.Enabled
|
||||||
}
|
}
|
||||||
|
|
||||||
// For other attributes, perform a direct comparison with the expected value
|
// For other attributes, perform a direct comparison with the expected value
|
||||||
ldapUser.Enabled = (ldapUser.EnableAttrValue == enableAttrValue)
|
ldapUser.Enabled = ldapUser.EnableAttrValue == enableAttrValue
|
||||||
return ldapUser.Enabled
|
return ldapUser.Enabled
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/lejianwen/rustdesk-api/v2/global"
|
|
||||||
"github.com/lejianwen/rustdesk-api/v2/model"
|
"github.com/lejianwen/rustdesk-api/v2/model"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
@@ -12,7 +11,7 @@ type LoginLogService struct {
|
|||||||
// InfoById 根据用户id取用户信息
|
// InfoById 根据用户id取用户信息
|
||||||
func (us *LoginLogService) InfoById(id uint) *model.LoginLog {
|
func (us *LoginLogService) InfoById(id uint) *model.LoginLog {
|
||||||
u := &model.LoginLog{}
|
u := &model.LoginLog{}
|
||||||
global.DB.Where("id = ?", id).First(u)
|
DB.Where("id = ?", id).First(u)
|
||||||
return u
|
return u
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -20,7 +19,7 @@ func (us *LoginLogService) List(page, pageSize uint, where func(tx *gorm.DB)) (r
|
|||||||
res = &model.LoginLogList{}
|
res = &model.LoginLogList{}
|
||||||
res.Page = int64(page)
|
res.Page = int64(page)
|
||||||
res.PageSize = int64(pageSize)
|
res.PageSize = int64(pageSize)
|
||||||
tx := global.DB.Model(&model.LoginLog{})
|
tx := DB.Model(&model.LoginLog{})
|
||||||
if where != nil {
|
if where != nil {
|
||||||
where(tx)
|
where(tx)
|
||||||
}
|
}
|
||||||
@@ -32,20 +31,20 @@ func (us *LoginLogService) List(page, pageSize uint, where func(tx *gorm.DB)) (r
|
|||||||
|
|
||||||
// Create 创建
|
// Create 创建
|
||||||
func (us *LoginLogService) Create(u *model.LoginLog) error {
|
func (us *LoginLogService) Create(u *model.LoginLog) error {
|
||||||
res := global.DB.Create(u).Error
|
res := DB.Create(u).Error
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
func (us *LoginLogService) Delete(u *model.LoginLog) error {
|
func (us *LoginLogService) Delete(u *model.LoginLog) error {
|
||||||
return global.DB.Delete(u).Error
|
return DB.Delete(u).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update 更新
|
// Update 更新
|
||||||
func (us *LoginLogService) Update(u *model.LoginLog) error {
|
func (us *LoginLogService) Update(u *model.LoginLog) error {
|
||||||
return global.DB.Model(u).Updates(u).Error
|
return DB.Model(u).Updates(u).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (us *LoginLogService) BatchDelete(ids []uint) error {
|
func (us *LoginLogService) BatchDelete(ids []uint) error {
|
||||||
return global.DB.Where("id in (?)", ids).Delete(&model.LoginLog{}).Error
|
return DB.Where("id in (?)", ids).Delete(&model.LoginLog{}).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (us *LoginLogService) SoftDelete(l *model.LoginLog) error {
|
func (us *LoginLogService) SoftDelete(l *model.LoginLog) error {
|
||||||
@@ -54,5 +53,5 @@ func (us *LoginLogService) SoftDelete(l *model.LoginLog) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (us *LoginLogService) BatchSoftDelete(uid uint, ids []uint) error {
|
func (us *LoginLogService) BatchSoftDelete(uid uint, ids []uint) error {
|
||||||
return global.DB.Model(&model.LoginLog{}).Where("user_id = ? and id in (?)", uid, ids).Update("is_deleted", model.IsDeletedYes).Error
|
return DB.Model(&model.LoginLog{}).Where("user_id = ? and id in (?)", uid, ids).Update("is_deleted", model.IsDeletedYes).Error
|
||||||
}
|
}
|
||||||
|
|||||||
248
service/oauth.go
248
service/oauth.go
@@ -4,7 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"github.com/lejianwen/rustdesk-api/v2/global"
|
"github.com/coreos/go-oidc/v3/oidc"
|
||||||
"github.com/lejianwen/rustdesk-api/v2/model"
|
"github.com/lejianwen/rustdesk-api/v2/model"
|
||||||
"github.com/lejianwen/rustdesk-api/v2/utils"
|
"github.com/lejianwen/rustdesk-api/v2/utils"
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
@@ -45,6 +45,8 @@ type OauthCacheItem struct {
|
|||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
|
Verifier string `json:"verifier"` // used for oauth pkce
|
||||||
|
Nonce string `json:"nonce"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (oci *OauthCacheItem) ToOauthUser() *model.OauthUser {
|
func (oci *OauthCacheItem) ToOauthUser() *model.OauthUser {
|
||||||
@@ -81,10 +83,9 @@ func (os *OauthService) GetOauthCache(key string) *OauthCacheItem {
|
|||||||
func (os *OauthService) SetOauthCache(key string, item *OauthCacheItem, expire uint) {
|
func (os *OauthService) SetOauthCache(key string, item *OauthCacheItem, expire uint) {
|
||||||
OauthCache.Store(key, item)
|
OauthCache.Store(key, item)
|
||||||
if expire > 0 {
|
if expire > 0 {
|
||||||
go func() {
|
time.AfterFunc(time.Duration(expire)*time.Second, func() {
|
||||||
time.Sleep(time.Duration(expire) * time.Second)
|
|
||||||
os.DeleteOauthCache(key)
|
os.DeleteOauthCache(key)
|
||||||
}()
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,151 +93,192 @@ func (os *OauthService) DeleteOauthCache(key string) {
|
|||||||
OauthCache.Delete(key)
|
OauthCache.Delete(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (os *OauthService) BeginAuth(op string) (error error, code, url string) {
|
func (os *OauthService) BeginAuth(op string) (error error, state, verifier, nonce, url string) {
|
||||||
code = utils.RandomString(10) + strconv.FormatInt(time.Now().Unix(), 10)
|
state = utils.RandomString(10) + strconv.FormatInt(time.Now().Unix(), 10)
|
||||||
if op == string(model.OauthTypeWebauth) {
|
verifier = ""
|
||||||
url = global.Config.Rustdesk.ApiServer + "/_admin/#/oauth/" + code
|
nonce = ""
|
||||||
|
if op == model.OauthTypeWebauth {
|
||||||
|
url = Config.Rustdesk.ApiServer + "/_admin/#/oauth/" + state
|
||||||
//url = "http://localhost:8888/_admin/#/oauth/" + code
|
//url = "http://localhost:8888/_admin/#/oauth/" + code
|
||||||
return nil, code, url
|
return nil, state, verifier, nonce, url
|
||||||
}
|
}
|
||||||
err, _, oauthConfig := os.GetOauthConfig(op)
|
err, oauthInfo, oauthConfig, _ := os.GetOauthConfig(op)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return err, code, oauthConfig.AuthCodeURL(code)
|
extras := make([]oauth2.AuthCodeOption, 0, 3)
|
||||||
|
|
||||||
|
nonce = utils.RandomString(10)
|
||||||
|
extras = append(extras, oauth2.SetAuthURLParam("nonce", nonce))
|
||||||
|
|
||||||
|
if oauthInfo.PkceEnable != nil && *oauthInfo.PkceEnable {
|
||||||
|
extras = append(extras, oauth2.AccessTypeOffline)
|
||||||
|
verifier = oauth2.GenerateVerifier()
|
||||||
|
switch oauthInfo.PkceMethod {
|
||||||
|
case model.PKCEMethodS256:
|
||||||
|
extras = append(extras, oauth2.S256ChallengeOption(verifier))
|
||||||
|
case model.PKCEMethodPlain:
|
||||||
|
// oauth2 does not have a plain challenge option, so we add it manually
|
||||||
|
extras = append(extras, oauth2.SetAuthURLParam("code_challenge_method", "plain"), oauth2.SetAuthURLParam("code_challenge", verifier))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return err, state, verifier, nonce, oauthConfig.AuthCodeURL(state, extras...)
|
||||||
}
|
}
|
||||||
|
|
||||||
return err, code, ""
|
return err, state, verifier, nonce, ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// Method to fetch OIDC configuration dynamically
|
func (os *OauthService) FetchOidcProvider(issuer string) (error, *oidc.Provider) {
|
||||||
func (os *OauthService) FetchOidcEndpoint(issuer string) (error, OidcEndpoint) {
|
|
||||||
configURL := strings.TrimSuffix(issuer, "/") + "/.well-known/openid-configuration"
|
|
||||||
|
|
||||||
// Get the HTTP client (with or without proxy based on configuration)
|
// Get the HTTP client (with or without proxy based on configuration)
|
||||||
client := getHTTPClientWithProxy()
|
client := getHTTPClientWithProxy()
|
||||||
|
|
||||||
resp, err := client.Get(configURL)
|
ctx := oidc.ClientContext(context.Background(), client)
|
||||||
|
|
||||||
|
provider, err := oidc.NewProvider(ctx, issuer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("failed to fetch OIDC configuration"), OidcEndpoint{}
|
return err, nil
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
|
||||||
return errors.New("OIDC configuration not found, status code: %d"), OidcEndpoint{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var endpoint OidcEndpoint
|
return nil, provider
|
||||||
if err := json.NewDecoder(resp.Body).Decode(&endpoint); err != nil {
|
|
||||||
return errors.New("failed to parse OIDC configuration"), OidcEndpoint{}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, endpoint
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (os *OauthService) FetchOidcEndpointByOp(op string) (error, OidcEndpoint) {
|
func (os *OauthService) GithubProvider() *oidc.Provider {
|
||||||
oauthInfo := os.InfoByOp(op)
|
return (&oidc.ProviderConfig{
|
||||||
if oauthInfo.Issuer == "" {
|
IssuerURL: "",
|
||||||
return errors.New("issuer is empty"), OidcEndpoint{}
|
AuthURL: github.Endpoint.AuthURL,
|
||||||
}
|
TokenURL: github.Endpoint.TokenURL,
|
||||||
return os.FetchOidcEndpoint(oauthInfo.Issuer)
|
DeviceAuthURL: github.Endpoint.DeviceAuthURL,
|
||||||
|
UserInfoURL: model.UserEndpointGithub,
|
||||||
|
JWKSURL: "",
|
||||||
|
Algorithms: nil,
|
||||||
|
}).NewProvider(context.Background())
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetOauthConfig retrieves the OAuth2 configuration based on the provider name
|
// GetOauthConfig retrieves the OAuth2 configuration based on the provider name
|
||||||
func (os *OauthService) GetOauthConfig(op string) (err error, oauthInfo *model.Oauth, oauthConfig *oauth2.Config) {
|
func (os *OauthService) GetOauthConfig(op string) (err error, oauthInfo *model.Oauth, oauthConfig *oauth2.Config, provider *oidc.Provider) {
|
||||||
err, oauthInfo, oauthConfig = os.getOauthConfigGeneral(op)
|
//err, oauthInfo, oauthConfig = os.getOauthConfigGeneral(op)
|
||||||
if err != nil {
|
oauthInfo = os.InfoByOp(op)
|
||||||
return err, nil, nil
|
if oauthInfo.Id == 0 || oauthInfo.ClientId == "" || oauthInfo.ClientSecret == "" {
|
||||||
|
return errors.New("ConfigNotFound"), nil, nil, nil
|
||||||
}
|
}
|
||||||
|
// If the redirect URL is empty, use the default redirect URL
|
||||||
|
if oauthInfo.RedirectUrl == "" {
|
||||||
|
oauthInfo.RedirectUrl = Config.Rustdesk.ApiServer + "/api/oidc/callback"
|
||||||
|
}
|
||||||
|
oauthConfig = &oauth2.Config{
|
||||||
|
ClientID: oauthInfo.ClientId,
|
||||||
|
ClientSecret: oauthInfo.ClientSecret,
|
||||||
|
RedirectURL: oauthInfo.RedirectUrl,
|
||||||
|
}
|
||||||
|
|
||||||
// Maybe should validate the oauthConfig here
|
// Maybe should validate the oauthConfig here
|
||||||
oauthType := oauthInfo.OauthType
|
oauthType := oauthInfo.OauthType
|
||||||
err = model.ValidateOauthType(oauthType)
|
err = model.ValidateOauthType(oauthType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err, nil, nil
|
return err, nil, nil, nil
|
||||||
}
|
}
|
||||||
switch oauthType {
|
switch oauthType {
|
||||||
case model.OauthTypeGithub:
|
case model.OauthTypeGithub:
|
||||||
oauthConfig.Endpoint = github.Endpoint
|
oauthConfig.Endpoint = github.Endpoint
|
||||||
oauthConfig.Scopes = []string{"read:user", "user:email"}
|
oauthConfig.Scopes = []string{"read:user", "user:email"}
|
||||||
|
provider = os.GithubProvider()
|
||||||
|
//case model.OauthTypeGoogle: //google单独出来,可以少一次FetchOidcEndpoint请求
|
||||||
|
// oauthConfig.Endpoint = google.Endpoint
|
||||||
|
// oauthConfig.Scopes = os.constructScopes(oauthInfo.Scopes)
|
||||||
case model.OauthTypeOidc, model.OauthTypeGoogle:
|
case model.OauthTypeOidc, model.OauthTypeGoogle:
|
||||||
var endpoint OidcEndpoint
|
err, provider = os.FetchOidcProvider(oauthInfo.Issuer)
|
||||||
err, endpoint = os.FetchOidcEndpoint(oauthInfo.Issuer)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err, nil, nil
|
return err, nil, nil, nil
|
||||||
}
|
}
|
||||||
oauthConfig.Endpoint = oauth2.Endpoint{AuthURL: endpoint.AuthURL, TokenURL: endpoint.TokenURL}
|
oauthConfig.Endpoint = provider.Endpoint()
|
||||||
oauthConfig.Scopes = os.constructScopes(oauthInfo.Scopes)
|
oauthConfig.Scopes = os.constructScopes(oauthInfo.Scopes)
|
||||||
default:
|
default:
|
||||||
return errors.New("unsupported OAuth type"), nil, nil
|
return errors.New("unsupported OAuth type"), nil, nil, nil
|
||||||
}
|
|
||||||
return nil, oauthInfo, oauthConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetOauthConfig retrieves the OAuth2 configuration based on the provider name
|
|
||||||
func (os *OauthService) getOauthConfigGeneral(op string) (err error, oauthInfo *model.Oauth, oauthConfig *oauth2.Config) {
|
|
||||||
oauthInfo = os.InfoByOp(op)
|
|
||||||
if oauthInfo.Id == 0 || oauthInfo.ClientId == "" || oauthInfo.ClientSecret == "" {
|
|
||||||
return errors.New("ConfigNotFound"), nil, nil
|
|
||||||
}
|
|
||||||
// If the redirect URL is empty, use the default redirect URL
|
|
||||||
if oauthInfo.RedirectUrl == "" {
|
|
||||||
oauthInfo.RedirectUrl = global.Config.Rustdesk.ApiServer + "/api/oidc/callback"
|
|
||||||
}
|
|
||||||
return nil, oauthInfo, &oauth2.Config{
|
|
||||||
ClientID: oauthInfo.ClientId,
|
|
||||||
ClientSecret: oauthInfo.ClientSecret,
|
|
||||||
RedirectURL: oauthInfo.RedirectUrl,
|
|
||||||
}
|
}
|
||||||
|
return nil, oauthInfo, oauthConfig, provider
|
||||||
}
|
}
|
||||||
|
|
||||||
func getHTTPClientWithProxy() *http.Client {
|
func getHTTPClientWithProxy() *http.Client {
|
||||||
//todo add timeout
|
//add timeout 30s
|
||||||
if global.Config.Proxy.Enable {
|
timeout := time.Duration(60) * time.Second
|
||||||
if global.Config.Proxy.Host == "" {
|
if Config.Proxy.Enable {
|
||||||
global.Logger.Warn("Proxy is enabled but proxy host is empty.")
|
if Config.Proxy.Host == "" {
|
||||||
|
Logger.Warn("Proxy is enabled but proxy host is empty.")
|
||||||
return http.DefaultClient
|
return http.DefaultClient
|
||||||
}
|
}
|
||||||
proxyURL, err := url.Parse(global.Config.Proxy.Host)
|
proxyURL, err := url.Parse(Config.Proxy.Host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
global.Logger.Warn("Invalid proxy URL: ", err)
|
Logger.Warn("Invalid proxy URL: ", err)
|
||||||
return http.DefaultClient
|
return http.DefaultClient
|
||||||
}
|
}
|
||||||
transport := &http.Transport{
|
transport := &http.Transport{
|
||||||
Proxy: http.ProxyURL(proxyURL),
|
Proxy: http.ProxyURL(proxyURL),
|
||||||
}
|
}
|
||||||
return &http.Client{Transport: transport}
|
return &http.Client{Transport: transport, Timeout: timeout}
|
||||||
}
|
}
|
||||||
return http.DefaultClient
|
return http.DefaultClient
|
||||||
}
|
}
|
||||||
|
func (os *OauthService) callbackBase(oauthConfig *oauth2.Config, provider *oidc.Provider, code string, verifier string, nonce string, userData interface{}) (err error, client *http.Client) {
|
||||||
func (os *OauthService) callbackBase(oauthConfig *oauth2.Config, code string, userEndpoint string, userData interface{}) (err error, client *http.Client) {
|
|
||||||
|
|
||||||
// 设置代理客户端
|
// 设置代理客户端
|
||||||
httpClient := getHTTPClientWithProxy()
|
httpClient := getHTTPClientWithProxy()
|
||||||
ctx := context.WithValue(context.Background(), oauth2.HTTPClient, httpClient)
|
ctx := context.WithValue(context.Background(), oauth2.HTTPClient, httpClient)
|
||||||
|
|
||||||
// 使用 code 换取 token
|
exchangeOpts := make([]oauth2.AuthCodeOption, 0, 1)
|
||||||
var token *oauth2.Token
|
if verifier != "" {
|
||||||
token, err = oauthConfig.Exchange(ctx, code)
|
exchangeOpts = append(exchangeOpts, oauth2.VerifierOption(verifier))
|
||||||
|
}
|
||||||
|
|
||||||
|
token, err := oauthConfig.Exchange(ctx, code, exchangeOpts...)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
global.Logger.Warn("oauthConfig.Exchange() failed: ", err)
|
Logger.Warn("oauthConfig.Exchange() failed: ", err)
|
||||||
return errors.New("GetOauthTokenError"), nil
|
return errors.New("GetOauthTokenError"), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取 ID Token, github没有id_token
|
||||||
|
rawIDToken, ok := token.Extra("id_token").(string)
|
||||||
|
if ok && rawIDToken != "" {
|
||||||
|
// 验证 ID Token
|
||||||
|
v := provider.Verifier(&oidc.Config{ClientID: oauthConfig.ClientID})
|
||||||
|
idToken, err2 := v.Verify(ctx, rawIDToken)
|
||||||
|
if err2 != nil {
|
||||||
|
Logger.Warn("IdTokenVerifyError: ", err2)
|
||||||
|
return errors.New("IdTokenVerifyError"), nil
|
||||||
|
}
|
||||||
|
if nonce != "" {
|
||||||
|
// 验证 nonce
|
||||||
|
var claims struct {
|
||||||
|
Nonce string `json:"nonce"`
|
||||||
|
}
|
||||||
|
if err2 = idToken.Claims(&claims); err2 != nil {
|
||||||
|
Logger.Warn("Failed to parse ID Token claims: ", err)
|
||||||
|
return errors.New("IDTokenClaimsError"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if claims.Nonce != nonce {
|
||||||
|
Logger.Warn("Nonce does not match")
|
||||||
|
return errors.New("NonceDoesNotMatch"), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 获取用户信息
|
// 获取用户信息
|
||||||
client = oauthConfig.Client(ctx, token)
|
client = oauthConfig.Client(ctx, token)
|
||||||
resp, err := client.Get(userEndpoint)
|
resp, err := client.Get(provider.UserInfoEndpoint())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
global.Logger.Warn("failed getting user info: ", err)
|
Logger.Warn("failed getting user info: ", err)
|
||||||
return errors.New("GetOauthUserInfoError"), nil
|
return errors.New("GetOauthUserInfoError"), nil
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if closeErr := resp.Body.Close(); closeErr != nil {
|
if closeErr := resp.Body.Close(); closeErr != nil {
|
||||||
global.Logger.Warn("failed closing response body: ", closeErr)
|
Logger.Warn("failed closing response body: ", closeErr)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// 解析用户信息
|
// 解析用户信息
|
||||||
if err = json.NewDecoder(resp.Body).Decode(userData); err != nil {
|
if err = json.NewDecoder(resp.Body).Decode(userData); err != nil {
|
||||||
global.Logger.Warn("failed decoding user info: ", err)
|
Logger.Warn("failed decoding user info: ", err)
|
||||||
return errors.New("DecodeOauthUserInfoError"), nil
|
return errors.New("DecodeOauthUserInfoError"), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -244,9 +286,9 @@ func (os *OauthService) callbackBase(oauthConfig *oauth2.Config, code string, us
|
|||||||
}
|
}
|
||||||
|
|
||||||
// githubCallback github回调
|
// githubCallback github回调
|
||||||
func (os *OauthService) githubCallback(oauthConfig *oauth2.Config, code string) (error, *model.OauthUser) {
|
func (os *OauthService) githubCallback(oauthConfig *oauth2.Config, provider *oidc.Provider, code, verifier, nonce string) (error, *model.OauthUser) {
|
||||||
var user = &model.GithubUser{}
|
var user = &model.GithubUser{}
|
||||||
err, client := os.callbackBase(oauthConfig, code, model.UserEndpointGithub, user)
|
err, client := os.callbackBase(oauthConfig, provider, code, verifier, nonce, user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err, nil
|
return err, nil
|
||||||
}
|
}
|
||||||
@@ -258,19 +300,17 @@ func (os *OauthService) githubCallback(oauthConfig *oauth2.Config, code string)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// oidcCallback oidc回调, 通过code获取用户信息
|
// oidcCallback oidc回调, 通过code获取用户信息
|
||||||
func (os *OauthService) oidcCallback(oauthConfig *oauth2.Config, code string, userInfoEndpoint string) (error, *model.OauthUser) {
|
func (os *OauthService) oidcCallback(oauthConfig *oauth2.Config, provider *oidc.Provider, code, verifier, nonce string) (error, *model.OauthUser) {
|
||||||
var user = &model.OidcUser{}
|
var user = &model.OidcUser{}
|
||||||
if err, _ := os.callbackBase(oauthConfig, code, userInfoEndpoint, user); err != nil {
|
if err, _ := os.callbackBase(oauthConfig, provider, code, verifier, nonce, user); err != nil {
|
||||||
return err, nil
|
return err, nil
|
||||||
}
|
}
|
||||||
return nil, user.ToOauthUser()
|
return nil, user.ToOauthUser()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Callback: Get user information by code and op(Oauth provider)
|
// Callback: Get user information by code and op(Oauth provider)
|
||||||
func (os *OauthService) Callback(code string, op string) (err error, oauthUser *model.OauthUser) {
|
func (os *OauthService) Callback(code, verifier, op, nonce string) (err error, oauthUser *model.OauthUser) {
|
||||||
var oauthInfo *model.Oauth
|
err, oauthInfo, oauthConfig, provider := os.GetOauthConfig(op)
|
||||||
var oauthConfig *oauth2.Config
|
|
||||||
err, oauthInfo, oauthConfig = os.GetOauthConfig(op)
|
|
||||||
// oauthType is already validated in GetOauthConfig
|
// oauthType is already validated in GetOauthConfig
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err, nil
|
return err, nil
|
||||||
@@ -278,13 +318,9 @@ func (os *OauthService) Callback(code string, op string) (err error, oauthUser *
|
|||||||
oauthType := oauthInfo.OauthType
|
oauthType := oauthInfo.OauthType
|
||||||
switch oauthType {
|
switch oauthType {
|
||||||
case model.OauthTypeGithub:
|
case model.OauthTypeGithub:
|
||||||
err, oauthUser = os.githubCallback(oauthConfig, code)
|
err, oauthUser = os.githubCallback(oauthConfig, provider, code, verifier, nonce)
|
||||||
case model.OauthTypeOidc, model.OauthTypeGoogle:
|
case model.OauthTypeOidc, model.OauthTypeGoogle:
|
||||||
err, endpoint := os.FetchOidcEndpoint(oauthInfo.Issuer)
|
err, oauthUser = os.oidcCallback(oauthConfig, provider, code, verifier, nonce)
|
||||||
if err != nil {
|
|
||||||
return err, nil
|
|
||||||
}
|
|
||||||
err, oauthUser = os.oidcCallback(oauthConfig, code, endpoint.UserInfo)
|
|
||||||
default:
|
default:
|
||||||
return errors.New("unsupported OAuth type"), nil
|
return errors.New("unsupported OAuth type"), nil
|
||||||
}
|
}
|
||||||
@@ -293,7 +329,7 @@ func (os *OauthService) Callback(code string, op string) (err error, oauthUser *
|
|||||||
|
|
||||||
func (os *OauthService) UserThirdInfo(op string, openId string) *model.UserThird {
|
func (os *OauthService) UserThirdInfo(op string, openId string) *model.UserThird {
|
||||||
ut := &model.UserThird{}
|
ut := &model.UserThird{}
|
||||||
global.DB.Where("open_id = ? and op = ?", openId, op).First(ut)
|
DB.Where("open_id = ? and op = ?", openId, op).First(ut)
|
||||||
return ut
|
return ut
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -305,7 +341,7 @@ func (os *OauthService) BindOauthUser(userId uint, oauthUser *model.OauthUser, o
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
utr.FromOauthUser(userId, oauthUser, oauthType, op)
|
utr.FromOauthUser(userId, oauthUser, oauthType, op)
|
||||||
return global.DB.Create(utr).Error
|
return DB.Create(utr).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnBindOauthUser: Unbind third party account
|
// UnBindOauthUser: Unbind third party account
|
||||||
@@ -315,25 +351,25 @@ func (os *OauthService) UnBindOauthUser(userId uint, op string) error {
|
|||||||
|
|
||||||
// UnBindThird: Unbind third party account
|
// UnBindThird: Unbind third party account
|
||||||
func (os *OauthService) UnBindThird(op string, userId uint) error {
|
func (os *OauthService) UnBindThird(op string, userId uint) error {
|
||||||
return global.DB.Where("user_id = ? and op = ?", userId, op).Delete(&model.UserThird{}).Error
|
return DB.Where("user_id = ? and op = ?", userId, op).Delete(&model.UserThird{}).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteUserByUserId: When user is deleted, delete all third party bindings
|
// DeleteUserByUserId: When user is deleted, delete all third party bindings
|
||||||
func (os *OauthService) DeleteUserByUserId(userId uint) error {
|
func (os *OauthService) DeleteUserByUserId(userId uint) error {
|
||||||
return global.DB.Where("user_id = ?", userId).Delete(&model.UserThird{}).Error
|
return DB.Where("user_id = ?", userId).Delete(&model.UserThird{}).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
// InfoById 根据id获取Oauth信息
|
// InfoById 根据id获取Oauth信息
|
||||||
func (os *OauthService) InfoById(id uint) *model.Oauth {
|
func (os *OauthService) InfoById(id uint) *model.Oauth {
|
||||||
oauthInfo := &model.Oauth{}
|
oauthInfo := &model.Oauth{}
|
||||||
global.DB.Where("id = ?", id).First(oauthInfo)
|
DB.Where("id = ?", id).First(oauthInfo)
|
||||||
return oauthInfo
|
return oauthInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
// InfoByOp 根据op获取Oauth信息
|
// InfoByOp 根据op获取Oauth信息
|
||||||
func (os *OauthService) InfoByOp(op string) *model.Oauth {
|
func (os *OauthService) InfoByOp(op string) *model.Oauth {
|
||||||
oauthInfo := &model.Oauth{}
|
oauthInfo := &model.Oauth{}
|
||||||
global.DB.Where("op = ?", op).First(oauthInfo)
|
DB.Where("op = ?", op).First(oauthInfo)
|
||||||
return oauthInfo
|
return oauthInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -356,7 +392,7 @@ func (os *OauthService) List(page, pageSize uint, where func(tx *gorm.DB)) (res
|
|||||||
res = &model.OauthList{}
|
res = &model.OauthList{}
|
||||||
res.Page = int64(page)
|
res.Page = int64(page)
|
||||||
res.PageSize = int64(pageSize)
|
res.PageSize = int64(pageSize)
|
||||||
tx := global.DB.Model(&model.Oauth{})
|
tx := DB.Model(&model.Oauth{})
|
||||||
if where != nil {
|
if where != nil {
|
||||||
where(tx)
|
where(tx)
|
||||||
}
|
}
|
||||||
@@ -369,7 +405,7 @@ func (os *OauthService) List(page, pageSize uint, where func(tx *gorm.DB)) (res
|
|||||||
// GetTypeByOp 根据op获取OauthType
|
// GetTypeByOp 根据op获取OauthType
|
||||||
func (os *OauthService) GetTypeByOp(op string) (error, string) {
|
func (os *OauthService) GetTypeByOp(op string) (error, string) {
|
||||||
oauthInfo := &model.Oauth{}
|
oauthInfo := &model.Oauth{}
|
||||||
if global.DB.Where("op = ?", op).First(oauthInfo).Error != nil {
|
if DB.Where("op = ?", op).First(oauthInfo).Error != nil {
|
||||||
return fmt.Errorf("OAuth provider with op '%s' not found", op), ""
|
return fmt.Errorf("OAuth provider with op '%s' not found", op), ""
|
||||||
}
|
}
|
||||||
return nil, oauthInfo.OauthType
|
return nil, oauthInfo.OauthType
|
||||||
@@ -387,7 +423,7 @@ func (os *OauthService) ValidateOauthProvider(op string) error {
|
|||||||
func (os *OauthService) IsOauthProviderExist(op string) bool {
|
func (os *OauthService) IsOauthProviderExist(op string) bool {
|
||||||
oauthInfo := &model.Oauth{}
|
oauthInfo := &model.Oauth{}
|
||||||
// 使用 Gorm 的 Take 方法查找符合条件的记录
|
// 使用 Gorm 的 Take 方法查找符合条件的记录
|
||||||
if err := global.DB.Where("op = ?", op).Take(oauthInfo).Error; err != nil {
|
if err := DB.Where("op = ?", op).Take(oauthInfo).Error; err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
@@ -399,11 +435,11 @@ func (os *OauthService) Create(oauthInfo *model.Oauth) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
res := global.DB.Create(oauthInfo).Error
|
res := DB.Create(oauthInfo).Error
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
func (os *OauthService) Delete(oauthInfo *model.Oauth) error {
|
func (os *OauthService) Delete(oauthInfo *model.Oauth) error {
|
||||||
return global.DB.Delete(oauthInfo).Error
|
return DB.Delete(oauthInfo).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update 更新
|
// Update 更新
|
||||||
@@ -412,13 +448,13 @@ func (os *OauthService) Update(oauthInfo *model.Oauth) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return global.DB.Model(oauthInfo).Updates(oauthInfo).Error
|
return DB.Model(oauthInfo).Updates(oauthInfo).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetOauthProviders 获取所有的provider
|
// GetOauthProviders 获取所有的provider
|
||||||
func (os *OauthService) GetOauthProviders() []string {
|
func (os *OauthService) GetOauthProviders() []string {
|
||||||
var res []string
|
var res []string
|
||||||
global.DB.Model(&model.Oauth{}).Pluck("op", &res)
|
DB.Model(&model.Oauth{}).Pluck("op", &res)
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/lejianwen/rustdesk-api/v2/global"
|
|
||||||
"github.com/lejianwen/rustdesk-api/v2/model"
|
"github.com/lejianwen/rustdesk-api/v2/model"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
@@ -12,24 +11,24 @@ type PeerService struct {
|
|||||||
// FindById 根据id查找
|
// FindById 根据id查找
|
||||||
func (ps *PeerService) FindById(id string) *model.Peer {
|
func (ps *PeerService) FindById(id string) *model.Peer {
|
||||||
p := &model.Peer{}
|
p := &model.Peer{}
|
||||||
global.DB.Where("id = ?", id).First(p)
|
DB.Where("id = ?", id).First(p)
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
func (ps *PeerService) FindByUuid(uuid string) *model.Peer {
|
func (ps *PeerService) FindByUuid(uuid string) *model.Peer {
|
||||||
p := &model.Peer{}
|
p := &model.Peer{}
|
||||||
global.DB.Where("uuid = ?", uuid).First(p)
|
DB.Where("uuid = ?", uuid).First(p)
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
func (ps *PeerService) InfoByRowId(id uint) *model.Peer {
|
func (ps *PeerService) InfoByRowId(id uint) *model.Peer {
|
||||||
p := &model.Peer{}
|
p := &model.Peer{}
|
||||||
global.DB.Where("row_id = ?", id).First(p)
|
DB.Where("row_id = ?", id).First(p)
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindByUserIdAndUuid 根据用户id和uuid查找peer
|
// FindByUserIdAndUuid 根据用户id和uuid查找peer
|
||||||
func (ps *PeerService) FindByUserIdAndUuid(uuid string, userId uint) *model.Peer {
|
func (ps *PeerService) FindByUserIdAndUuid(uuid string, userId uint) *model.Peer {
|
||||||
p := &model.Peer{}
|
p := &model.Peer{}
|
||||||
global.DB.Where("uuid = ? and user_id = ?", uuid, userId).First(p)
|
DB.Where("uuid = ? and user_id = ?", uuid, userId).First(p)
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,7 +42,7 @@ func (ps *PeerService) UuidBindUserId(deviceId string, uuid string, userId uint)
|
|||||||
} else {
|
} else {
|
||||||
// 不存在则创建
|
// 不存在则创建
|
||||||
/*if deviceId != "" {
|
/*if deviceId != "" {
|
||||||
global.DB.Create(&model.Peer{
|
DB.Create(&model.Peer{
|
||||||
Id: deviceId,
|
Id: deviceId,
|
||||||
Uuid: uuid,
|
Uuid: uuid,
|
||||||
UserId: userId,
|
UserId: userId,
|
||||||
@@ -56,13 +55,13 @@ func (ps *PeerService) UuidBindUserId(deviceId string, uuid string, userId uint)
|
|||||||
func (ps *PeerService) UuidUnbindUserId(uuid string, userId uint) {
|
func (ps *PeerService) UuidUnbindUserId(uuid string, userId uint) {
|
||||||
peer := ps.FindByUserIdAndUuid(uuid, userId)
|
peer := ps.FindByUserIdAndUuid(uuid, userId)
|
||||||
if peer.RowId > 0 {
|
if peer.RowId > 0 {
|
||||||
global.DB.Model(peer).Update("user_id", 0)
|
DB.Model(peer).Update("user_id", 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// EraseUserId 清除用户id, 用于用户删除
|
// EraseUserId 清除用户id, 用于用户删除
|
||||||
func (ps *PeerService) EraseUserId(userId uint) error {
|
func (ps *PeerService) EraseUserId(userId uint) error {
|
||||||
return global.DB.Model(&model.Peer{}).Where("user_id = ?", userId).Update("user_id", 0).Error
|
return DB.Model(&model.Peer{}).Where("user_id = ?", userId).Update("user_id", 0).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListByUserIds 根据用户id取列表
|
// ListByUserIds 根据用户id取列表
|
||||||
@@ -70,7 +69,7 @@ func (ps *PeerService) ListByUserIds(userIds []uint, page, pageSize uint) (res *
|
|||||||
res = &model.PeerList{}
|
res = &model.PeerList{}
|
||||||
res.Page = int64(page)
|
res.Page = int64(page)
|
||||||
res.PageSize = int64(pageSize)
|
res.PageSize = int64(pageSize)
|
||||||
tx := global.DB.Model(&model.Peer{})
|
tx := DB.Model(&model.Peer{})
|
||||||
tx.Where("user_id in (?)", userIds)
|
tx.Where("user_id in (?)", userIds)
|
||||||
tx.Count(&res.Total)
|
tx.Count(&res.Total)
|
||||||
tx.Scopes(Paginate(page, pageSize))
|
tx.Scopes(Paginate(page, pageSize))
|
||||||
@@ -82,7 +81,7 @@ func (ps *PeerService) List(page, pageSize uint, where func(tx *gorm.DB)) (res *
|
|||||||
res = &model.PeerList{}
|
res = &model.PeerList{}
|
||||||
res.Page = int64(page)
|
res.Page = int64(page)
|
||||||
res.PageSize = int64(pageSize)
|
res.PageSize = int64(pageSize)
|
||||||
tx := global.DB.Model(&model.Peer{})
|
tx := DB.Model(&model.Peer{})
|
||||||
if where != nil {
|
if where != nil {
|
||||||
where(tx)
|
where(tx)
|
||||||
}
|
}
|
||||||
@@ -106,14 +105,14 @@ func (ps *PeerService) ListFilterByUserId(page, pageSize uint, where func(tx *go
|
|||||||
|
|
||||||
// Create 创建
|
// Create 创建
|
||||||
func (ps *PeerService) Create(u *model.Peer) error {
|
func (ps *PeerService) Create(u *model.Peer) error {
|
||||||
res := global.DB.Create(u).Error
|
res := DB.Create(u).Error
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete 删除, 同时也应该删除token
|
// Delete 删除, 同时也应该删除token
|
||||||
func (ps *PeerService) Delete(u *model.Peer) error {
|
func (ps *PeerService) Delete(u *model.Peer) error {
|
||||||
uuid := u.Uuid
|
uuid := u.Uuid
|
||||||
err := global.DB.Delete(u).Error
|
err := DB.Delete(u).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -124,16 +123,23 @@ func (ps *PeerService) Delete(u *model.Peer) error {
|
|||||||
// GetUuidListByIDs 根据ids获取uuid列表
|
// GetUuidListByIDs 根据ids获取uuid列表
|
||||||
func (ps *PeerService) GetUuidListByIDs(ids []uint) ([]string, error) {
|
func (ps *PeerService) GetUuidListByIDs(ids []uint) ([]string, error) {
|
||||||
var uuids []string
|
var uuids []string
|
||||||
err := global.DB.Model(&model.Peer{}).
|
err := DB.Model(&model.Peer{}).
|
||||||
Where("row_id in (?)", ids).
|
Where("row_id in (?)", ids).
|
||||||
Pluck("uuid", &uuids).Error
|
Pluck("uuid", &uuids).Error
|
||||||
return uuids, err
|
//过滤uuids中的空字符串
|
||||||
|
var newUuids []string
|
||||||
|
for _, uuid := range uuids {
|
||||||
|
if uuid != "" {
|
||||||
|
newUuids = append(newUuids, uuid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newUuids, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// BatchDelete 批量删除, 同时也应该删除token
|
// BatchDelete 批量删除, 同时也应该删除token
|
||||||
func (ps *PeerService) BatchDelete(ids []uint) error {
|
func (ps *PeerService) BatchDelete(ids []uint) error {
|
||||||
uuids, err := ps.GetUuidListByIDs(ids)
|
uuids, err := ps.GetUuidListByIDs(ids)
|
||||||
err = global.DB.Where("row_id in (?)", ids).Delete(&model.Peer{}).Error
|
err = DB.Where("row_id in (?)", ids).Delete(&model.Peer{}).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -143,5 +149,5 @@ func (ps *PeerService) BatchDelete(ids []uint) error {
|
|||||||
|
|
||||||
// Update 更新
|
// Update 更新
|
||||||
func (ps *PeerService) Update(u *model.Peer) error {
|
func (ps *PeerService) Update(u *model.Peer) error {
|
||||||
return global.DB.Model(u).Updates(u).Error
|
return DB.Model(u).Updates(u).Error
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package service
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/lejianwen/rustdesk-api/v2/global"
|
|
||||||
"github.com/lejianwen/rustdesk-api/v2/model"
|
"github.com/lejianwen/rustdesk-api/v2/model"
|
||||||
"net"
|
"net"
|
||||||
"time"
|
"time"
|
||||||
@@ -15,7 +14,7 @@ func (is *ServerCmdService) List(page, pageSize uint) (res *model.ServerCmdList)
|
|||||||
res = &model.ServerCmdList{}
|
res = &model.ServerCmdList{}
|
||||||
res.Page = int64(page)
|
res.Page = int64(page)
|
||||||
res.PageSize = int64(pageSize)
|
res.PageSize = int64(pageSize)
|
||||||
tx := global.DB.Model(&model.ServerCmd{})
|
tx := DB.Model(&model.ServerCmd{})
|
||||||
tx.Count(&res.Total)
|
tx.Count(&res.Total)
|
||||||
tx.Scopes(Paginate(page, pageSize))
|
tx.Scopes(Paginate(page, pageSize))
|
||||||
tx.Find(&res.ServerCmds)
|
tx.Find(&res.ServerCmds)
|
||||||
@@ -25,18 +24,18 @@ func (is *ServerCmdService) List(page, pageSize uint) (res *model.ServerCmdList)
|
|||||||
// Info
|
// Info
|
||||||
func (is *ServerCmdService) Info(id uint) *model.ServerCmd {
|
func (is *ServerCmdService) Info(id uint) *model.ServerCmd {
|
||||||
u := &model.ServerCmd{}
|
u := &model.ServerCmd{}
|
||||||
global.DB.Where("id = ?", id).First(u)
|
DB.Where("id = ?", id).First(u)
|
||||||
return u
|
return u
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete
|
// Delete
|
||||||
func (is *ServerCmdService) Delete(u *model.ServerCmd) error {
|
func (is *ServerCmdService) Delete(u *model.ServerCmd) error {
|
||||||
return global.DB.Delete(u).Error
|
return DB.Delete(u).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create
|
// Create
|
||||||
func (is *ServerCmdService) Create(u *model.ServerCmd) error {
|
func (is *ServerCmdService) Create(u *model.ServerCmd) error {
|
||||||
res := global.DB.Create(u).Error
|
res := DB.Create(u).Error
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,9 +44,9 @@ func (is *ServerCmdService) SendCmd(target string, cmd string, arg string) (stri
|
|||||||
port := 0
|
port := 0
|
||||||
switch target {
|
switch target {
|
||||||
case model.ServerCmdTargetIdServer:
|
case model.ServerCmdTargetIdServer:
|
||||||
port = global.Config.Rustdesk.IdServerPort - 1
|
port = Config.Rustdesk.IdServerPort - 1
|
||||||
case model.ServerCmdTargetRelayServer:
|
case model.ServerCmdTargetRelayServer:
|
||||||
port = global.Config.Rustdesk.RelayServerPort
|
port = Config.Rustdesk.RelayServerPort
|
||||||
}
|
}
|
||||||
//组装命令
|
//组装命令
|
||||||
cmd = cmd + " " + arg
|
cmd = cmd + " " + arg
|
||||||
@@ -73,14 +72,14 @@ func (is *ServerCmdService) SendSocketCmd(ty string, port int, cmd string) (stri
|
|||||||
}
|
}
|
||||||
conn, err := net.Dial(tcp, fmt.Sprintf("%s:%v", addr, port))
|
conn, err := net.Dial(tcp, fmt.Sprintf("%s:%v", addr, port))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
global.Logger.Debugf("%s connect to id server failed: %v", ty, err)
|
Logger.Debugf("%s connect to id server failed: %v", ty, err)
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
//发送命令
|
//发送命令
|
||||||
_, err = conn.Write([]byte(cmd))
|
_, err = conn.Write([]byte(cmd))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
global.Logger.Debugf("%s send cmd failed: %v", ty, err)
|
Logger.Debugf("%s send cmd failed: %v", ty, err)
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(100 * time.Millisecond)
|
||||||
@@ -88,12 +87,12 @@ func (is *ServerCmdService) SendSocketCmd(ty string, port int, cmd string) (stri
|
|||||||
buf := make([]byte, 1024)
|
buf := make([]byte, 1024)
|
||||||
n, err := conn.Read(buf)
|
n, err := conn.Read(buf)
|
||||||
if err != nil && err.Error() != "EOF" {
|
if err != nil && err.Error() != "EOF" {
|
||||||
global.Logger.Debugf("%s read response failed: %v", ty, err)
|
Logger.Debugf("%s read response failed: %v", ty, err)
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
return string(buf[:n]), nil
|
return string(buf[:n]), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (is *ServerCmdService) Update(f *model.ServerCmd) error {
|
func (is *ServerCmdService) Update(f *model.ServerCmd) error {
|
||||||
return global.DB.Model(f).Updates(f).Error
|
return DB.Model(f).Updates(f).Error
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/config"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/lib/jwt"
|
||||||
|
"github.com/lejianwen/rustdesk-api/v2/lib/lock"
|
||||||
"github.com/lejianwen/rustdesk-api/v2/model"
|
"github.com/lejianwen/rustdesk-api/v2/model"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -19,14 +23,34 @@ type Service struct {
|
|||||||
*ShareRecordService
|
*ShareRecordService
|
||||||
*ServerCmdService
|
*ServerCmdService
|
||||||
*LdapService
|
*LdapService
|
||||||
|
*AppService
|
||||||
}
|
}
|
||||||
|
|
||||||
func New() *Service {
|
type Dependencies struct {
|
||||||
all := new(Service)
|
Config *config.Config
|
||||||
return all
|
DB *gorm.DB
|
||||||
|
Logger *log.Logger
|
||||||
|
Jwt *jwt.Jwt
|
||||||
|
Lock *lock.Locker
|
||||||
}
|
}
|
||||||
|
|
||||||
var AllService = New()
|
var Config *config.Config
|
||||||
|
var DB *gorm.DB
|
||||||
|
var Logger *log.Logger
|
||||||
|
var Jwt *jwt.Jwt
|
||||||
|
var Lock lock.Locker
|
||||||
|
|
||||||
|
var AllService *Service
|
||||||
|
|
||||||
|
func New(c *config.Config, g *gorm.DB, l *log.Logger, j *jwt.Jwt, lo lock.Locker) *Service {
|
||||||
|
Config = c
|
||||||
|
DB = g
|
||||||
|
Logger = l
|
||||||
|
Jwt = j
|
||||||
|
Lock = lo
|
||||||
|
AllService = new(Service)
|
||||||
|
return AllService
|
||||||
|
}
|
||||||
|
|
||||||
func Paginate(page, pageSize uint) func(db *gorm.DB) *gorm.DB {
|
func Paginate(page, pageSize uint) func(db *gorm.DB) *gorm.DB {
|
||||||
return func(db *gorm.DB) *gorm.DB {
|
return func(db *gorm.DB) *gorm.DB {
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/lejianwen/rustdesk-api/v2/global"
|
|
||||||
"github.com/lejianwen/rustdesk-api/v2/model"
|
"github.com/lejianwen/rustdesk-api/v2/model"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
@@ -12,7 +11,7 @@ type ShareRecordService struct {
|
|||||||
// InfoById 根据用户id取用户信息
|
// InfoById 根据用户id取用户信息
|
||||||
func (srs *ShareRecordService) InfoById(id uint) *model.ShareRecord {
|
func (srs *ShareRecordService) InfoById(id uint) *model.ShareRecord {
|
||||||
u := &model.ShareRecord{}
|
u := &model.ShareRecord{}
|
||||||
global.DB.Where("id = ?", id).First(u)
|
DB.Where("id = ?", id).First(u)
|
||||||
return u
|
return u
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -20,7 +19,7 @@ func (srs *ShareRecordService) List(page, pageSize uint, where func(tx *gorm.DB)
|
|||||||
res = &model.ShareRecordList{}
|
res = &model.ShareRecordList{}
|
||||||
res.Page = int64(page)
|
res.Page = int64(page)
|
||||||
res.PageSize = int64(pageSize)
|
res.PageSize = int64(pageSize)
|
||||||
tx := global.DB.Model(&model.ShareRecord{})
|
tx := DB.Model(&model.ShareRecord{})
|
||||||
if where != nil {
|
if where != nil {
|
||||||
where(tx)
|
where(tx)
|
||||||
}
|
}
|
||||||
@@ -32,18 +31,18 @@ func (srs *ShareRecordService) List(page, pageSize uint, where func(tx *gorm.DB)
|
|||||||
|
|
||||||
// Create 创建
|
// Create 创建
|
||||||
func (srs *ShareRecordService) Create(u *model.ShareRecord) error {
|
func (srs *ShareRecordService) Create(u *model.ShareRecord) error {
|
||||||
res := global.DB.Create(u).Error
|
res := DB.Create(u).Error
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
func (srs *ShareRecordService) Delete(u *model.ShareRecord) error {
|
func (srs *ShareRecordService) Delete(u *model.ShareRecord) error {
|
||||||
return global.DB.Delete(u).Error
|
return DB.Delete(u).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update 更新
|
// Update 更新
|
||||||
func (srs *ShareRecordService) Update(u *model.ShareRecord) error {
|
func (srs *ShareRecordService) Update(u *model.ShareRecord) error {
|
||||||
return global.DB.Model(u).Updates(u).Error
|
return DB.Model(u).Updates(u).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srs *ShareRecordService) BatchDelete(ids []uint) error {
|
func (srs *ShareRecordService) BatchDelete(ids []uint) error {
|
||||||
return global.DB.Where("id in (?)", ids).Delete(&model.ShareRecord{}).Error
|
return DB.Where("id in (?)", ids).Delete(&model.ShareRecord{}).Error
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/lejianwen/rustdesk-api/v2/global"
|
|
||||||
"github.com/lejianwen/rustdesk-api/v2/model"
|
"github.com/lejianwen/rustdesk-api/v2/model"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
@@ -11,12 +10,12 @@ type TagService struct {
|
|||||||
|
|
||||||
func (s *TagService) Info(id uint) *model.Tag {
|
func (s *TagService) Info(id uint) *model.Tag {
|
||||||
p := &model.Tag{}
|
p := &model.Tag{}
|
||||||
global.DB.Where("id = ?", id).First(p)
|
DB.Where("id = ?", id).First(p)
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
func (s *TagService) InfoByUserIdAndNameAndCollectionId(userid uint, name string, cid uint) *model.Tag {
|
func (s *TagService) InfoByUserIdAndNameAndCollectionId(userid uint, name string, cid uint) *model.Tag {
|
||||||
p := &model.Tag{}
|
p := &model.Tag{}
|
||||||
global.DB.Where("user_id = ? and name = ? and collection_id = ?", userid, name, cid).First(p)
|
DB.Where("user_id = ? and name = ? and collection_id = ?", userid, name, cid).First(p)
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,7 +33,7 @@ func (s *TagService) ListByUserIdAndCollectionId(userId, cid uint) (res *model.T
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
func (s *TagService) UpdateTags(userId uint, tags map[string]uint) {
|
func (s *TagService) UpdateTags(userId uint, tags map[string]uint) {
|
||||||
tx := global.DB.Begin()
|
tx := DB.Begin()
|
||||||
//先查询所有tag
|
//先查询所有tag
|
||||||
var allTags []*model.Tag
|
var allTags []*model.Tag
|
||||||
tx.Where("user_id = ?", userId).Find(&allTags)
|
tx.Where("user_id = ?", userId).Find(&allTags)
|
||||||
@@ -66,7 +65,7 @@ func (s *TagService) UpdateTags(userId uint, tags map[string]uint) {
|
|||||||
// InfoById 根据用户id取用户信息
|
// InfoById 根据用户id取用户信息
|
||||||
func (s *TagService) InfoById(id uint) *model.Tag {
|
func (s *TagService) InfoById(id uint) *model.Tag {
|
||||||
u := &model.Tag{}
|
u := &model.Tag{}
|
||||||
global.DB.Where("id = ?", id).First(u)
|
DB.Where("id = ?", id).First(u)
|
||||||
return u
|
return u
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,7 +73,7 @@ func (s *TagService) List(page, pageSize uint, where func(tx *gorm.DB)) (res *mo
|
|||||||
res = &model.TagList{}
|
res = &model.TagList{}
|
||||||
res.Page = int64(page)
|
res.Page = int64(page)
|
||||||
res.PageSize = int64(pageSize)
|
res.PageSize = int64(pageSize)
|
||||||
tx := global.DB.Model(&model.Tag{})
|
tx := DB.Model(&model.Tag{})
|
||||||
if where != nil {
|
if where != nil {
|
||||||
where(tx)
|
where(tx)
|
||||||
}
|
}
|
||||||
@@ -86,14 +85,14 @@ func (s *TagService) List(page, pageSize uint, where func(tx *gorm.DB)) (res *mo
|
|||||||
|
|
||||||
// Create 创建
|
// Create 创建
|
||||||
func (s *TagService) Create(u *model.Tag) error {
|
func (s *TagService) Create(u *model.Tag) error {
|
||||||
res := global.DB.Create(u).Error
|
res := DB.Create(u).Error
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
func (s *TagService) Delete(u *model.Tag) error {
|
func (s *TagService) Delete(u *model.Tag) error {
|
||||||
return global.DB.Delete(u).Error
|
return DB.Delete(u).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update 更新
|
// Update 更新
|
||||||
func (s *TagService) Update(u *model.Tag) error {
|
func (s *TagService) Update(u *model.Tag) error {
|
||||||
return global.DB.Model(u).Select("*").Omit("created_at").Updates(u).Error
|
return DB.Model(u).Select("*").Omit("created_at").Updates(u).Error
|
||||||
}
|
}
|
||||||
|
|||||||
100
service/user.go
100
service/user.go
@@ -2,7 +2,6 @@ package service
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"github.com/lejianwen/rustdesk-api/v2/global"
|
|
||||||
"github.com/lejianwen/rustdesk-api/v2/model"
|
"github.com/lejianwen/rustdesk-api/v2/model"
|
||||||
"github.com/lejianwen/rustdesk-api/v2/utils"
|
"github.com/lejianwen/rustdesk-api/v2/utils"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
@@ -20,43 +19,43 @@ type UserService struct {
|
|||||||
// InfoById 根据用户id取用户信息
|
// InfoById 根据用户id取用户信息
|
||||||
func (us *UserService) InfoById(id uint) *model.User {
|
func (us *UserService) InfoById(id uint) *model.User {
|
||||||
u := &model.User{}
|
u := &model.User{}
|
||||||
global.DB.Where("id = ?", id).First(u)
|
DB.Where("id = ?", id).First(u)
|
||||||
return u
|
return u
|
||||||
}
|
}
|
||||||
|
|
||||||
// InfoByUsername 根据用户名取用户信息
|
// InfoByUsername 根据用户名取用户信息
|
||||||
func (us *UserService) InfoByUsername(un string) *model.User {
|
func (us *UserService) InfoByUsername(un string) *model.User {
|
||||||
u := &model.User{}
|
u := &model.User{}
|
||||||
global.DB.Where("username = ?", un).First(u)
|
DB.Where("username = ?", un).First(u)
|
||||||
return u
|
return u
|
||||||
}
|
}
|
||||||
|
|
||||||
// InfoByEmail 根据邮箱取用户信息
|
// InfoByEmail 根据邮箱取用户信息
|
||||||
func (us *UserService) InfoByEmail(email string) *model.User {
|
func (us *UserService) InfoByEmail(email string) *model.User {
|
||||||
u := &model.User{}
|
u := &model.User{}
|
||||||
global.DB.Where("email = ?", email).First(u)
|
DB.Where("email = ?", email).First(u)
|
||||||
return u
|
return u
|
||||||
}
|
}
|
||||||
|
|
||||||
// InfoByOpenid 根据openid取用户信息
|
// InfoByOpenid 根据openid取用户信息
|
||||||
func (us *UserService) InfoByOpenid(openid string) *model.User {
|
func (us *UserService) InfoByOpenid(openid string) *model.User {
|
||||||
u := &model.User{}
|
u := &model.User{}
|
||||||
global.DB.Where("openid = ?", openid).First(u)
|
DB.Where("openid = ?", openid).First(u)
|
||||||
return u
|
return u
|
||||||
}
|
}
|
||||||
|
|
||||||
// InfoByUsernamePassword 根据用户名密码取用户信息
|
// InfoByUsernamePassword 根据用户名密码取用户信息
|
||||||
func (us *UserService) InfoByUsernamePassword(username, password string) *model.User {
|
func (us *UserService) InfoByUsernamePassword(username, password string) *model.User {
|
||||||
if global.Config.Ldap.Enable {
|
if Config.Ldap.Enable {
|
||||||
u, err := AllService.LdapService.Authenticate(username, password)
|
u, err := AllService.LdapService.Authenticate(username, password)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return u
|
return u
|
||||||
}
|
}
|
||||||
global.Logger.Errorf("LDAP authentication failed, %v", err)
|
Logger.Errorf("LDAP authentication failed, %v", err)
|
||||||
global.Logger.Warn("Fallback to local database")
|
Logger.Warn("Fallback to local database")
|
||||||
}
|
}
|
||||||
u := &model.User{}
|
u := &model.User{}
|
||||||
global.DB.Where("username = ? and password = ?", username, us.EncryptPassword(password)).First(u)
|
DB.Where("username = ? and password = ?", username, us.EncryptPassword(password)).First(u)
|
||||||
return u
|
return u
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,21 +63,21 @@ func (us *UserService) InfoByUsernamePassword(username, password string) *model.
|
|||||||
func (us *UserService) InfoByAccessToken(token string) (*model.User, *model.UserToken) {
|
func (us *UserService) InfoByAccessToken(token string) (*model.User, *model.UserToken) {
|
||||||
u := &model.User{}
|
u := &model.User{}
|
||||||
ut := &model.UserToken{}
|
ut := &model.UserToken{}
|
||||||
global.DB.Where("token = ?", token).First(ut)
|
DB.Where("token = ?", token).First(ut)
|
||||||
if ut.Id == 0 {
|
if ut.Id == 0 {
|
||||||
return u, ut
|
return u, ut
|
||||||
}
|
}
|
||||||
if ut.ExpiredAt < time.Now().Unix() {
|
if ut.ExpiredAt < time.Now().Unix() {
|
||||||
return u, ut
|
return u, ut
|
||||||
}
|
}
|
||||||
global.DB.Where("id = ?", ut.UserId).First(u)
|
DB.Where("id = ?", ut.UserId).First(u)
|
||||||
return u, ut
|
return u, ut
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateToken 生成token
|
// GenerateToken 生成token
|
||||||
func (us *UserService) GenerateToken(u *model.User) string {
|
func (us *UserService) GenerateToken(u *model.User) string {
|
||||||
if len(global.Jwt.Key) > 0 {
|
if len(Jwt.Key) > 0 {
|
||||||
return global.Jwt.GenerateToken(u.Id)
|
return Jwt.GenerateToken(u.Id)
|
||||||
}
|
}
|
||||||
return utils.Md5(u.Username + time.Now().String())
|
return utils.Md5(u.Username + time.Now().String())
|
||||||
}
|
}
|
||||||
@@ -93,9 +92,9 @@ func (us *UserService) Login(u *model.User, llog *model.LoginLog) *model.UserTok
|
|||||||
DeviceId: llog.DeviceId,
|
DeviceId: llog.DeviceId,
|
||||||
ExpiredAt: us.UserTokenExpireTimestamp(),
|
ExpiredAt: us.UserTokenExpireTimestamp(),
|
||||||
}
|
}
|
||||||
global.DB.Create(ut)
|
DB.Create(ut)
|
||||||
llog.UserTokenId = ut.UserId
|
llog.UserTokenId = ut.UserId
|
||||||
global.DB.Create(llog)
|
DB.Create(llog)
|
||||||
if llog.Uuid != "" {
|
if llog.Uuid != "" {
|
||||||
AllService.PeerService.UuidBindUserId(llog.DeviceId, llog.Uuid, u.Id)
|
AllService.PeerService.UuidBindUserId(llog.DeviceId, llog.Uuid, u.Id)
|
||||||
}
|
}
|
||||||
@@ -116,7 +115,7 @@ func (us *UserService) List(page, pageSize uint, where func(tx *gorm.DB)) (res *
|
|||||||
res = &model.UserList{}
|
res = &model.UserList{}
|
||||||
res.Page = int64(page)
|
res.Page = int64(page)
|
||||||
res.PageSize = int64(pageSize)
|
res.PageSize = int64(pageSize)
|
||||||
tx := global.DB.Model(&model.User{})
|
tx := DB.Model(&model.User{})
|
||||||
if where != nil {
|
if where != nil {
|
||||||
where(tx)
|
where(tx)
|
||||||
}
|
}
|
||||||
@@ -127,7 +126,7 @@ func (us *UserService) List(page, pageSize uint, where func(tx *gorm.DB)) (res *
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (us *UserService) ListByIds(ids []uint) (res []*model.User) {
|
func (us *UserService) ListByIds(ids []uint) (res []*model.User) {
|
||||||
global.DB.Where("id in ?", ids).Find(&res)
|
DB.Where("id in ?", ids).Find(&res)
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,14 +140,14 @@ func (us *UserService) ListByGroupId(groupId, page, pageSize uint) (res *model.U
|
|||||||
|
|
||||||
// ListIdsByGroupId 根据组id取用户id列表
|
// ListIdsByGroupId 根据组id取用户id列表
|
||||||
func (us *UserService) ListIdsByGroupId(groupId uint) (ids []uint) {
|
func (us *UserService) ListIdsByGroupId(groupId uint) (ids []uint) {
|
||||||
global.DB.Model(&model.User{}).Where("group_id = ?", groupId).Pluck("id", &ids)
|
DB.Model(&model.User{}).Where("group_id = ?", groupId).Pluck("id", &ids)
|
||||||
return ids
|
return ids
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListIdAndNameByGroupId 根据组id取用户id和用户名列表
|
// ListIdAndNameByGroupId 根据组id取用户id和用户名列表
|
||||||
func (us *UserService) ListIdAndNameByGroupId(groupId uint) (res []*model.User) {
|
func (us *UserService) ListIdAndNameByGroupId(groupId uint) (res []*model.User) {
|
||||||
global.DB.Model(&model.User{}).Where("group_id = ?", groupId).Select("id, username").Find(&res)
|
DB.Model(&model.User{}).Where("group_id = ?", groupId).Select("id, username").Find(&res)
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,14 +169,14 @@ func (us *UserService) Create(u *model.User) error {
|
|||||||
}
|
}
|
||||||
u.Username = us.formatUsername(u.Username)
|
u.Username = us.formatUsername(u.Username)
|
||||||
u.Password = us.EncryptPassword(u.Password)
|
u.Password = us.EncryptPassword(u.Password)
|
||||||
res := global.DB.Create(u).Error
|
res := DB.Create(u).Error
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUuidByToken 根据token和user取uuid
|
// GetUuidByToken 根据token和user取uuid
|
||||||
func (us *UserService) GetUuidByToken(u *model.User, token string) string {
|
func (us *UserService) GetUuidByToken(u *model.User, token string) string {
|
||||||
ut := &model.UserToken{}
|
ut := &model.UserToken{}
|
||||||
err := global.DB.Where("user_id = ? and token = ?", u.Id, token).First(ut).Error
|
err := DB.Where("user_id = ? and token = ?", u.Id, token).First(ut).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
@@ -187,7 +186,7 @@ func (us *UserService) GetUuidByToken(u *model.User, token string) string {
|
|||||||
// Logout 退出登录 -> 删除token, 解绑uuid
|
// Logout 退出登录 -> 删除token, 解绑uuid
|
||||||
func (us *UserService) Logout(u *model.User, token string) error {
|
func (us *UserService) Logout(u *model.User, token string) error {
|
||||||
uuid := us.GetUuidByToken(u, token)
|
uuid := us.GetUuidByToken(u, token)
|
||||||
err := global.DB.Where("user_id = ? and token = ?", u.Id, token).Delete(&model.UserToken{}).Error
|
err := DB.Where("user_id = ? and token = ?", u.Id, token).Delete(&model.UserToken{}).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -203,7 +202,7 @@ func (us *UserService) Delete(u *model.User) error {
|
|||||||
if userCount <= 1 && us.IsAdmin(u) {
|
if userCount <= 1 && us.IsAdmin(u) {
|
||||||
return errors.New("The last admin user cannot be deleted")
|
return errors.New("The last admin user cannot be deleted")
|
||||||
}
|
}
|
||||||
tx := global.DB.Begin()
|
tx := DB.Begin()
|
||||||
// 删除用户
|
// 删除用户
|
||||||
if err := tx.Delete(u).Error; err != nil {
|
if err := tx.Delete(u).Error; err != nil {
|
||||||
tx.Rollback()
|
tx.Rollback()
|
||||||
@@ -232,7 +231,7 @@ func (us *UserService) Delete(u *model.User) error {
|
|||||||
tx.Commit()
|
tx.Commit()
|
||||||
// 删除关联的peer
|
// 删除关联的peer
|
||||||
if err := AllService.PeerService.EraseUserId(u.Id); err != nil {
|
if err := AllService.PeerService.EraseUserId(u.Id); err != nil {
|
||||||
global.Logger.Warn("User deleted successfully, but failed to unlink peer.")
|
Logger.Warn("User deleted successfully, but failed to unlink peer.")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@@ -249,28 +248,28 @@ func (us *UserService) Update(u *model.User) error {
|
|||||||
return errors.New("The last admin user cannot be disabled or demoted")
|
return errors.New("The last admin user cannot be disabled or demoted")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return global.DB.Model(u).Updates(u).Error
|
return DB.Model(u).Updates(u).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
// FlushToken 清空token
|
// FlushToken 清空token
|
||||||
func (us *UserService) FlushToken(u *model.User) error {
|
func (us *UserService) FlushToken(u *model.User) error {
|
||||||
return global.DB.Where("user_id = ?", u.Id).Delete(&model.UserToken{}).Error
|
return DB.Where("user_id = ?", u.Id).Delete(&model.UserToken{}).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
// FlushTokenByUuid 清空token
|
// FlushTokenByUuid 清空token
|
||||||
func (us *UserService) FlushTokenByUuid(uuid string) error {
|
func (us *UserService) FlushTokenByUuid(uuid string) error {
|
||||||
return global.DB.Where("device_uuid = ?", uuid).Delete(&model.UserToken{}).Error
|
return DB.Where("device_uuid = ?", uuid).Delete(&model.UserToken{}).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
// FlushTokenByUuids 清空token
|
// FlushTokenByUuids 清空token
|
||||||
func (us *UserService) FlushTokenByUuids(uuids []string) error {
|
func (us *UserService) FlushTokenByUuids(uuids []string) error {
|
||||||
return global.DB.Where("device_uuid in (?)", uuids).Delete(&model.UserToken{}).Error
|
return DB.Where("device_uuid in (?)", uuids).Delete(&model.UserToken{}).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdatePassword 更新密码
|
// UpdatePassword 更新密码
|
||||||
func (us *UserService) UpdatePassword(u *model.User, password string) error {
|
func (us *UserService) UpdatePassword(u *model.User, password string) error {
|
||||||
u.Password = us.EncryptPassword(password)
|
u.Password = us.EncryptPassword(password)
|
||||||
err := global.DB.Model(u).Update("password", u.Password).Error
|
err := DB.Model(u).Update("password", u.Password).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -306,8 +305,8 @@ func (us *UserService) InfoByOauthId(op string, openId string) *model.User {
|
|||||||
|
|
||||||
// RegisterByOauth 注册
|
// RegisterByOauth 注册
|
||||||
func (us *UserService) RegisterByOauth(oauthUser *model.OauthUser, op string) (error, *model.User) {
|
func (us *UserService) RegisterByOauth(oauthUser *model.OauthUser, op string) (error, *model.User) {
|
||||||
global.Lock.Lock("registerByOauth")
|
Lock.Lock("registerByOauth")
|
||||||
defer global.Lock.UnLock("registerByOauth")
|
defer Lock.UnLock("registerByOauth")
|
||||||
ut := AllService.OauthService.UserThirdInfo(op, oauthUser.OpenId)
|
ut := AllService.OauthService.UserThirdInfo(op, oauthUser.OpenId)
|
||||||
if ut.Id != 0 {
|
if ut.Id != 0 {
|
||||||
return nil, us.InfoById(ut.UserId)
|
return nil, us.InfoById(ut.UserId)
|
||||||
@@ -335,12 +334,12 @@ func (us *UserService) RegisterByOauth(oauthUser *model.OauthUser, op string) (e
|
|||||||
}
|
}
|
||||||
if user.Id != 0 {
|
if user.Id != 0 {
|
||||||
ut.FromOauthUser(user.Id, oauthUser, oauthType, op)
|
ut.FromOauthUser(user.Id, oauthUser, oauthType, op)
|
||||||
global.DB.Create(ut)
|
DB.Create(ut)
|
||||||
return nil, user
|
return nil, user
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tx := global.DB.Begin()
|
tx := DB.Begin()
|
||||||
ut = &model.UserThird{}
|
ut = &model.UserThird{}
|
||||||
ut.FromOauthUser(0, oauthUser, oauthType, op)
|
ut.FromOauthUser(0, oauthUser, oauthType, op)
|
||||||
// The initial username should be formatted
|
// The initial username should be formatted
|
||||||
@@ -372,27 +371,27 @@ func (us *UserService) GenerateUsernameByOauth(name string) string {
|
|||||||
|
|
||||||
// UserThirdsByUserId
|
// UserThirdsByUserId
|
||||||
func (us *UserService) UserThirdsByUserId(userId uint) (res []*model.UserThird) {
|
func (us *UserService) UserThirdsByUserId(userId uint) (res []*model.UserThird) {
|
||||||
global.DB.Where("user_id = ?", userId).Find(&res)
|
DB.Where("user_id = ?", userId).Find(&res)
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
func (us *UserService) UserThirdInfo(userId uint, op string) *model.UserThird {
|
func (us *UserService) UserThirdInfo(userId uint, op string) *model.UserThird {
|
||||||
ut := &model.UserThird{}
|
ut := &model.UserThird{}
|
||||||
global.DB.Where("user_id = ? and op = ?", userId, op).First(ut)
|
DB.Where("user_id = ? and op = ?", userId, op).First(ut)
|
||||||
return ut
|
return ut
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindLatestUserIdFromLoginLogByUuid 根据uuid查找最后登录的用户id
|
// FindLatestUserIdFromLoginLogByUuid 根据uuid查找最后登录的用户id
|
||||||
func (us *UserService) FindLatestUserIdFromLoginLogByUuid(uuid string) uint {
|
func (us *UserService) FindLatestUserIdFromLoginLogByUuid(uuid string) uint {
|
||||||
llog := &model.LoginLog{}
|
llog := &model.LoginLog{}
|
||||||
global.DB.Where("uuid = ?", uuid).Order("id desc").First(llog)
|
DB.Where("uuid = ?", uuid).Order("id desc").First(llog)
|
||||||
return llog.UserId
|
return llog.UserId
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsPasswordEmptyById 根据用户id判断密码是否为空,主要用于第三方登录的自动注册
|
// IsPasswordEmptyById 根据用户id判断密码是否为空,主要用于第三方登录的自动注册
|
||||||
func (us *UserService) IsPasswordEmptyById(id uint) bool {
|
func (us *UserService) IsPasswordEmptyById(id uint) bool {
|
||||||
u := &model.User{}
|
u := &model.User{}
|
||||||
if global.DB.Where("id = ?", id).First(u).Error != nil {
|
if DB.Where("id = ?", id).First(u).Error != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return u.Password == ""
|
return u.Password == ""
|
||||||
@@ -401,7 +400,7 @@ func (us *UserService) IsPasswordEmptyById(id uint) bool {
|
|||||||
// IsPasswordEmptyByUsername 根据用户id判断密码是否为空,主要用于第三方登录的自动注册
|
// IsPasswordEmptyByUsername 根据用户id判断密码是否为空,主要用于第三方登录的自动注册
|
||||||
func (us *UserService) IsPasswordEmptyByUsername(username string) bool {
|
func (us *UserService) IsPasswordEmptyByUsername(username string) bool {
|
||||||
u := &model.User{}
|
u := &model.User{}
|
||||||
if global.DB.Where("username = ?", username).First(u).Error != nil {
|
if DB.Where("username = ?", username).First(u).Error != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return u.Password == ""
|
return u.Password == ""
|
||||||
@@ -431,7 +430,7 @@ func (us *UserService) TokenList(page uint, size uint, f func(tx *gorm.DB)) *mod
|
|||||||
res := &model.UserTokenList{}
|
res := &model.UserTokenList{}
|
||||||
res.Page = int64(page)
|
res.Page = int64(page)
|
||||||
res.PageSize = int64(size)
|
res.PageSize = int64(size)
|
||||||
tx := global.DB.Model(&model.UserToken{})
|
tx := DB.Model(&model.UserToken{})
|
||||||
if f != nil {
|
if f != nil {
|
||||||
f(tx)
|
f(tx)
|
||||||
}
|
}
|
||||||
@@ -443,12 +442,12 @@ func (us *UserService) TokenList(page uint, size uint, f func(tx *gorm.DB)) *mod
|
|||||||
|
|
||||||
func (us *UserService) TokenInfoById(id uint) *model.UserToken {
|
func (us *UserService) TokenInfoById(id uint) *model.UserToken {
|
||||||
ut := &model.UserToken{}
|
ut := &model.UserToken{}
|
||||||
global.DB.Where("id = ?", id).First(ut)
|
DB.Where("id = ?", id).First(ut)
|
||||||
return ut
|
return ut
|
||||||
}
|
}
|
||||||
|
|
||||||
func (us *UserService) DeleteToken(l *model.UserToken) error {
|
func (us *UserService) DeleteToken(l *model.UserToken) error {
|
||||||
return global.DB.Delete(l).Error
|
return DB.Delete(l).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper functions, used for formatting username
|
// Helper functions, used for formatting username
|
||||||
@@ -461,29 +460,30 @@ func (us *UserService) formatUsername(username string) string {
|
|||||||
// Helper functions, getUserCount
|
// Helper functions, getUserCount
|
||||||
func (us *UserService) getUserCount() int64 {
|
func (us *UserService) getUserCount() int64 {
|
||||||
var count int64
|
var count int64
|
||||||
global.DB.Model(&model.User{}).Count(&count)
|
DB.Model(&model.User{}).Count(&count)
|
||||||
return count
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
// helper functions, getAdminUserCount
|
// helper functions, getAdminUserCount
|
||||||
func (us *UserService) getAdminUserCount() int64 {
|
func (us *UserService) getAdminUserCount() int64 {
|
||||||
var count int64
|
var count int64
|
||||||
global.DB.Model(&model.User{}).Where("is_admin = ?", true).Count(&count)
|
DB.Model(&model.User{}).Where("is_admin = ?", true).Count(&count)
|
||||||
return count
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
// UserTokenExpireTimestamp 生成用户token过期时间
|
// UserTokenExpireTimestamp 生成用户token过期时间
|
||||||
func (us *UserService) UserTokenExpireTimestamp() int64 {
|
func (us *UserService) UserTokenExpireTimestamp() int64 {
|
||||||
exp := global.Config.App.TokenExpire
|
exp := Config.App.TokenExpire
|
||||||
if exp == 0 {
|
if exp == 0 {
|
||||||
exp = 3600 * 24 * 7
|
//默认七天
|
||||||
|
exp = 604800
|
||||||
}
|
}
|
||||||
return time.Now().Add(time.Second * time.Duration(exp)).Unix()
|
return time.Now().Add(exp).Unix()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (us *UserService) RefreshAccessToken(ut *model.UserToken) {
|
func (us *UserService) RefreshAccessToken(ut *model.UserToken) {
|
||||||
ut.ExpiredAt = us.UserTokenExpireTimestamp()
|
ut.ExpiredAt = us.UserTokenExpireTimestamp()
|
||||||
global.DB.Model(ut).Update("expired_at", ut.ExpiredAt)
|
DB.Model(ut).Update("expired_at", ut.ExpiredAt)
|
||||||
}
|
}
|
||||||
func (us *UserService) AutoRefreshAccessToken(ut *model.UserToken) {
|
func (us *UserService) AutoRefreshAccessToken(ut *model.UserToken) {
|
||||||
if ut.ExpiredAt-time.Now().Unix() < 86400 {
|
if ut.ExpiredAt-time.Now().Unix() < 86400 {
|
||||||
@@ -492,11 +492,11 @@ func (us *UserService) AutoRefreshAccessToken(ut *model.UserToken) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (us *UserService) BatchDeleteUserToken(ids []uint) error {
|
func (us *UserService) BatchDeleteUserToken(ids []uint) error {
|
||||||
return global.DB.Where("id in ?", ids).Delete(&model.UserToken{}).Error
|
return DB.Where("id in ?", ids).Delete(&model.UserToken{}).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (us *UserService) VerifyJWT(token string) (uint, error) {
|
func (us *UserService) VerifyJWT(token string) (uint, error) {
|
||||||
return global.Jwt.ParseToken(token)
|
return Jwt.ParseToken(token)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsUsernameExists 判断用户名是否存在, it will check the internal database and LDAP(if enabled)
|
// IsUsernameExists 判断用户名是否存在, it will check the internal database and LDAP(if enabled)
|
||||||
@@ -506,7 +506,7 @@ func (us *UserService) IsUsernameExists(username string) bool {
|
|||||||
|
|
||||||
func (us *UserService) IsUsernameExistsLocal(username string) bool {
|
func (us *UserService) IsUsernameExistsLocal(username string) bool {
|
||||||
u := &model.User{}
|
u := &model.User{}
|
||||||
global.DB.Where("username = ?", username).First(u)
|
DB.Where("username = ?", username).First(u)
|
||||||
return u.Id != 0
|
return u.Id != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"math/rand"
|
"math/rand"
|
||||||
"reflect"
|
"reflect"
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Md5(str string) string {
|
func Md5(str string) string {
|
||||||
@@ -100,3 +101,11 @@ func InArray(k string, arr []string) bool {
|
|||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func StringConcat(strs ...string) string {
|
||||||
|
var builder strings.Builder
|
||||||
|
for _, str := range strs {
|
||||||
|
builder.WriteString(str)
|
||||||
|
}
|
||||||
|
return builder.String()
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user