mirror of
https://github.com/lejianwen/rustdesk-api.git
synced 2026-02-11 08:41:07 +00:00
Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9c8822f857 | ||
|
|
9709da7fb6 | ||
|
|
1852f10131 | ||
|
|
77d7b43e21 | ||
|
|
7410b539a0 | ||
|
|
0403d71502 | ||
|
|
2af1d93a7d | ||
|
|
76281ad761 | ||
|
|
d1ec9b4916 | ||
|
|
448e7460a9 | ||
|
|
ee0cbabffc | ||
|
|
d6a5af890a | ||
|
|
dc313441e5 | ||
|
|
c75320f4f4 | ||
|
|
c788f78416 | ||
|
|
49cf954d4a | ||
|
|
014e3db54f |
@@ -76,6 +76,7 @@ COPY --from=builder-backend /app/release /app/
|
|||||||
COPY --from=builder-backend /app/conf /app/conf/
|
COPY --from=builder-backend /app/conf /app/conf/
|
||||||
COPY --from=builder-backend /app/resources /app/resources/
|
COPY --from=builder-backend /app/resources /app/resources/
|
||||||
COPY --from=builder-backend /app/docs /app/docs/
|
COPY --from=builder-backend /app/docs /app/docs/
|
||||||
|
COPY --from=builder-backend /app/http/templates /app/http/templates
|
||||||
# Copy frontend build from builder2 stage
|
# Copy frontend build from builder2 stage
|
||||||
COPY --from=builder-admin-frontend /frontend/dist/ /app/resources/admin/
|
COPY --from=builder-admin-frontend /frontend/dist/ /app/resources/admin/
|
||||||
|
|
||||||
|
|||||||
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 | |
|
||||||
| ----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 | |
|
||||||
| 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
|
||||||
@@ -162,13 +161,15 @@ 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)
|
||||||
}
|
}
|
||||||
func DatabaseAutoUpdate() {
|
func DatabaseAutoUpdate() {
|
||||||
version := 260
|
version := 262
|
||||||
|
|
||||||
db := global.DB
|
db := global.DB
|
||||||
|
|
||||||
@@ -212,6 +213,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 +236,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 +254,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"
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
2
go.mod
2
go.mod
@@ -36,9 +36,11 @@ 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/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
|
||||||
|
|||||||
@@ -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,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -32,15 +32,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 +47,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 +144,9 @@ 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": response.TranslateParamMsg(c, "ParamIsEmpty", "state"),
|
||||||
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
cacheKey := state
|
cacheKey := state
|
||||||
@@ -151,17 +154,23 @@ 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": response.TranslateMsg(c, "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": response.TranslateMsg(c, "OauthFailed") + response.TranslateMsg(c, err.Error()),
|
||||||
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
userId := oauthCache.UserId
|
userId := oauthCache.UserId
|
||||||
@@ -172,28 +181,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": response.TranslateMsg(c, "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": response.TranslateMsg(c, "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": response.TranslateMsg(c, "BindFail"),
|
||||||
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.String(http.StatusOK, response.TranslateMsg(c, "BindSuccess"))
|
c.HTML(http.StatusOK, "oauth_success.html", gin.H{
|
||||||
|
"message": response.TranslateMsg(c, "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": response.TranslateMsg(c, "OauthHasBeenSuccess"),
|
||||||
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
user = service.AllService.UserService.InfoByOauthId(op, openid)
|
user = service.AllService.UserService.InfoByOauthId(op, openid)
|
||||||
@@ -210,7 +229,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": response.TranslateMsg(c, err.Error()),
|
||||||
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -230,10 +251,14 @@ 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": response.TranslateMsg(c, "OauthSuccess"),
|
||||||
|
})
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "ParamsError"))
|
c.HTML(http.StatusOK, "oauth_fail.html", gin.H{
|
||||||
|
"message": response.TranslateMsg(c, "ParamsError"),
|
||||||
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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")
|
||||||
|
|
||||||
@@ -79,6 +81,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 +92,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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
73
resources/templates/oauth_fail.html
Normal file
73
resources/templates/oauth_fail.html
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>授权失败 - 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">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="success-container">
|
||||||
|
<i class="fas fa-triangle-exclamation checkmark"></i>
|
||||||
|
<h1>授权失败!</h1>
|
||||||
|
<p>{{.message}}</p>
|
||||||
|
<a href="javascript:window.close()" class="return-link">关闭页面</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
73
resources/templates/oauth_success.html
Normal file
73
resources/templates/oauth_success.html
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>授权成功 - 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>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="success-container">
|
||||||
|
<i class="fas fa-check-circle checkmark"></i>
|
||||||
|
<h1>授权成功!</h1>
|
||||||
|
<p>您已成功授权访问您的账户。</p>
|
||||||
|
<p>现在可以关闭本页面或返回应用继续操作。</p>
|
||||||
|
<a href="javascript:window.close()" class="return-link">关闭页面</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
3
resources/web2/js/dist/index.js
vendored
3
resources/web2/js/dist/index.js
vendored
@@ -11088,6 +11088,9 @@ function R4(u = !1) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getUriFromRs(uri, isRelay = false, roffset = 0) {
|
function getUriFromRs(uri, isRelay = false, roffset = 0) {
|
||||||
|
if (window.ws_host) {
|
||||||
|
return window.ws_host + "/ws/" + (isRelay ? "relay" : "id")
|
||||||
|
}
|
||||||
const p = isHttps() ? "wss://" : "ws://"
|
const p = isHttps() ? "wss://" : "ws://"
|
||||||
const [domain, uriport] = uri.split(":")
|
const [domain, uriport] = uri.split(":")
|
||||||
if (!isHttps()) {
|
if (!isHttps()) {
|
||||||
|
|||||||
@@ -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,23 @@ 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 {
|
||||||
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 = ?", model.ShareAddressBookRuleTypePersonal, 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 +316,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 +335,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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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")
|
||||||
@@ -68,21 +72,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)
|
||||||
}
|
}
|
||||||
@@ -114,7 +135,7 @@ 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
|
||||||
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 +156,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 +185,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 +198,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 +211,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 +231,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 +256,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 +474,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,7 +123,7 @@ 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
|
return uuids, err
|
||||||
@@ -133,7 +132,7 @@ func (ps *PeerService) GetUuidListByIDs(ids []uint) ([]string, error) {
|
|||||||
// 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 +142,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"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -21,12 +25,31 @@ type Service struct {
|
|||||||
*LdapService
|
*LdapService
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user