Compare commits

...

68 Commits

Author SHA1 Message Date
lejianwen
ab231b3fed feat: Add SysInfoVer endpoint and AppService for version retrieval 2025-04-07 16:38:21 +08:00
lejianwen
e7f28cca36 fix: Update peer based on the UUID (#180) 2025-04-02 09:50:16 +08:00
lejianwen
505e8aac4b feat: Add Korean translations validator (#168) 2025-04-02 09:42:29 +08:00
lejianwen
746e2a6052 fix: Get Uuids 2025-03-15 21:02:47 +08:00
lejianwen
dc03d5d83d style: Update peer last online time logic (#173) 2025-03-15 21:02:08 +08:00
lejianwen
b770ab178d feat(admin): Add filter by ip and username (#172) 2025-03-15 19:49:49 +08:00
Tao Chen
fd7e022e88 fix: rm varify password accidentally (#176) 2025-03-15 19:40:02 +08:00
lejianwen
ac5df6826b fix: Init database err (#166) 2025-03-04 18:14:25 +08:00
lejianwen
91908859bc docs: Readme 2025-03-04 16:30:12 +08:00
lejianwen
9c8822f857 fix: templates 2025-03-04 16:14:34 +08:00
lejianwen
9709da7fb6 style(webclient): ws-host 2025-03-04 15:48:25 +08:00
lejianwen
1852f10131 feat(config): add ws-host configuration (#156) 2025-03-04 15:43:25 +08:00
Tao Chen
77d7b43e21 fix: Fix/ldap tls (#162)
* optimize and fix tls of LDAP

* fix
2025-03-02 22:59:01 +08:00
lejianwen
7410b539a0 style(service): refactor global dependencies to use local variables 2025-02-26 19:15:38 +08:00
lejianwen
0403d71502 feat(oauth): Oauth nonce (#148) 2025-02-26 16:36:53 +08:00
lejianwen
2af1d93a7d feat(oauth): Oauth callback page beautification (#115) 2025-02-25 13:51:49 +08:00
lejianwen
76281ad761 feat(api): Add device group for 1.3.8 2025-02-23 21:35:42 +08:00
lejianwen
d1ec9b4916 feat(api): Add /device-group/accessible for 1.3.8 2025-02-23 15:32:44 +08:00
lejianwen
448e7460a9 style(oidc): Oidc style 2025-02-21 09:49:41 +08:00
lejianwen
ee0cbabffc fix(admin): Admin hello 2025-02-20 19:37:34 +08:00
lejianwen
d6a5af890a style: No need exec sql on version 261 2025-02-20 19:22:22 +08:00
lejianwen
dc313441e5 fix: Js content-type 2025-02-19 16:04:03 +08:00
Tao Chen
c75320f4f4 feat(oidc): add pkce (#150) 2025-02-19 09:31:25 +08:00
lejianwen
c788f78416 docs: Readme 2025-02-17 10:59:38 +08:00
lejianwen
49cf954d4a fix(config)!: Token expire time (#145)
将配置中的过期时间单位统一为time.Duration,可以设置为`h`,`m`,`s`
2025-02-17 10:49:59 +08:00
lejianwen
014e3db54f docs: Readme 2025-02-16 21:06:55 +08:00
lejianwen
6d9c245c81 fix(webclient): port 2025-02-16 14:17:37 +08:00
lejianwen
7fa9b79f31 fix(webclient): port 2025-02-16 14:11:36 +08:00
lejianwen
c7f3d13b7f fix(webclient): port 2025-02-16 13:33:06 +08:00
lejianwen
46f08a89d2 feat: Login by pwd can be disable
---

Closes: #141
2025-02-16 13:06:45 +08:00
lejianwen
0dcfedb4dc fix(webclient)!: Webclient path is /ws/(relay|id) (#73 #143 #140)
Webclient 的反代引发了很多问题,现在将在HTTPS下的path固定为`/ws/(relay|id)`

---

Closes: #143 #140
2025-02-16 12:41:32 +08:00
lejianwen
918bf85a2d style: middleware name 2025-02-12 22:09:52 +08:00
lejianwen
99db5f7190 fix(admin): Admin web title 2025-02-12 21:10:07 +08:00
lejianwen
18eff791b2 style: Module name 2025-02-12 19:46:39 +08:00
lejianwen
624dcacac5 style: generate 2025-02-12 16:56:41 +08:00
lejianwen
878d5fd27c style: Remove generate 2025-02-12 16:25:04 +08:00
lejianwen
4b893ce0e8 docs: Docs 2025-02-12 16:14:51 +08:00
lejianwen
472524f836 style: Module name 2025-02-12 16:07:51 +08:00
lejianwen
dbf8b23b15 fix: Config watchConfig (#135)
---
Closes: #135
2025-02-10 10:13:34 +08:00
lejianwen
79a5dd53ae fix: User disabled can not work (#133)
---
Closes: #133
2025-02-10 10:13:15 +08:00
Tao Chen
8a5b20685c fix: When OIDC and LDAP work togethar (#132 #134)
* fix OIDC create user if LDAP enable

* `newUser.GroupId = 1` for ldap

* fix
2025-02-10 10:08:49 +08:00
lejianwen
5a9c972de0 docs: Readme 2025-02-09 21:13:01 +08:00
Tao Chen
fc0e67122d docs: add LDAP info (#130) 2025-02-09 19:36:31 +08:00
lejianwen
eb642f66ca docs: Readme 2025-02-07 18:14:12 +08:00
lejianwen
8cac15f7dd style: Log time 2025-02-07 17:57:28 +08:00
lejianwen
5011e2b7c1 feat: Web sso env (#125) 2025-02-07 17:52:40 +08:00
lejianwen
b0008143b1 docs: Up readme 2025-02-07 17:52:40 +08:00
lejianwen
a3c3ab5a72 style: Up conf 2025-02-07 17:52:40 +08:00
lejianwen
3a16269215 docs: Up readme 2025-02-07 17:52:40 +08:00
lejianwen
151145b0c3 feat: Random Initial Password for Admin (#117) 2025-02-07 17:52:39 +08:00
lejianwen
9c794e9d4b fix(build): Fix no admin in deb (#119 #120) 2025-02-03 13:29:35 +08:00
lejianwen
01f697d279 fix(api): Add Default Token Expire (#113) 2025-02-03 13:18:08 +08:00
lejianwen
6cdc37333b style: webclient 2025-02-03 00:01:10 +08:00
Tao Chen
ae32915565 feat(ldap): Add LDAP
* rename: Admin to AdminGroup

* update

* cleanup

* tmp save group mapping

* add enableControl(not-test)

* verify username exist before create(for LDAP)

* add getAllGroupsDn()

* rename

* adminGroup

* enable TLS Verify

* init for ldap

---------

Co-authored-by: Tao Chen <iamtaochen@outlook.com>
2025-02-02 23:59:52 +08:00
lejianwen
f49457dc5b feat(webclient): Up to 1.3.7 2025-01-21 19:12:28 +08:00
lejianwen
d9e2e247ea feat(api): Add api token expire
Resolves #109
2025-01-21 18:23:28 +08:00
lejianwen
c6f2f2f150 feat(api): Add api/version
Resolves #110
2025-01-20 20:04:22 +08:00
lejianwen
56b9c66cb8 docs: Up Swagger docs 2025-01-20 19:35:47 +08:00
lejianwen
5d8a0d0e1f style: Add Start Tips 2025-01-20 13:12:45 +08:00
lejianwen
f4cb9beda5 fix(api): Change tag to alphabetical sorting
Fixes: #108
2025-01-20 13:07:41 +08:00
lejianwen
b66fc3c06d fix(docs): Api Route doc 2025-01-19 13:10:43 +08:00
lejianwen
ab2e1a9236 feat(i18n): Add ZH_TW 2025-01-19 13:10:19 +08:00
Jia-Bin
ab77b400a1 Add Traditional Chinese 2025-01-18 23:04:39 +08:00
lejianwen
eb7ab63563 docs: Up readme 2025-01-16 22:01:23 +08:00
lejianwen
4cf7d01622 docs: Up readme 2025-01-16 21:59:46 +08:00
lejianwen
a876078a9c feat(server): Rustdesk Id Server Port & Relay Server Port #104 2025-01-16 20:57:00 +08:00
lejianwen
495f2ae3c6 refactor(config): Up Config Load 2025-01-16 20:40:42 +08:00
lejianwen
4e6d11baf0 docs: Up readme 2025-01-15 21:56:04 +08:00
120 changed files with 11404 additions and 8497 deletions

View File

@@ -84,6 +84,15 @@ jobs:
- name: tidy - name: tidy
run: go mod tidy run: go mod tidy
- name: Get tag version
run: |
TAG_VERSION="${GITHUB_REF##*/}"
VERSION="${TAG_VERSION#v}"
echo "VERSION=$VERSION" >> $GITHUB_ENV
- name: Write version to resources/version
run: echo $VERSION > resources/version
- name: swag - name: swag
run: | run: |
go install github.com/swaggo/swag/cmd/swag@latest go install github.com/swaggo/swag/cmd/swag@latest
@@ -194,6 +203,7 @@ jobs:
- name: Build package for ${{ matrix.job.platform }} arch - name: Build package for ${{ matrix.job.platform }} arch
run: | run: |
mv ${{ matrix.job.platform }}/release/apimain debian-build/${{ matrix.job.platform }}/bin/rustdesk-api mv ${{ matrix.job.platform }}/release/apimain debian-build/${{ matrix.job.platform }}/bin/rustdesk-api
mv ${{ matrix.job.platform }}/release/resources/admin resources
chmod -v a+x debian-build/${{ matrix.job.platform }}/bin/* chmod -v a+x debian-build/${{ matrix.job.platform }}/bin/*
mkdir -p data mkdir -p data
cp -vr debian systemd conf data resources runtime debian-build/${{ matrix.job.platform }}/ cp -vr debian systemd conf data resources runtime debian-build/${{ matrix.job.platform }}/

7
.gitignore vendored
View File

@@ -1,13 +1,8 @@
.idea .idea
runtime/* runtime/*
!runtime !runtime
!runtime/cache
!runtime/cache/.gitkeep !runtime/cache/.gitkeep
go.sum go.sum
resources/* resources/admin
!resources/public/upload/.gitignore
!resources/web
!resources/web2
!resources/i18n
release release
data data

191
README.md
View File

@@ -4,11 +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://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>
@@ -19,7 +20,10 @@
- 登录 - 登录
- 地址簿 - 地址簿
- 群组 - 群组
- 授权登录,支持`github`, `google``OIDC` 登录,支持`web后台`授权登录 - 授权登录
- 支持`github`, `google``OIDC` 登录,
- 支持`web后台`授权登录
- 支持`LDAP`(AD和OpenLDAP已测试), 如果API Server配置了LDAP
- i18n - i18n
- Web Admin - Web Admin
- 用户管理 - 用户管理
@@ -28,6 +32,7 @@
- 标签管理 - 标签管理
- 群组管理 - 群组管理
- Oauth 管理 - Oauth 管理
- 配置LDAP, 配置文件或者环境变量
- 登录日志 - 登录日志
- 链接日志 - 链接日志
- 文件传输日志 - 文件传输日志
@@ -46,45 +51,45 @@
## 功能 ## 功能
### API 服务 ### API 服务
基本实现了PC端基础的接口。支持Personal版本接口可以通过配置文件`rustdesk.personal`或环境变量`RUSTDESK_API_RUSTDESK_PERSONAL`来控制是否启用 基本实现了PC端基础的接口。支持Personal版本接口可以通过配置文件`rustdesk.personal`或环境变量`RUSTDESK_API_RUSTDESK_PERSONAL`来控制是否启用
#### 登录 <table>
<tr>
- 添加了`github`, `google` 以及`OIDC`授权登录需要在后台配置好就可以用了具体可看后台OAuth配置 <td width="50%" align="center" colspan="2"><b>登录</b></td>
- 添加了web后台授权登录,点击后直接登录后台就自动登录客户端了 </tr>
<tr>
![pc_login](docs/pc_login.png) <td width="50%" align="center" colspan="2"><img src="docs/pc_login.png"></td>
</tr>
#### 地址簿 <tr>
<td width="50%" align="center"><b>地址簿</b></td>
![pc_ab](docs/pc_ab.png) <td width="50%" align="center"><b>群组</b></td>
</tr>
#### 群组 <tr>
群组分为`共享组``普通组`,共享组中所有人都能看到小组成员的设备,普通组只有管理员能看到所有小组成员的设备 <td width="50%" align="center"><img src="docs/pc_ab.png"></td>
<td width="50%" align="center"><img src="docs/pc_gr.png"></td>
![pc_gr](docs/pc_gr.png) </tr>
</table>
### Web Admin: ### Web Admin:
* 使用前后端分离,提供用户友好的管理界面,主要用来管理和展示。前端代码在[rustdesk-api-web](https://github.com/lejianwen/rustdesk-api-web) * 使用前后端分离,提供用户友好的管理界面,主要用来管理和展示。前端代码在[rustdesk-api-web](https://github.com/lejianwen/rustdesk-api-web)
* 后台访问地址是`http://<your server>[:port]/_admin/`初次安装管理员为用户名密码为`admin` `admin`,请即时更改密码 * 后台访问地址是`http://<your server>[:port]/_admin/`
* 初次安装管理员为用户名为`admin`,密码将在控制台打印,可以通过[命令行](#CLI)更改密码
![img.png](./docs/init_admin_pwd.png)
1. 管理员界面 1. 管理员界面
![web_admin](docs/web_admin.png) ![web_admin](docs/web_admin.png)
2. 普通用户界面 2. 普通用户界面
![web_user](docs/web_admin_user.png) ![web_user](docs/web_admin_user.png)
右上角可以更改密码,可以切换语言,可以切换`白天/黑夜`模式
![web_resetpwd](docs/web_resetpwd.png)
3. 每个用户可以多个地址簿,也可以将地址簿共享给其他用户 3. 每个用户可以多个地址簿,也可以将地址簿共享给其他用户
4. 分组可以自定义,方便管理,暂时支持两种类型: `共享组``普通组` 4. 分组可以自定义,方便管理,暂时支持两种类型: `共享组``普通组`
5. 可以直接打开webclient方便使用也可以分享给游客游客可以直接通过webclient远程到设备 5. 可以直接打开webclient方便使用也可以分享给游客游客可以直接通过webclient远程到设备
![web_webclient](docs/admin_webclient.png)
6. Oauth,支持了`Github`, `Google` 以及 `OIDC`, 需要创建一个`OAuth App`,然后配置到后台 6. Oauth,支持了`Github`, `Google` 以及 `OIDC`, 需要创建一个`OAuth App`,然后配置到后台
![web_admin_oauth](docs/web_admin_oauth.png)
- 对于`Google``Github`, `Issuer``Scopes`不需要填写. - 对于`Google``Github`, `Issuer``Scopes`不需要填写.
- 对于`OIDC`, `Issuer`是必须的。`Scopes`是可选的,默认为 `openid,profile,email`. 确保可以获取 `sub`,`email``preferred_username` - 对于`OIDC`, `Issuer`是必须的。`Scopes`是可选的,默认为 `openid,profile,email`. 确保可以获取 `sub`,`email``preferred_username`
- `github oauth app``Settings`->`Developer settings`->`OAuth Apps`->`New OAuth App` - `github oauth app``Settings`->`Developer settings`->`OAuth Apps`->`New OAuth App`
@@ -103,9 +108,9 @@
* 可以官方指令 * 可以官方指令
* 可以添加自定义指令 * 可以添加自定义指令
* 可以执行自定义指令 * 可以执行自定义指令
![rustdesk_command_advance](./docs/rustdesk_command_advance.png)
11. **LDAP 支持**, 当在API Server上设置了LDAP(已测试AD和LDAP),可以通过LDAP中的用户信息进行登录 https://github.com/lejianwen/rustdesk-api/issues/114 ,如果LDAP验证失败返回本地用户
### Web Client: ### Web Client:
@@ -125,6 +130,7 @@
![api_swag](docs/api_swag.png) ![api_swag](docs/api_swag.png)
### CLI ### CLI
```bash ```bash
# 查看帮助 # 查看帮助
./apimain -h ./apimain -h
@@ -139,87 +145,54 @@
### 相关配置 ### 相关配置
* [配置文件](./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
```
### 环境变量 ### 环境变量
变量名前缀是`RUSTDESK_API`,环境变量如果存在将覆盖配置文件中的配置 环境变量和配置文件`conf/config.yaml`中的配置一一对应,变量名前缀是`RUSTDESK_API`
下面表格并未全部列出,可以参考`conf/config.yaml`中的配置。
| 变量名 | 说明 | 示例 | | 变量名 | 说明 | 示例 |
|---------------------------------------------------|---------------------------------------------------------|------------------------------| |--------------------------------------------------------|--------------------------------------------------------------------------------|------------------------------|
| TZ | 时区 | Asia/Shanghai | | TZ | 时区 | Asia/Shanghai |
| RUSTDESK_API_LANG | 语言 | `en`,`zh-CN` | | RUSTDESK_API_LANG | 语言 | `en`,`zh-CN` |
| RUSTDESK_API_APP_WEB_CLIENT | 是否启用web-client; 1:启用,0:不启用; 默认启用 | 1 | | RUSTDESK_API_APP_WEB_CLIENT | 是否启用web-client; 1:启用,0:不启用; 默认启用 | 1 |
| RUSTDESK_API_APP_REGISTER | 是否开启注册; `true`, `false` 默认`false` | `false` | | RUSTDESK_API_APP_REGISTER | 是否开启注册; `true`, `false` 默认`false` | `false` |
| RUSTDESK_API_APP_SHOW_SWAGGER | 是否可见swagger文档;`1`显示,`0`不显示,默认`0`不显示 | `1` | | RUSTDESK_API_APP_SHOW_SWAGGER | 是否可见swagger文档;`1`显示,`0`不显示,默认`0`不显示 | `1` |
| -----ADMIN配置----- | ---------- | ---------- | | RUSTDESK_API_APP_TOKEN_EXPIRE | token有效时长 | `168h` |
| RUSTDESK_API_ADMIN_TITLE | 后台标题 | `RustDesk Api Admin` | | RUSTDESK_API_APP_DISABLE_PWD_LOGIN | 是否禁用密码登录; `true`, `false` 默认`false` | `false` |
| RUSTDESK_API_ADMIN_HELLO | 后台欢迎语,可以使用`html` | | | -----ADMIN配置----- | ---------- | ---------- |
| RUSTDESK_API_ADMIN_HELLO_FILE | 后台欢迎语文件,如果内容多,使用文件更方便。<br>会覆盖`RUSTDESK_API_ADMIN_HELLO` | `./conf/admin/hello.html` | | RUSTDESK_API_ADMIN_TITLE | 后台标题 | `RustDesk Api Admin` |
| -----GIN配置----- | ---------- | ---------- | | RUSTDESK_API_ADMIN_HELLO | 后台欢迎语,可以使用`html` | |
| RUSTDESK_API_GIN_TRUST_PROXY | 信任的代理IP列表`,`分割,默认信任所有 | 192.168.1.2,192.168.1.3 | | RUSTDESK_API_ADMIN_HELLO_FILE | 后台欢迎语文件,如果内容多,使用文件更方便。<br>会覆盖`RUSTDESK_API_ADMIN_HELLO` | `./conf/admin/hello.html` |
| -----------GORM配置---------------- | ------------------------------------ | --------------------------- | | -----GIN配置----- | ---------- | ---------- |
| RUSTDESK_API_GORM_TYPE | 数据库类型sqlite或者mysql默认sqlite | sqlite | | RUSTDESK_API_GIN_TRUST_PROXY | 信任的代理IP列表`,`分割,默认信任所有 | 192.168.1.2,192.168.1.3 |
| RUSTDESK_API_GORM_MAX_IDLE_CONNS | 数据库最大空闲连接数 | 10 | | -----GORM配置----- | ---------- | --------------------------- |
| RUSTDESK_API_GORM_MAX_OPEN_CONNS | 数据库最大打开连接数 | 100 | | RUSTDESK_API_GORM_TYPE | 数据库类型sqlite或者mysql默认sqlite | sqlite |
| RUSTDESK_API_RUSTDESK_PERSONAL | 是否启用个人版API 1:启用,0:不启用; 默认启用 | 1 | | RUSTDESK_API_GORM_MAX_IDLE_CONNS | 数据库最大空闲连接数 | 10 |
| -----MYSQL配置----- | ---------- | ---------- | | RUSTDESK_API_GORM_MAX_OPEN_CONNS | 数据库最大打开连接数 | 100 |
| RUSTDESK_API_MYSQL_USERNAME | mysql用户名 | root | | RUSTDESK_API_RUSTDESK_PERSONAL | 是否启用个人版API 1:启用,0:不启用; 默认启用 | 1 |
| RUSTDESK_API_MYSQL_PASSWORD | mysql密码 | 111111 | | -----MYSQL配置----- | ---------- | ---------- |
| RUSTDESK_API_MYSQL_ADDR | mysql地址 | 192.168.1.66:3306 | | RUSTDESK_API_MYSQL_USERNAME | mysql用户名 | root |
| RUSTDESK_API_MYSQL_DBNAME | mysql数据库名 | rustdesk | | RUSTDESK_API_MYSQL_PASSWORD | mysql密码 | 111111 |
| -----RUSTDESK配置----- | --------------- | ---------- | | RUSTDESK_API_MYSQL_ADDR | mysql地址 | 192.168.1.66:3306 |
| RUSTDESK_API_RUSTDESK_ID_SERVER | Rustdesk的id服务器地址 | 192.168.1.66:21116 | | RUSTDESK_API_MYSQL_DBNAME | mysql数据库名 | rustdesk |
| RUSTDESK_API_RUSTDESK_RELAY_SERVER | Rustdesk的relay服务器地址 | 192.168.1.66:21117 | | -----RUSTDESK配置----- | ---------- | ---------- |
| RUSTDESK_API_RUSTDESK_API_SERVER | Rustdesk的api服务器地址 | http://192.168.1.66:21114 | | RUSTDESK_API_RUSTDESK_ID_SERVER | Rustdesk的id服务器地址 | 192.168.1.66:21116 |
| RUSTDESK_API_RUSTDESK_KEY | Rustdesk的key | 123456789 | | RUSTDESK_API_RUSTDESK_RELAY_SERVER | Rustdesk的relay服务器地址 | 192.168.1.66:21117 |
| RUSTDESK_API_RUSTDESK_KEY_FILE | Rustdesk存放key的文件 | `./conf/data/id_ed25519.pub` | | RUSTDESK_API_RUSTDESK_API_SERVER | Rustdesk的api服务器地址 | http://192.168.1.66:21114 |
| RUSTDESK_API_RUSTDESK_WEBCLIENT_MAGIC_QUERYONLINE | Web client v2 中是否启用新的在线状态查询方法; `1`:启用,`0`:不启用,默认不启用 | `0` | | RUSTDESK_API_RUSTDESK_KEY | Rustdesk的key | 123456789 |
| ----PROXY配置----- | --------------- | ---------- | | RUSTDESK_API_RUSTDESK_KEY_FILE | Rustdesk存放key的文件 | `./conf/data/id_ed25519.pub` |
| RUSTDESK_API_PROXY_ENABLE | 是否启用代理:`false`, `true` | `false` | | RUSTDESK_API_RUSTDESK_WEBCLIENT<br/>_MAGIC_QUERYONLINE | Web client v2 中是否启用新的在线状态查询方法; `1`:启用,`0`:不启用,默认不启用 | `0` |
| RUSTDESK_API_PROXY_HOST | 代理地址 | `http://127.0.0.1:1080` | | RUSTDESK_API_RUSTDESK_WS_HOST | 自定义Websocket Host | `wss://192.168.1.123:1234` |
| ----JWT配置---- | -------- | -------- | | ----PROXY配置----- | ---------- | ---------- |
| RUSTDESK_API_JWT_KEY | 自定义JWT KEY,为空则不启用JWT | | | RUSTDESK_API_PROXY_ENABLE | 是否启用代理:`false`, `true` | `false` |
| RUSTDESK_API_JWT_EXPIRE_DURATION | JWT有效时间 | 360000 | | RUSTDESK_API_PROXY_HOST | 代理地址 | `http://127.0.0.1:1080` |
| ----JWT配置---- | -------- | -------- |
| RUSTDESK_API_JWT_KEY | 自定义JWT KEY,为空则不启用JWT<br/>如果没使用`lejianwen/rustdesk-server`中的`MUST_LOGIN`,建议设置为空 | |
| RUSTDESK_API_JWT_EXPIRE_DURATION | JWT有效时间 | `168h` |
### 运行 ### 运行
@@ -285,10 +258,11 @@ jwt:
6. 打开浏览器访问`http://<your server[:port]>/_admin/`,默认用户名密码为`admin`,请及时更改密码。 6. 打开浏览器访问`http://<your server[:port]>/_admin/`,默认用户名密码为`admin`,请及时更改密码。
#### 使用我fork后的server-s6镜像运行 #### 使用`lejianwen/server-s6`镜像运行
- 已解决链接超时问题
- 可以强制登录后才能发起链接
- github https://github.com/lejianwen/rustdesk-server - github https://github.com/lejianwen/rustdesk-server
- docker hub https://hub.docker.com/r/lejianwen/rustdesk-server-s6
```yaml ```yaml
networks: networks:
@@ -330,4 +304,15 @@ jwt:
- [WIKI](https://github.com/lejianwen/rustdesk-api/wiki) - [WIKI](https://github.com/lejianwen/rustdesk-api/wiki)
- [链接超时问题](https://github.com/lejianwen/rustdesk-api/issues/92) - [链接超时问题](https://github.com/lejianwen/rustdesk-api/issues/92)
- [修改客户端ID](https://github.com/abdullah-erturk/RustDesk-ID-Changer) - [修改客户端ID](https://github.com/abdullah-erturk/RustDesk-ID-Changer)
- [webclient来源](https://hub.docker.com/r/keyurbhole/flutter_web_desk) - [webclient来源](https://hub.docker.com/r/keyurbhole/flutter_web_desk)
## 鸣谢
感谢所有做过贡献的人!
<a href="https://github.com/lejianwen/rustdesk-api/graphs/contributors">
<img src="https://contrib.rocks/image?repo=lejianwen/rustdesk-api" />
</a>
## 感谢你的支持!如果这个项目对你有帮助,请点个⭐️鼓励一下,谢谢!

View File

@@ -8,6 +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://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>
@@ -18,7 +19,10 @@ desktop software that provides self-hosted solutions.
- Login - Login
- Address Book - Address Book
- Groups - Groups
- Authorized login, supports `GitHub`, `Google` and `OIDC` login, supports `web admin` authorized login - Authorized login,
- supports `GitHub`, `Google` and `OIDC` login,
- supports `web admin` authorized login,
- supports LDAP(test AD and openladp) if API Server config
- i18n - i18n
- Web Admin - Web Admin
- User Management - User Management
@@ -27,6 +31,7 @@ desktop software that provides self-hosted solutions.
- Tag Management - Tag Management
- Group Management - Group Management
- OAuth Management - OAuth Management
- LDAP Config by config file or ENV
- Login Logs - Login Logs
- Connection Logs - Connection Logs
- File Transfer Logs - File Transfer Logs
@@ -47,45 +52,44 @@ desktop software that provides self-hosted solutions.
### API Service ### API Service
Basic implementation of the PC client's primary interfaces.Supports the Personal version api, which can be enabled by configuring the `rustdesk.personal` file or the `RUSTDESK_API_RUSTDESK_PERSONAL` environment variable. Basic implementation of the PC client's primary interfaces.Supports the Personal version api, which can be enabled by configuring the `rustdesk.personal` file or the `RUSTDESK_API_RUSTDESK_PERSONAL` environment variable.
#### Login <table>
<tr>
- Added `GitHub`, `Google` and `OIDC` login, which can be used after configuration in the admin panel. See the OAuth <td width="50%" align="center" colspan="2"><b>Login</b></td>
configuration section for details. </tr>
- Added authorization login for the web admin panel. <tr>
<td width="50%" align="center" colspan="2"><img src="docs/en_img/pc_login.png"></td>
![pc_login](docs/en_img/pc_login.png) </tr>
<tr>
#### Address Book <td width="50%" align="center"><b>Address Book</b></td>
<td width="50%" align="center"><b>Groups</b></td>
![pc_ab](docs/en_img/pc_ab.png) </tr>
<tr>
#### Groups <td width="50%" align="center"><img src="docs/en_img/pc_ab.png"></td>
Groups are divided into `shared groups` and `regular groups`. In shared groups, everyone can see the peers of all group members, while in regular groups, only administrators can see all members' peers. <td width="50%" align="center"><img src="docs/en_img/pc_gr.png"></td>
</tr>
![pc_gr](docs/en_img/pc_gr.png) </table>
### Web Admin ### Web Admin
* The frontend and backend are separated to provide a user-friendly management interface, primarily for managing and * The frontend and backend are separated to provide a user-friendly management interface, primarily for managing and
displaying data.Frontend code is available at [rustdesk-api-web](https://github.com/lejianwen/rustdesk-api-web) displaying data.Frontend code is available at [rustdesk-api-web](https://github.com/lejianwen/rustdesk-api-web)
* Admin panel URL: `http://<your server[:port]>/_admin/`. The default username and password for the initial * Admin panel URL: `http://<your server[:port]>/_admin/`
installation are `admin` `admin`, please change the password immediately. * For the initial installation, the admin username is `admin`, and the password will be printed in the console. You can change the password via the [command line](#CLI).
![img.png](./docs/init_admin_pwd.png)
1. Admin interface: 1. Admin interface:
![web_admin](docs/en_img/web_admin.png) ![web_admin](docs/en_img/web_admin.png)
2. Regular user interface: 2. Regular user interface:
![web_user](docs/en_img/web_admin_user.png) ![web_user](docs/en_img/web_admin_user.png)
In the top right corner, you can change the password, switch languages, and toggle between `day/night` mode.
![web_resetpwd](docs/en_img/web_resetpwd.png)
3. Each user can have multiple address books, which can also be shared with other users. 3. Each user can have multiple address books, which can also be shared with other users.
4. Groups can be customized for easy management. Currently, two types are supported: `shared group` and `regular group`. 4. Groups can be customized for easy management. Currently, two types are supported: `shared group` and `regular group`.
5. You can directly launch the client or open the web client for convenience; you can also share it with guests, who can remotely access the device via the web client. 5. You can directly launch the client or open the web client for convenience; you can also share it with guests, who can remotely access the device via the web client.
![web_webclient](docs/en_img/admin_webclient.png)
6. OAuth support: Currently, `GitHub`, `Google` and `OIDC` are supported. You need to create an `OAuth App` and configure it in 6. OAuth support: Currently, `GitHub`, `Google` and `OIDC` are supported. You need to create an `OAuth App` and configure it in
the admin panel. the admin panel.
![web_admin_oauth](docs/en_img/web_admin_oauth.png)
- For `Google` and `Github`, you don't need to fill the `Issuer` and `Scpoes` - For `Google` and `Github`, you don't need to fill the `Issuer` and `Scpoes`
- For `OIDC`, you must set the `Issuer`. And `Scopes` is optional which default is `openid,email,profile`, please make sure this `Oauth App` can access `sub`, `email` and `preferred_username` - For `OIDC`, you must set the `Issuer`. And `Scopes` is optional which default is `openid,email,profile`, please make sure this `Oauth App` can access `sub`, `email` and `preferred_username`
- Create a `GitHub OAuth App` - Create a `GitHub OAuth App`
@@ -97,19 +101,16 @@ installation are `admin` `admin`, please change the password immediately.
8. Connection logs 8. Connection logs
9. File transfer logs 9. File transfer logs
10. Server control 10. Server control
- `Simple mode`, some simple commands have been GUI-ized and can be executed directly in the backend
![rustdesk_command_simple](./docs/en_img/rustdesk_command_simple.png)
- `Simple mode`, some simple commands have been GUI-ized and can be executed directly in the backend - `Advanced mode`, commands can be executed directly in the backend
![rustdesk_command_simple](./docs/en_img/rustdesk_command_simple.png)
- `Advanced mode`, commands can be executed directly in the backend
* Official commands can be used * Official commands can be used
* Custom commands can be added * Custom commands can be added
* Custom commands can be executed * Custom commands can be executed
![rustdesk_command_advance](./docs/en_img/rustdesk_command_advance.png) 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:
1. If you're already logged into the admin panel, the web client will log in automatically. 1. If you're already logged into the admin panel, the web client will log in automatically.
@@ -142,87 +143,55 @@ installation are `admin` `admin`, please change the password immediately.
### 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
```
### Environment Variables ### Environment Variables
The prefix for variable names is `RUSTDESK_API`. If environment variables exist, they will override the configurations in the configuration file. 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`.
| 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` |
| ----- ADMIN Configuration----- | ---------- | ---------- | | RUSTDESK_API_APP_TOKEN_EXPIRE | token expire duration | `168h` |
| RUSTDESK_API_ADMIN_TITLE | Admin Title | `RustDesk Api Admin` | | RUSTDESK_API_APP_DISABLE_PWD_LOGIN | disable password login | `false` |
| RUSTDESK_API_ADMIN_HELLO | Admin welcome message, you can use `html` | | | ----- ADMIN Configuration----- | ---------- | ---------- |
| RUSTDESK_API_ADMIN_HELLO_FILE | Admin welcome message file,<br>will override `RUSTDESK_API_ADMIN_HELLO` | `./conf/admin/hello.html` | | RUSTDESK_API_ADMIN_TITLE | Admin Title | `RustDesk Api Admin` |
| ----- GIN Configuration ----- | --------------------------------------- | ----------------------------- | | RUSTDESK_API_ADMIN_HELLO | Admin welcome message, you can use `html` | |
| RUSTDESK_API_GIN_TRUST_PROXY | Trusted proxy IPs, separated by commas. | 192.168.1.2,192.168.1.3 | | RUSTDESK_API_ADMIN_HELLO_FILE | Admin welcome message file,<br>will override `RUSTDESK_API_ADMIN_HELLO` | `./conf/admin/hello.html` |
| ----- GORM Configuration ----- | --------------------------------------- | ----------------------------- | | ----- GIN Configuration ----- | --------------------------------------- | ----------------------------- |
| RUSTDESK_API_GORM_TYPE | Database type (`sqlite` or `mysql`). Default is `sqlite`. | sqlite | | RUSTDESK_API_GIN_TRUST_PROXY | Trusted proxy IPs, separated by commas. | 192.168.1.2,192.168.1.3 |
| RUSTDESK_API_GORM_MAX_IDLE_CONNS | Maximum idle connections | 10 | | ----- GORM Configuration ----- | --------------------------------------- | ----------------------------- |
| RUSTDESK_API_GORM_MAX_OPEN_CONNS | Maximum open connections | 100 | | RUSTDESK_API_GORM_TYPE | Database type (`sqlite` or `mysql`). Default is `sqlite`. | sqlite |
| RUSTDESK_API_RUSTDESK_PERSONAL | Open Personal Api 1:Enable,0:Disable | 1 | | RUSTDESK_API_GORM_MAX_IDLE_CONNS | Maximum idle connections | 10 |
| ----- MYSQL Configuration ----- | --------------------------------------- | ----------------------------- | | RUSTDESK_API_GORM_MAX_OPEN_CONNS | Maximum open connections | 100 |
| RUSTDESK_API_MYSQL_USERNAME | MySQL username | root | | RUSTDESK_API_RUSTDESK_PERSONAL | Open Personal Api 1:Enable,0:Disable | 1 |
| RUSTDESK_API_MYSQL_PASSWORD | MySQL password | 111111 | | ----- MYSQL Configuration ----- | --------------------------------------- | ----------------------------- |
| RUSTDESK_API_MYSQL_ADDR | MySQL address | 192.168.1.66:3306 | | RUSTDESK_API_MYSQL_USERNAME | MySQL username | root |
| RUSTDESK_API_MYSQL_DBNAME | MySQL database name | rustdesk | | RUSTDESK_API_MYSQL_PASSWORD | MySQL password | 111111 |
| ----- RUSTDESK Configuration ----- | --------------------------------------- | ----------------------------- | | RUSTDESK_API_MYSQL_ADDR | MySQL address | 192.168.1.66:3306 |
| RUSTDESK_API_RUSTDESK_ID_SERVER | Rustdesk ID server address | 192.168.1.66:21116 | | RUSTDESK_API_MYSQL_DBNAME | MySQL database name | rustdesk |
| RUSTDESK_API_RUSTDESK_RELAY_SERVER | Rustdesk relay server address | 192.168.1.66:21117 | | ----- RUSTDESK Configuration ----- | --------------------------------------- | ----------------------------- |
| RUSTDESK_API_RUSTDESK_API_SERVER | Rustdesk API server address | http://192.168.1.66:21114 | | RUSTDESK_API_RUSTDESK_ID_SERVER | Rustdesk ID server address | 192.168.1.66:21116 |
| RUSTDESK_API_RUSTDESK_KEY | Rustdesk key | 123456789 | | RUSTDESK_API_RUSTDESK_RELAY_SERVER | Rustdesk relay server address | 192.168.1.66:21117 |
| RUSTDESK_API_RUSTDESK_KEY_FILE | Rustdesk key file | `./conf/data/id_ed25519.pub` | | RUSTDESK_API_RUSTDESK_API_SERVER | Rustdesk API server address | http://192.168.1.66:21114 |
| RUSTDESK_API_RUSTDESK_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 | Rustdesk key | 123456789 |
| ---- PROXY ----- | --------------- | ---------- | | RUSTDESK_API_RUSTDESK_KEY_FILE | Rustdesk key file | `./conf/data/id_ed25519.pub` |
| RUSTDESK_API_PROXY_ENABLE | proxy_enable :`false`, `true` | `false` | | 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_HOST | proxy_host | `http://127.0.0.1:1080` | | RUSTDESK_API_RUSTDESK_WS_HOST | Custom Websocket Host | `wss://192.168.1.123:1234` |
| ----JWT---- | -------- | -------- | | ---- PROXY ----- | --------------- | ---------- |
| RUSTDESK_API_JWT_KEY | JWT KEY. Set empty to disable jwt | | | RUSTDESK_API_PROXY_ENABLE | proxy_enable :`false`, `true` | `false` |
| RUSTDESK_API_JWT_EXPIRE_DURATION | JWT expire duration | 360000 | | RUSTDESK_API_PROXY_HOST | proxy_host | `http://127.0.0.1:1080` |
| ----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
@@ -293,8 +262,9 @@ Download the release from [release](https://github.com/lejianwen/rustdesk-api/re
#### Running with my forked server-s6 image #### Running with my forked server-s6 image
- Connection timeout issue resolved
- Can enforce login before initiating a connection
- github https://github.com/lejianwen/rustdesk-server - github https://github.com/lejianwen/rustdesk-server
- docker hub https://hub.docker.com/r/lejianwen/rustdesk-server-s6
```yaml ```yaml
networks: networks:
@@ -334,4 +304,14 @@ Download the release from [release](https://github.com/lejianwen/rustdesk-api/re
- [WIKI](https://github.com/lejianwen/rustdesk-api/wiki) - [WIKI](https://github.com/lejianwen/rustdesk-api/wiki)
- [Connection Timeout](https://github.com/lejianwen/rustdesk-api/issues/92) - [Connection Timeout](https://github.com/lejianwen/rustdesk-api/issues/92)
- [Change client ID](https://github.com/abdullah-erturk/RustDesk-ID-Changer) - [Change client ID](https://github.com/abdullah-erturk/RustDesk-ID-Changer)
- [Web client source](https://hub.docker.com/r/keyurbhole/flutter_web_desk) - [Web client source](https://hub.docker.com/r/keyurbhole/flutter_web_desk)
## Acknowledgements
Thanks to everyone who contributed!
<a href="https://github.com/lejianwen/rustdesk-api/graphs/contributors">
<img src="https://contrib.rocks/image?repo=lejianwen/rustdesk-api" />
</a>
## Thanks for your support! If you find this project useful, please give it a ⭐️. Thank you!

View File

@@ -1,24 +1,23 @@
package main package main
import ( import (
"Gwen/config"
"Gwen/global"
"Gwen/http"
"Gwen/lib/cache"
"Gwen/lib/jwt"
"Gwen/lib/lock"
"Gwen/lib/logger"
"Gwen/lib/orm"
"Gwen/lib/upload"
"Gwen/model"
"Gwen/service"
"fmt"
"github.com/go-redis/redis/v8" "github.com/go-redis/redis/v8"
"github.com/lejianwen/rustdesk-api/v2/config"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http"
"github.com/lejianwen/rustdesk-api/v2/lib/cache"
"github.com/lejianwen/rustdesk-api/v2/lib/jwt"
"github.com/lejianwen/rustdesk-api/v2/lib/lock"
"github.com/lejianwen/rustdesk-api/v2/lib/logger"
"github.com/lejianwen/rustdesk-api/v2/lib/orm"
"github.com/lejianwen/rustdesk-api/v2/lib/upload"
"github.com/lejianwen/rustdesk-api/v2/model"
"github.com/lejianwen/rustdesk-api/v2/service"
"github.com/lejianwen/rustdesk-api/v2/utils"
"github.com/nicksnyder/go-i18n/v2/i18n" "github.com/nicksnyder/go-i18n/v2/i18n"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"os" "os"
"strconv" "strconv"
"time"
) )
// @title 管理系统API // @title 管理系统API
@@ -39,7 +38,7 @@ var rootCmd = &cobra.Command{
InitGlobal() InitGlobal()
}, },
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
//gin global.Logger.Info("API SERVER START")
http.ApiInit() http.ApiInit()
}, },
} }
@@ -52,12 +51,16 @@ var resetPwdCmd = &cobra.Command{
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
pwd := args[0] pwd := args[0]
admin := service.AllService.UserService.InfoById(1) admin := service.AllService.UserService.InfoById(1)
err := service.AllService.UserService.UpdatePassword(admin, pwd) if admin.Id == 0 {
if err != nil { global.Logger.Warn("user not found! ")
fmt.Printf("reset password fail! %v \n", err)
return return
} }
fmt.Printf("reset password success! \n") err := service.AllService.UserService.UpdatePassword(admin, pwd)
if err != nil {
global.Logger.Error("reset password fail! ", err)
return
}
global.Logger.Info("reset password success! ")
}, },
} }
var resetUserPwdCmd = &cobra.Command{ var resetUserPwdCmd = &cobra.Command{
@@ -70,20 +73,24 @@ var resetUserPwdCmd = &cobra.Command{
pwd := args[1] pwd := args[1]
uid, err := strconv.Atoi(userId) uid, err := strconv.Atoi(userId)
if err != nil { if err != nil {
fmt.Printf("userId must be int! \n") global.Logger.Warn("userId must be int!")
return return
} }
if uid <= 0 { if uid <= 0 {
fmt.Printf("userId must be greater than 0! \n") global.Logger.Warn("userId must be greater than 0! ")
return return
} }
u := service.AllService.UserService.InfoById(uint(uid)) u := service.AllService.UserService.InfoById(uint(uid))
err = service.AllService.UserService.UpdatePassword(u, pwd) if u.Id == 0 {
if err != nil { global.Logger.Warn("user not found! ")
fmt.Printf("reset password fail! %v \n", err)
return return
} }
fmt.Printf("reset password success! \n") err = service.AllService.UserService.UpdatePassword(u, pwd)
if err != nil {
global.Logger.Warn("reset password fail! ", err)
return
}
global.Logger.Info("reset password success!")
}, },
} }
@@ -93,7 +100,7 @@ func init() {
} }
func main() { func main() {
if err := rootCmd.Execute(); err != nil { if err := rootCmd.Execute(); err != nil {
fmt.Println(err) global.Logger.Error(err)
os.Exit(1) os.Exit(1)
} }
} }
@@ -102,9 +109,6 @@ func InitGlobal() {
//配置解析 //配置解析
global.Viper = config.Init(&global.Config, global.ConfigPath) global.Viper = config.Init(&global.Config, global.ConfigPath)
//从配置文件中加载密钥
config.LoadKeyFile(&global.Config.Rustdesk)
//日志 //日志
global.Logger = logger.New(&logger.Config{ global.Logger = logger.New(&logger.Config{
Path: global.Config.Logger.Path, Path: global.Config.Logger.Path,
@@ -148,7 +152,6 @@ func InitGlobal() {
MaxOpenConns: global.Config.Gorm.MaxOpenConns, MaxOpenConns: global.Config.Gorm.MaxOpenConns,
}) })
} }
DatabaseAutoUpdate()
//validator //validator
global.ApiInitValidator() global.ApiInitValidator()
@@ -165,20 +168,23 @@ func InitGlobal() {
//jwt //jwt
//fmt.Println(global.Config.Jwt.PrivateKey) //fmt.Println(global.Config.Jwt.PrivateKey)
global.Jwt = jwt.NewJwt(global.Config.Jwt.Key, global.Config.Jwt.ExpireDuration*time.Second) global.Jwt = jwt.NewJwt(global.Config.Jwt.Key, global.Config.Jwt.ExpireDuration)
//locker //locker
global.Lock = lock.NewLocal() global.Lock = lock.NewLocal()
//service
service.New(&global.Config, global.DB, global.Logger, global.Jwt, global.Lock)
DatabaseAutoUpdate()
} }
func DatabaseAutoUpdate() { func DatabaseAutoUpdate() {
version := 260 version := 262
db := global.DB db := global.DB
if global.Config.Gorm.Type == config.TypeMysql { if global.Config.Gorm.Type == config.TypeMysql {
//检查存不存在数据库,不存在则创建 //检查存不存在数据库,不存在则创建
dbName := db.Migrator().CurrentDatabase() dbName := db.Migrator().CurrentDatabase()
fmt.Println("dbName", dbName)
if dbName == "" { if dbName == "" {
dbName = global.Config.Mysql.Dbname dbName = global.Config.Mysql.Dbname
// 移除 DSN 中的数据库名称,以便初始连接时不指定数据库 // 移除 DSN 中的数据库名称,以便初始连接时不指定数据库
@@ -190,18 +196,18 @@ func DatabaseAutoUpdate() {
// 获取底层的 *sql.DB 对象,并确保在程序退出时关闭连接 // 获取底层的 *sql.DB 对象,并确保在程序退出时关闭连接
sqlDBWithoutDB, err := dbWithoutDB.DB() sqlDBWithoutDB, err := dbWithoutDB.DB()
if err != nil { if err != nil {
fmt.Printf("获取底层 *sql.DB 对象失败: %v\n", err) global.Logger.Errorf("获取底层 *sql.DB 对象失败: %v", err)
return return
} }
defer func() { defer func() {
if err := sqlDBWithoutDB.Close(); err != nil { if err := sqlDBWithoutDB.Close(); err != nil {
fmt.Printf("关闭连接失败: %v\n", err) global.Logger.Errorf("关闭连接失败: %v", err)
} }
}() }()
err = dbWithoutDB.Exec("CREATE DATABASE IF NOT EXISTS " + dbName + " DEFAULT CHARSET utf8mb4").Error err = dbWithoutDB.Exec("CREATE DATABASE IF NOT EXISTS " + dbName + " DEFAULT CHARSET utf8mb4").Error
if err != nil { if err != nil {
fmt.Println(err) global.Logger.Error(err)
return return
} }
} }
@@ -216,6 +222,7 @@ func DatabaseAutoUpdate() {
if v.Version < uint(version) { if v.Version < uint(version) {
Migrate(uint(version)) Migrate(uint(version))
} }
// 245迁移 // 245迁移
if v.Version < 245 { if v.Version < 245 {
//oauths 表的 oauth_type 字段设置为 op同样的值 //oauths 表的 oauth_type 字段设置为 op同样的值
@@ -238,7 +245,7 @@ func DatabaseAutoUpdate() {
} }
func Migrate(version uint) { func Migrate(version uint) {
fmt.Println("migrating....", version) global.Logger.Info("Migrating....", version)
err := global.DB.AutoMigrate( err := global.DB.AutoMigrate(
&model.Version{}, &model.Version{},
&model.User{}, &model.User{},
@@ -256,9 +263,10 @@ func Migrate(version uint) {
&model.AddressBookCollection{}, &model.AddressBookCollection{},
&model.AddressBookCollectionRule{}, &model.AddressBookCollectionRule{},
&model.ServerCmd{}, &model.ServerCmd{},
&model.DeviceGroup{},
) )
if err != nil { if err != nil {
fmt.Println("migrate err :=>", err) global.Logger.Error("migrate err :=>", err)
} }
global.DB.Create(&model.Version{Version: version}) global.DB.Create(&model.Version{Version: version})
//如果是初次则创建一个默认用户 //如果是初次则创建一个默认用户
@@ -292,7 +300,11 @@ func Migrate(version uint) {
IsAdmin: &is_admin, IsAdmin: &is_admin,
GroupId: 1, GroupId: 1,
} }
admin.Password = service.AllService.UserService.EncryptPassword("admin")
// 生成随机密码
pwd := utils.RandomString(8)
global.Logger.Info("Admin Password Is: ", pwd)
admin.Password = service.AllService.UserService.EncryptPassword(pwd)
global.DB.Create(admin) global.DB.Create(admin)
} }

View File

@@ -1 +1 @@
### 👏👏👏 你好 ***{{username}}*** 欢迎使用 [RustDesk Api](https://github.com/lejianwen/rustdesk-api) ### 👏👏👏 你好 ***{{username}}*** 欢迎使用 [RustDesk API](https://github.com/lejianwen/rustdesk-api)

View File

@@ -3,6 +3,9 @@ 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: 168h
web-sso: true #web auth sso
disable-pwd-login: false #禁用密码登录
admin: admin:
title: "RustDesk Api Admin" title: "RustDesk Api Admin"
hello-file: "./conf/admin/hello.html" #优先使用file hello-file: "./conf/admin/hello.html" #优先使用file
@@ -29,16 +32,38 @@ 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: "debug" #trace,debug,info,warn,error,fatal level: "info" #trace,debug,info,warn,error,fatal
report-caller: true report-caller: true
proxy: proxy:
enable: false enable: false
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:
enable: false
url: "ldap://ldap.example.com:389"
tls-ca-file: ""
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.
redis: redis:
addr: "127.0.0.1:6379" addr: "127.0.0.1:6379"
password: "" password: ""

View File

@@ -2,9 +2,9 @@ package config
import ( import (
"fmt" "fmt"
"github.com/fsnotify/fsnotify"
"github.com/spf13/viper" "github.com/spf13/viper"
"strings" "strings"
"time"
) )
const ( const (
@@ -14,9 +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 time.Duration `mapstructure:"token-expire"`
WebSso bool `mapstructure:"web-sso"`
DisablePwdLogin bool `mapstructure:"disable-pwd-login"`
} }
type Admin struct { type Admin struct {
Title string `mapstructure:"title"` Title string `mapstructure:"title"`
@@ -37,10 +40,11 @@ type Config struct {
Jwt Jwt Jwt Jwt
Rustdesk Rustdesk Rustdesk Rustdesk
Proxy Proxy Proxy Proxy
Ldap Ldap
} }
// Init 初始化配置 // Init 初始化配置
func Init(rowVal interface{}, path string) *viper.Viper { func Init(rowVal *Config, path string) *viper.Viper {
if path == "" { if path == "" {
path = DefaultConfig path = DefaultConfig
} }
@@ -54,18 +58,26 @@ func Init(rowVal interface{}, path string) *viper.Viper {
if err != nil { if err != nil {
panic(fmt.Errorf("Fatal error config file: %s \n", err)) panic(fmt.Errorf("Fatal error config file: %s \n", err))
} }
v.WatchConfig() /*
v.OnConfigChange(func(e fsnotify.Event) { v.WatchConfig()
//配置文件修改监听
fmt.Println("config file changed:", e.Name)
if err2 := v.Unmarshal(rowVal); err2 != nil {
fmt.Println(err2)
}
})
if err := v.Unmarshal(rowVal); err != nil {
fmt.Println(err)
}
//监听配置修改没什么必要
v.OnConfigChange(func(e fsnotify.Event) {
//配置文件修改监听
fmt.Println("config file changed:", e.Name)
if err2 := v.Unmarshal(rowVal); err2 != nil {
fmt.Println(err2)
}
rowVal.Rustdesk.LoadKeyFile()
rowVal.Rustdesk.ParsePort()
})
*/
if err := v.Unmarshal(rowVal); err != nil {
panic(fmt.Errorf("Fatal error config: %s \n", err))
}
rowVal.Rustdesk.LoadKeyFile()
rowVal.Rustdesk.ParsePort()
return v return v
} }

36
config/ldap.go Normal file
View File

@@ -0,0 +1,36 @@
package config
type LdapUser struct {
BaseDn string `mapstructure:"base-dn"` // The base DN of the user for searching
EnableAttr string `mapstructure:"enable-attr"` // The attribute name of the user for enabling, in AD it is "userAccountControl", empty means no enable attribute, all users are enabled
EnableAttrValue string `mapstructure:"enable-attr-value"` // The value of the enable attribute when the user is enabled. If you are using AD, just leave it random str, it will be ignored.
Filter string `mapstructure:"filter"`
Username string `mapstructure:"username"`
Email string `mapstructure:"email"`
FirstName string `mapstructure:"first-name"`
LastName string `mapstructure:"last-name"`
Sync bool `mapstructure:"sync"` // Will sync the user's information to the internal database
AdminGroup string `mapstructure:"admin-group"` // Which group is the admin group
}
// type LdapGroup struct {
// BaseDn string `mapstructure:"base-dn"` // The base DN of the group for searching
// Name string `mapstructure:"name"` // The attribute name of the group
// Filter string `mapstructure:"filter"`
// Admin string `mapstructure:"admin"` // Which group is the admin group
// Member string `mapstructure:"member"` // How to get the member of the group: member, uniqueMember, or memberOf (default: member)
// Mode string `mapstructure:"mode"`
// Map map[string]string `mapstructure:"map"` // If mode is "map", map the LDAP group to the internal group
// }
type Ldap struct {
Enable bool `mapstructure:"enable"`
Url string `mapstructure:"url"`
TlsCaFile string `mapstructure:"tls-ca-file"`
TlsVerify bool `mapstructure:"tls-verify"`
BaseDn string `mapstructure:"base-dn"`
BindDn string `mapstructure:"bind-dn"`
BindPassword string `mapstructure:"bind-password"`
User LdapUser `mapstructure:"user"`
// Group LdapGroup `mapstructure:"group"`
}

View File

@@ -17,4 +17,4 @@ type OidcOauth struct {
ClientId string `mapstructure:"client-id"` ClientId string `mapstructure:"client-id"`
ClientSecret string `mapstructure:"client-secret"` ClientSecret string `mapstructure:"client-secret"`
RedirectUrl string `mapstructure:"redirect-url"` RedirectUrl string `mapstructure:"redirect-url"`
} }

View File

@@ -2,31 +2,57 @@ package config
import ( import (
"os" "os"
"strconv"
"strings"
)
const (
DefaultIdServerPort = 21116
DefaultRelayServerPort = 21117
) )
type Rustdesk struct { type Rustdesk struct {
IdServer string `mapstructure:"id-server"` IdServer string `mapstructure:"id-server"`
RelayServer string `mapstructure:"relay-server"` IdServerPort int `mapstructure:"-"`
ApiServer string `mapstructure:"api-server"` RelayServer string `mapstructure:"relay-server"`
Key string `mapstructure:"key"` RelayServerPort int `mapstructure:"-"`
KeyFile string `mapstructure:"key-file"` ApiServer string `mapstructure:"api-server"`
Personal int `mapstructure:"personal"` Key string `mapstructure:"key"`
KeyFile string `mapstructure:"key-file"`
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 LoadKeyFile(rustdesk *Rustdesk) { func (rd *Rustdesk) LoadKeyFile() {
// Load key file // Load key file
if rustdesk.Key != "" { if rd.Key != "" {
return return
} }
if rustdesk.KeyFile != "" { if rd.KeyFile != "" {
// Load key from file // Load key from file
b, err := os.ReadFile(rustdesk.KeyFile) b, err := os.ReadFile(rd.KeyFile)
if err != nil { if err != nil {
return return
} }
rustdesk.Key = string(b) rd.Key = string(b)
return return
} }
} }
func (rd *Rustdesk) ParsePort() {
// Parse port
idres := strings.Split(rd.IdServer, ":")
if len(idres) == 1 {
rd.IdServerPort = DefaultIdServerPort
} else if len(idres) == 2 {
rd.IdServerPort, _ = strconv.Atoi(idres[1])
}
relayres := strings.Split(rd.RelayServer, ":")
if len(relayres) == 1 {
rd.RelayServerPort = DefaultRelayServerPort
} else if len(relayres) == 2 {
rd.RelayServerPort, _ = strconv.Atoi(relayres[1])
}
}

View File

@@ -981,40 +981,6 @@ const docTemplateadmin = `{
} }
} }
}, },
"/admin/app-config": {
"get": {
"security": [
{
"token": []
}
],
"description": "APP服务配置",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"ADMIN"
],
"summary": "APP服务配置",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/admin/audit_conn/batchDelete": { "/admin/audit_conn/batchDelete": {
"post": { "post": {
"security": [ "security": [
@@ -1441,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": [
@@ -1817,7 +2057,7 @@ const docTemplateadmin = `{
"in": "body", "in": "body",
"required": true, "required": true,
"schema": { "schema": {
"$ref": "#/definitions/Gwen_http_request_admin.Login" "$ref": "#/definitions/github_com_lejianwen_rustdesk-api_v2_http_request_admin.Login"
} }
} }
], ],
@@ -1881,7 +2121,7 @@ const docTemplateadmin = `{
} }
} }
}, },
"/admin/login_log/delete": { "/admin/login_log/batchDelete": {
"post": { "post": {
"security": [ "security": [
{ {
@@ -1926,6 +2166,51 @@ const docTemplateadmin = `{
} }
} }
}, },
"/admin/login_log/delete": {
"post": {
"security": [
{
"token": []
}
],
"description": "登录日志删除",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"登录日志"
],
"summary": "登录日志删除",
"parameters": [
{
"description": "登录日志信息",
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/model.LoginLog"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/admin/login_log/detail/{id}": { "/admin/login_log/detail/{id}": {
"get": { "get": {
"security": [ "security": [
@@ -2757,6 +3042,162 @@ const docTemplateadmin = `{
} }
} }
}, },
"/admin/my/login_log/batchDelete": {
"post": {
"security": [
{
"token": []
}
],
"description": "登录日志批量删除",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"我的登录日志"
],
"summary": "登录日志批量删除",
"parameters": [
{
"description": "登录日志",
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/admin.LoginLogIds"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/admin/my/login_log/delete": {
"post": {
"security": [
{
"token": []
}
],
"description": "登录日志删除",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"我的登录日志"
],
"summary": "登录日志删除",
"parameters": [
{
"description": "登录日志信息",
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/model.LoginLog"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/admin/my/login_log/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"
},
{
"type": "integer",
"description": "用户ID",
"name": "user_id",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/response.Response"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/model.LoginLogList"
}
}
}
]
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/admin/my/peer/list": { "/admin/my/peer/list": {
"get": { "get": {
"security": [ "security": [
@@ -3884,40 +4325,6 @@ const docTemplateadmin = `{
} }
} }
}, },
"/admin/server-config": {
"get": {
"security": [
{
"token": []
}
],
"description": "服务配置,给webclient提供api-server",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"ADMIN"
],
"summary": "RUSTDESK服务配置",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/admin/share_record/batchDelete": { "/admin/share_record/batchDelete": {
"post": { "post": {
"security": [ "security": [
@@ -4971,27 +5378,6 @@ const docTemplateadmin = `{
} }
}, },
"definitions": { "definitions": {
"Gwen_http_request_admin.Login": {
"type": "object",
"required": [
"password",
"username"
],
"properties": {
"captcha": {
"type": "string"
},
"password": {
"type": "string"
},
"platform": {
"type": "string"
},
"username": {
"type": "string"
}
}
},
"admin.AddressBookForm": { "admin.AddressBookForm": {
"type": "object", "type": "object",
"required": [ "required": [
@@ -5107,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": [
@@ -5194,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"
}, },
@@ -5222,6 +5628,9 @@ const docTemplateadmin = `{
"cpu": { "cpu": {
"type": "string" "type": "string"
}, },
"group_id": {
"type": "integer"
},
"hostname": { "hostname": {
"type": "string" "type": "string"
}, },
@@ -5409,6 +5818,27 @@ const docTemplateadmin = `{
} }
} }
}, },
"github_com_lejianwen_rustdesk-api_v2_http_request_admin.Login": {
"type": "object",
"required": [
"password",
"username"
],
"properties": {
"captcha": {
"type": "string"
},
"password": {
"type": "string"
},
"platform": {
"type": "string"
},
"username": {
"type": "string"
}
}
},
"model.AddressBook": { "model.AddressBook": {
"type": "object", "type": "object",
"properties": { "properties": {
@@ -5709,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": {
@@ -5768,6 +6215,9 @@ const docTemplateadmin = `{
"ip": { "ip": {
"type": "string" "type": "string"
}, },
"is_deleted": {
"type": "integer"
},
"platform": { "platform": {
"description": "windows,linux,mac,android,ios", "description": "windows,linux,mac,android,ios",
"type": "string" "type": "string"
@@ -5837,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"
}, },
@@ -5877,6 +6333,9 @@ const docTemplateadmin = `{
"created_at": { "created_at": {
"type": "string" "type": "string"
}, },
"group_id": {
"type": "integer"
},
"hostname": { "hostname": {
"type": "string" "type": "string"
}, },

View File

@@ -974,40 +974,6 @@
} }
} }
}, },
"/admin/app-config": {
"get": {
"security": [
{
"token": []
}
],
"description": "APP服务配置",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"ADMIN"
],
"summary": "APP服务配置",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/admin/audit_conn/batchDelete": { "/admin/audit_conn/batchDelete": {
"post": { "post": {
"security": [ "security": [
@@ -1434,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": [
@@ -1810,7 +2050,7 @@
"in": "body", "in": "body",
"required": true, "required": true,
"schema": { "schema": {
"$ref": "#/definitions/Gwen_http_request_admin.Login" "$ref": "#/definitions/github_com_lejianwen_rustdesk-api_v2_http_request_admin.Login"
} }
} }
], ],
@@ -1874,7 +2114,7 @@
} }
} }
}, },
"/admin/login_log/delete": { "/admin/login_log/batchDelete": {
"post": { "post": {
"security": [ "security": [
{ {
@@ -1919,6 +2159,51 @@
} }
} }
}, },
"/admin/login_log/delete": {
"post": {
"security": [
{
"token": []
}
],
"description": "登录日志删除",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"登录日志"
],
"summary": "登录日志删除",
"parameters": [
{
"description": "登录日志信息",
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/model.LoginLog"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/admin/login_log/detail/{id}": { "/admin/login_log/detail/{id}": {
"get": { "get": {
"security": [ "security": [
@@ -2750,6 +3035,162 @@
} }
} }
}, },
"/admin/my/login_log/batchDelete": {
"post": {
"security": [
{
"token": []
}
],
"description": "登录日志批量删除",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"我的登录日志"
],
"summary": "登录日志批量删除",
"parameters": [
{
"description": "登录日志",
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/admin.LoginLogIds"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/admin/my/login_log/delete": {
"post": {
"security": [
{
"token": []
}
],
"description": "登录日志删除",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"我的登录日志"
],
"summary": "登录日志删除",
"parameters": [
{
"description": "登录日志信息",
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/model.LoginLog"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/admin/my/login_log/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"
},
{
"type": "integer",
"description": "用户ID",
"name": "user_id",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/response.Response"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/model.LoginLogList"
}
}
}
]
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/admin/my/peer/list": { "/admin/my/peer/list": {
"get": { "get": {
"security": [ "security": [
@@ -3877,40 +4318,6 @@
} }
} }
}, },
"/admin/server-config": {
"get": {
"security": [
{
"token": []
}
],
"description": "服务配置,给webclient提供api-server",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"ADMIN"
],
"summary": "RUSTDESK服务配置",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/admin/share_record/batchDelete": { "/admin/share_record/batchDelete": {
"post": { "post": {
"security": [ "security": [
@@ -4964,27 +5371,6 @@
} }
}, },
"definitions": { "definitions": {
"Gwen_http_request_admin.Login": {
"type": "object",
"required": [
"password",
"username"
],
"properties": {
"captcha": {
"type": "string"
},
"password": {
"type": "string"
},
"platform": {
"type": "string"
},
"username": {
"type": "string"
}
}
},
"admin.AddressBookForm": { "admin.AddressBookForm": {
"type": "object", "type": "object",
"required": [ "required": [
@@ -5100,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": [
@@ -5187,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"
}, },
@@ -5215,6 +5621,9 @@
"cpu": { "cpu": {
"type": "string" "type": "string"
}, },
"group_id": {
"type": "integer"
},
"hostname": { "hostname": {
"type": "string" "type": "string"
}, },
@@ -5402,6 +5811,27 @@
} }
} }
}, },
"github_com_lejianwen_rustdesk-api_v2_http_request_admin.Login": {
"type": "object",
"required": [
"password",
"username"
],
"properties": {
"captcha": {
"type": "string"
},
"password": {
"type": "string"
},
"platform": {
"type": "string"
},
"username": {
"type": "string"
}
}
},
"model.AddressBook": { "model.AddressBook": {
"type": "object", "type": "object",
"properties": { "properties": {
@@ -5702,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": {
@@ -5761,6 +6208,9 @@
"ip": { "ip": {
"type": "string" "type": "string"
}, },
"is_deleted": {
"type": "integer"
},
"platform": { "platform": {
"description": "windows,linux,mac,android,ios", "description": "windows,linux,mac,android,ios",
"type": "string" "type": "string"
@@ -5830,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"
}, },
@@ -5870,6 +6326,9 @@
"created_at": { "created_at": {
"type": "string" "type": "string"
}, },
"group_id": {
"type": "integer"
},
"hostname": { "hostname": {
"type": "string" "type": "string"
}, },

View File

@@ -1,19 +1,5 @@
basePath: /api basePath: /api
definitions: definitions:
Gwen_http_request_admin.Login:
properties:
captcha:
type: string
password:
type: string
platform:
type: string
username:
type: string
required:
- password
- username
type: object
admin.AddressBookForm: admin.AddressBookForm:
properties: properties:
alias: alias:
@@ -91,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:
@@ -144,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:
@@ -167,6 +166,8 @@ definitions:
properties: properties:
cpu: cpu:
type: string type: string
group_id:
type: integer
hostname: hostname:
type: string type: string
id: id:
@@ -292,6 +293,20 @@ definitions:
required: required:
- ids - ids
type: object type: object
github_com_lejianwen_rustdesk-api_v2_http_request_admin.Login:
properties:
captcha:
type: string
password:
type: string
platform:
type: string
username:
type: string
required:
- password
- username
type: object
model.AddressBook: model.AddressBook:
properties: properties:
alias: alias:
@@ -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:
@@ -531,6 +557,8 @@ definitions:
type: integer type: integer
ip: ip:
type: string type: string
is_deleted:
type: integer
platform: platform:
description: windows,linux,mac,android,ios description: windows,linux,mac,android,ios
type: string type: string
@@ -577,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:
@@ -603,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:
@@ -1347,27 +1381,6 @@ paths:
summary: 地址簿规则编辑 summary: 地址簿规则编辑
tags: tags:
- 地址簿规则 - 地址簿规则
/admin/app-config:
get:
consumes:
- application/json
description: APP服务配置
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: APP服务配置
tags:
- ADMIN
/admin/audit_conn/batchDelete: /admin/audit_conn/batchDelete:
post: post:
consumes: consumes:
@@ -1629,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:
@@ -1849,7 +2023,7 @@ paths:
name: body name: body
required: true required: true
schema: schema:
$ref: '#/definitions/Gwen_http_request_admin.Login' $ref: '#/definitions/github_com_lejianwen_rustdesk-api_v2_http_request_admin.Login'
produces: produces:
- application/json - application/json
responses: responses:
@@ -1892,7 +2066,7 @@ paths:
summary: 登录选项 summary: 登录选项
tags: tags:
- 登录 - 登录
/admin/login_log/delete: /admin/login_log/batchDelete:
post: post:
consumes: consumes:
- application/json - application/json
@@ -1920,6 +2094,34 @@ paths:
summary: 登录日志批量删除 summary: 登录日志批量删除
tags: tags:
- 登录日志 - 登录日志
/admin/login_log/delete:
post:
consumes:
- application/json
description: 登录日志删除
parameters:
- description: 登录日志信息
in: body
name: body
required: true
schema:
$ref: '#/definitions/model.LoginLog'
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/login_log/detail/{id}: /admin/login_log/detail/{id}:
get: get:
consumes: consumes:
@@ -2413,6 +2615,101 @@ paths:
summary: 地址簿规则编辑 summary: 地址簿规则编辑
tags: tags:
- 我的地址簿规则 - 我的地址簿规则
/admin/my/login_log/batchDelete:
post:
consumes:
- application/json
description: 登录日志批量删除
parameters:
- description: 登录日志
in: body
name: body
required: true
schema:
$ref: '#/definitions/admin.LoginLogIds'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/response.Response'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.Response'
security:
- token: []
summary: 登录日志批量删除
tags:
- 我的登录日志
/admin/my/login_log/delete:
post:
consumes:
- application/json
description: 登录日志删除
parameters:
- description: 登录日志信息
in: body
name: body
required: true
schema:
$ref: '#/definitions/model.LoginLog'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/response.Response'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.Response'
security:
- token: []
summary: 登录日志删除
tags:
- 我的登录日志
/admin/my/login_log/list:
get:
consumes:
- application/json
description: 登录日志列表
parameters:
- description: 页码
in: query
name: page
type: integer
- description: 页大小
in: query
name: page_size
type: integer
- description: 用户ID
in: query
name: user_id
type: integer
produces:
- application/json
responses:
"200":
description: OK
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
data:
$ref: '#/definitions/model.LoginLogList'
type: object
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.Response'
security:
- token: []
summary: 登录日志列表
tags:
- 我的登录日志
/admin/my/peer/list: /admin/my/peer/list:
get: get:
consumes: consumes:
@@ -3088,27 +3385,6 @@ paths:
summary: 设备编辑 summary: 设备编辑
tags: tags:
- 设备 - 设备
/admin/server-config:
get:
consumes:
- application/json
description: 服务配置,给webclient提供api-server
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/response.Response'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.Response'
security:
- token: []
summary: RUSTDESK服务配置
tags:
- ADMIN
/admin/share_record/batchDelete: /admin/share_record/batchDelete:
post: post:
consumes: consumes:

View File

@@ -653,40 +653,6 @@ const docTemplateapi = `{
} }
} }
}, },
"/api": {
"get": {
"security": [
{
"token": []
}
],
"description": "用户信息",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"用户"
],
"summary": "用户信息",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/api.UserPayload"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/audit/conn": { "/audit/conn": {
"post": { "post": {
"description": "审计连接", "description": "审计连接",
@@ -767,6 +733,100 @@ const docTemplateapi = `{
} }
} }
}, },
"/currentUser": {
"get": {
"security": [
{
"token": []
}
],
"description": "用户信息",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"用户"
],
"summary": "用户信息",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/api.UserPayload"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/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": "心跳",
@@ -1178,43 +1238,6 @@ const docTemplateapi = `{
} }
} }
}, },
"/tags": {
"post": {
"security": [
{
"BearerAuth": []
}
],
"description": "标签",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"地址"
],
"summary": "标签",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/model.Tag"
}
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.ErrorResponse"
}
}
}
}
},
"/users": { "/users": {
"get": { "get": {
"security": [ "security": [
@@ -1289,6 +1312,35 @@ const docTemplateapi = `{
} }
} }
} }
},
"/version": {
"get": {
"description": "版本",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"首页"
],
"summary": "版本",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
} }
}, },
"definitions": { "definitions": {

View File

@@ -646,40 +646,6 @@
} }
} }
}, },
"/api": {
"get": {
"security": [
{
"token": []
}
],
"description": "用户信息",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"用户"
],
"summary": "用户信息",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/api.UserPayload"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/audit/conn": { "/audit/conn": {
"post": { "post": {
"description": "审计连接", "description": "审计连接",
@@ -760,6 +726,100 @@
} }
} }
}, },
"/currentUser": {
"get": {
"security": [
{
"token": []
}
],
"description": "用户信息",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"用户"
],
"summary": "用户信息",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/api.UserPayload"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/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": "心跳",
@@ -1171,43 +1231,6 @@
} }
} }
}, },
"/tags": {
"post": {
"security": [
{
"BearerAuth": []
}
],
"description": "标签",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"地址"
],
"summary": "标签",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/model.Tag"
}
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.ErrorResponse"
}
}
}
}
},
"/users": { "/users": {
"get": { "get": {
"security": [ "security": [
@@ -1282,6 +1305,35 @@
} }
} }
} }
},
"/version": {
"get": {
"description": "版本",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"首页"
],
"summary": "版本",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
} }
}, },
"definitions": { "definitions": {

View File

@@ -598,27 +598,6 @@ paths:
summary: 标签 summary: 标签
tags: tags:
- 地址[Personal] - 地址[Personal]
/api:
get:
consumes:
- application/json
description: 用户信息
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/api.UserPayload'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.Response'
security:
- token: []
summary: 用户信息
tags:
- 用户
/audit/conn: /audit/conn:
post: post:
consumes: consumes:
@@ -671,6 +650,65 @@ paths:
summary: 审计文件 summary: 审计文件
tags: tags:
- 审计 - 审计
/currentUser:
get:
consumes:
- application/json
description: 用户信息
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/api.UserPayload'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.Response'
security:
- token: []
summary: 用户信息
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:
@@ -936,29 +974,6 @@ paths:
summary: 提交系统信息 summary: 提交系统信息
tags: tags:
- 地址 - 地址
/tags:
post:
consumes:
- application/json
description: 标签
produces:
- application/json
responses:
"200":
description: OK
schema:
items:
$ref: '#/definitions/model.Tag'
type: array
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.ErrorResponse'
security:
- BearerAuth: []
summary: 标签
tags:
- 地址
/users: /users:
get: get:
consumes: consumes:
@@ -1004,6 +1019,25 @@ paths:
summary: 用户列表 summary: 用户列表
tags: tags:
- 群组 - 群组
/version:
get:
consumes:
- application/json
description: 版本
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/response.Response'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.Response'
summary: 版本
tags:
- 首页
securityDefinitions: securityDefinitions:
BearerAuth: BearerAuth:
in: header in: header

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 67 KiB

BIN
docs/init_admin_pwd.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 67 KiB

View File

@@ -1,4 +1,4 @@
package Gwen package main
//go:generate swag init -g cmd/apimain.go --output docs/api --instanceName api --exclude http/controller/admin //go:generate swag init -g cmd/apimain.go --output docs/api --instanceName api --exclude http/controller/admin
//go:generate swag init -g cmd/apimain.go --output docs/admin --instanceName admin --exclude http/controller/api //go:generate swag init -g cmd/apimain.go --output docs/admin --instanceName admin --exclude http/controller/api

View File

@@ -1,3 +1,3 @@
package Gwen package main
//go:generate go run cmd/apimain.go //go:generate go run cmd/apimain.go

View File

@@ -4,18 +4,20 @@ import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/go-playground/locales/en" "github.com/go-playground/locales/en"
"github.com/go-playground/locales/es" "github.com/go-playground/locales/es"
"github.com/go-playground/locales/fr"
"github.com/go-playground/locales/ko" "github.com/go-playground/locales/ko"
"github.com/go-playground/locales/ru" "github.com/go-playground/locales/ru"
"github.com/go-playground/locales/fr"
"github.com/go-playground/locales/zh_Hans_CN" "github.com/go-playground/locales/zh_Hans_CN"
"github.com/go-playground/locales/zh_Hant"
ut "github.com/go-playground/universal-translator" ut "github.com/go-playground/universal-translator"
"github.com/go-playground/validator/v10" "github.com/go-playground/validator/v10"
en_translations "github.com/go-playground/validator/v10/translations/en" en_translations "github.com/go-playground/validator/v10/translations/en"
es_translations "github.com/go-playground/validator/v10/translations/es" es_translations "github.com/go-playground/validator/v10/translations/es"
fr_translations "github.com/go-playground/validator/v10/translations/fr"
ko_translations "github.com/go-playground/validator/v10/translations/ko"
ru_translations "github.com/go-playground/validator/v10/translations/ru" ru_translations "github.com/go-playground/validator/v10/translations/ru"
zh_translations "github.com/go-playground/validator/v10/translations/zh" zh_translations "github.com/go-playground/validator/v10/translations/zh"
fr_translations "github.com/go-playground/validator/v10/translations/fr" zh_tw_translations "github.com/go-playground/validator/v10/translations/zh_tw"
"reflect" "reflect"
) )
@@ -29,8 +31,9 @@ func ApiInitValidator() {
ruT := ru.New() ruT := ru.New()
esT := es.New() esT := es.New()
frT := fr.New() frT := fr.New()
zhTwT := zh_Hant.New()
uni := ut.New(enT, cn, koT, ruT, esT, frT) uni := ut.New(enT, cn, koT, ruT, esT, frT, zhTwT)
enTrans, _ := uni.GetTranslator("en") enTrans, _ := uni.GetTranslator("en")
zhTrans, _ := uni.GetTranslator("zh_Hans_CN") zhTrans, _ := uni.GetTranslator("zh_Hans_CN")
@@ -38,6 +41,7 @@ func ApiInitValidator() {
ruTrans, _ := uni.GetTranslator("ru") ruTrans, _ := uni.GetTranslator("ru")
esTrans, _ := uni.GetTranslator("es") esTrans, _ := uni.GetTranslator("es")
frTrans, _ := uni.GetTranslator("fr") frTrans, _ := uni.GetTranslator("fr")
zhTwTrans, _ := uni.GetTranslator("zh_Hant")
err := zh_translations.RegisterDefaultTranslations(validate, zhTrans) err := zh_translations.RegisterDefaultTranslations(validate, zhTrans)
if err != nil { if err != nil {
@@ -48,8 +52,7 @@ func ApiInitValidator() {
panic(err) panic(err)
} }
//validate没有ko的翻译使用zh的翻译 err = ko_translations.RegisterDefaultTranslations(validate, koTrans)
err = zh_translations.RegisterDefaultTranslations(validate, koTrans)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@@ -65,6 +68,10 @@ func ApiInitValidator() {
if err != nil { if err != nil {
panic(err) panic(err)
} }
err = zh_tw_translations.RegisterDefaultTranslations(validate, zhTwTrans)
if err != nil {
panic(err)
}
validate.RegisterTagNameFunc(func(field reflect.StructField) string { validate.RegisterTagNameFunc(func(field reflect.StructField) string {
label := field.Tag.Get("label") label := field.Tag.Get("label")
@@ -125,6 +132,13 @@ func getTranslatorForLang(lang string) ut.Translator {
case "zh": case "zh":
trans, _ := Validator.UT.GetTranslator("zh_Hans_CN") trans, _ := Validator.UT.GetTranslator("zh_Hans_CN")
return trans return trans
case "zh_TW":
fallthrough
case "zh-TW":
fallthrough
case "zh-tw":
trans, _ := Validator.UT.GetTranslator("zh_Hant")
return trans
case "ko": case "ko":
trans, _ := Validator.UT.GetTranslator("ko") trans, _ := Validator.UT.GetTranslator("ko")
return trans return trans

View File

@@ -1,15 +1,15 @@
package global package global
import ( import (
"Gwen/config"
"Gwen/lib/cache"
"Gwen/lib/jwt"
"Gwen/lib/lock"
"Gwen/lib/upload"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
ut "github.com/go-playground/universal-translator" ut "github.com/go-playground/universal-translator"
"github.com/go-playground/validator/v10" "github.com/go-playground/validator/v10"
"github.com/go-redis/redis/v8" "github.com/go-redis/redis/v8"
"github.com/lejianwen/rustdesk-api/v2/config"
"github.com/lejianwen/rustdesk-api/v2/lib/cache"
"github.com/lejianwen/rustdesk-api/v2/lib/jwt"
"github.com/lejianwen/rustdesk-api/v2/lib/lock"
"github.com/lejianwen/rustdesk-api/v2/lib/upload"
"github.com/nicksnyder/go-i18n/v2/i18n" "github.com/nicksnyder/go-i18n/v2/i18n"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/spf13/viper" "github.com/spf13/viper"

View File

@@ -15,7 +15,6 @@ func InitI18n() {
fileInfos, err := os.ReadDir(dir) fileInfos, err := os.ReadDir(dir)
if err != nil { if err != nil {
panic(err) panic(err)
return
} }
for _, fileInfo := range fileInfos { for _, fileInfo := range fileInfos {
//如果文件名不是.toml结尾 //如果文件名不是.toml结尾

22
go.mod
View File

@@ -1,4 +1,4 @@
module Gwen module github.com/lejianwen/rustdesk-api/v2
go 1.22 go 1.22
@@ -10,10 +10,10 @@ require (
github.com/gin-gonic/gin v1.9.0 github.com/gin-gonic/gin v1.9.0
github.com/go-playground/locales v0.14.1 github.com/go-playground/locales v0.14.1
github.com/go-playground/universal-translator v0.18.1 github.com/go-playground/universal-translator v0.18.1
github.com/go-playground/validator/v10 v10.11.2 github.com/go-playground/validator/v10 v10.26.0
github.com/go-redis/redis/v8 v8.11.4 github.com/go-redis/redis/v8 v8.11.4
github.com/golang-jwt/jwt/v5 v5.2.1 github.com/golang-jwt/jwt/v5 v5.2.1
github.com/google/uuid v1.1.2 github.com/google/uuid v1.6.0
github.com/nicksnyder/go-i18n/v2 v2.4.0 github.com/nicksnyder/go-i18n/v2 v2.4.0
github.com/sirupsen/logrus v1.8.1 github.com/sirupsen/logrus v1.8.1
github.com/spf13/cobra v1.8.1 github.com/spf13/cobra v1.8.1
@@ -22,21 +22,27 @@ require (
github.com/swaggo/gin-swagger v1.6.0 github.com/swaggo/gin-swagger v1.6.0
github.com/swaggo/swag v1.16.3 github.com/swaggo/swag v1.16.3
golang.org/x/oauth2 v0.23.0 golang.org/x/oauth2 v0.23.0
golang.org/x/text v0.18.0 golang.org/x/text v0.22.0
gorm.io/driver/mysql v1.5.7 gorm.io/driver/mysql v1.5.7
gorm.io/driver/sqlite v1.5.6 gorm.io/driver/sqlite v1.5.6
gorm.io/gorm v1.25.7 gorm.io/gorm v1.25.7
) )
require ( require (
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
github.com/KyleBanks/depth v1.2.1 // indirect github.com/KyleBanks/depth v1.2.1 // indirect
github.com/PuerkitoBio/purell v1.1.1 // indirect github.com/PuerkitoBio/purell v1.1.1 // indirect
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
github.com/bytedance/sonic v1.8.0 // indirect github.com/bytedance/sonic v1.8.0 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
github.com/coreos/go-oidc/v3 v3.12.0 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-asn1-ber/asn1-ber v1.5.7 // indirect
github.com/go-jose/go-jose/v4 v4.0.2 // 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
github.com/go-openapi/spec v0.20.4 // indirect github.com/go-openapi/spec v0.20.4 // indirect
@@ -51,7 +57,7 @@ require (
github.com/josharian/intern v1.0.0 // indirect github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.0.9 // indirect github.com/klauspost/cpuid/v2 v2.0.9 // indirect
github.com/leodido/go-urn v1.2.1 // indirect github.com/leodido/go-urn v1.4.0 // indirect
github.com/magiconair/properties v1.8.5 // indirect github.com/magiconair/properties v1.8.5 // indirect
github.com/mailru/easyjson v0.7.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect github.com/mattn/go-isatty v0.0.17 // indirect
@@ -70,10 +76,10 @@ require (
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.9 // indirect github.com/ugorji/go/codec v1.2.9 // indirect
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
golang.org/x/crypto v0.23.0 // indirect golang.org/x/crypto v0.33.0 // indirect
golang.org/x/image v0.13.0 // indirect golang.org/x/image v0.13.0 // indirect
golang.org/x/net v0.25.0 // indirect golang.org/x/net v0.34.0 // indirect
golang.org/x/sys v0.25.0 // indirect golang.org/x/sys v0.30.0 // indirect
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
google.golang.org/protobuf v1.33.0 // indirect google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/ini.v1 v1.63.2 // indirect gopkg.in/ini.v1 v1.63.2 // indirect

View File

@@ -1,13 +1,13 @@
package admin package admin
import ( import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/service"
"encoding/json" "encoding/json"
_ "encoding/json" _ "encoding/json"
"github.com/gin-gonic/gin" "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"
"gorm.io/gorm" "gorm.io/gorm"
"strconv" "strconv"
) )

View File

@@ -1,12 +1,12 @@
package admin package admin
import ( import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/model"
"Gwen/service"
"github.com/gin-gonic/gin" "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/model"
"github.com/lejianwen/rustdesk-api/v2/service"
"gorm.io/gorm" "gorm.io/gorm"
"strconv" "strconv"
) )

View File

@@ -1,12 +1,12 @@
package admin package admin
import ( import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/model"
"Gwen/service"
"github.com/gin-gonic/gin" "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/model"
"github.com/lejianwen/rustdesk-api/v2/service"
"gorm.io/gorm" "gorm.io/gorm"
"strconv" "strconv"
) )

View File

@@ -1,12 +1,12 @@
package admin package admin
import ( import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/model"
"Gwen/service"
"github.com/gin-gonic/gin" "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/model"
"github.com/lejianwen/rustdesk-api/v2/service"
"gorm.io/gorm" "gorm.io/gorm"
) )

View File

@@ -1,10 +1,11 @@
package admin package admin
import ( import (
"Gwen/global"
"Gwen/http/response"
"Gwen/service"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/model"
"github.com/lejianwen/rustdesk-api/v2/service"
"os" "os"
"strings" "strings"
) )
@@ -60,7 +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{}
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{
"title": global.Config.Admin.Title,
})
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 != "" {

View 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"))
}

View File

@@ -1,11 +1,11 @@
package admin package admin
import ( import (
"Gwen/global"
"Gwen/http/response"
"Gwen/lib/upload"
"fmt" "fmt"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/lib/upload"
"os" "os"
"time" "time"
) )

View File

@@ -1,11 +1,11 @@
package admin package admin
import ( import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/service"
"github.com/gin-gonic/gin" "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" "strconv"
) )

View File

@@ -1,16 +1,16 @@
package admin package admin
import ( import (
"Gwen/global"
"Gwen/http/controller/api"
"Gwen/http/request/admin"
apiReq "Gwen/http/request/api"
"Gwen/http/response"
adResp "Gwen/http/response/admin"
"Gwen/model"
"Gwen/service"
"fmt" "fmt"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/controller/api"
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
apiReq "github.com/lejianwen/rustdesk-api/v2/http/request/api"
"github.com/lejianwen/rustdesk-api/v2/http/response"
adResp "github.com/lejianwen/rustdesk-api/v2/http/response/admin"
"github.com/lejianwen/rustdesk-api/v2/model"
"github.com/lejianwen/rustdesk-api/v2/service"
"github.com/mojocn/base64Captcha" "github.com/mojocn/base64Captcha"
"sync" "sync"
"time" "time"
@@ -152,6 +152,10 @@ var loginLimiter = NewLoginLimiter(3, 5*time.Minute)
// @Router /admin/login [post] // @Router /admin/login [post]
// @Security token // @Security token
func (ct *Login) Login(c *gin.Context) { func (ct *Login) Login(c *gin.Context) {
if global.Config.App.DisablePwdLogin {
response.Fail(c, 101, response.TranslateMsg(c, "PwdLoginDisabled"))
return
}
f := &admin.Login{} f := &admin.Login{}
err := c.ShouldBindJSON(f) err := c.ShouldBindJSON(f)
clientIp := c.ClientIP() clientIp := c.ClientIP()
@@ -182,15 +186,20 @@ func (ct *Login) Login(c *gin.Context) {
global.Logger.Warn(fmt.Sprintf("Login Fail: %s %s %s", "UsernameOrPasswordError", c.RemoteIP(), clientIp)) global.Logger.Warn(fmt.Sprintf("Login Fail: %s %s %s", "UsernameOrPasswordError", c.RemoteIP(), clientIp))
loginLimiter.RecordFailure(clientIp) loginLimiter.RecordFailure(clientIp)
if loginLimiter.NeedsCaptcha(clientIp) { if loginLimiter.NeedsCaptcha(clientIp) {
// 移除原验证码,重新生成
loginLimiter.RemoveCaptcha(clientIp) loginLimiter.RemoveCaptcha(clientIp)
response.Fail(c, 110, response.TranslateMsg(c, "UsernameOrPasswordError"))
return
} }
response.Fail(c, 101, response.TranslateMsg(c, "UsernameOrPasswordError")) response.Fail(c, 101, response.TranslateMsg(c, "UsernameOrPasswordError"))
return return
} }
if !service.AllService.UserService.CheckUserEnable(u) {
if loginLimiter.NeedsCaptcha(clientIp) {
loginLimiter.RemoveCaptcha(clientIp)
}
response.Fail(c, 101, response.TranslateMsg(c, "UserDisabled"))
return
}
ut := service.AllService.UserService.Login(u, &model.LoginLog{ ut := service.AllService.UserService.Login(u, &model.LoginLog{
UserId: u.Id, UserId: u.Id,
Client: model.LoginLogClientWebAdmin, Client: model.LoginLogClientWebAdmin,
@@ -274,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,
@@ -288,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,
}) })
} }

View File

@@ -1,12 +1,12 @@
package admin package admin
import ( import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/model"
"Gwen/service"
"github.com/gin-gonic/gin" "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/model"
"github.com/lejianwen/rustdesk-api/v2/service"
"gorm.io/gorm" "gorm.io/gorm"
"strconv" "strconv"
) )

View File

@@ -1,12 +1,12 @@
package my package my
import ( import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/service"
"encoding/json" "encoding/json"
"github.com/gin-gonic/gin" "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"
"gorm.io/gorm" "gorm.io/gorm"
) )

View File

@@ -1,12 +1,12 @@
package my package my
import ( import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/model"
"Gwen/service"
"github.com/gin-gonic/gin" "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/model"
"github.com/lejianwen/rustdesk-api/v2/service"
"gorm.io/gorm" "gorm.io/gorm"
) )

View File

@@ -1,12 +1,12 @@
package my package my
import ( import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/model"
"Gwen/service"
"github.com/gin-gonic/gin" "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/model"
"github.com/lejianwen/rustdesk-api/v2/service"
"gorm.io/gorm" "gorm.io/gorm"
) )

View File

@@ -1,12 +1,12 @@
package my package my
import ( import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/model"
"Gwen/service"
"github.com/gin-gonic/gin" "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/model"
"github.com/lejianwen/rustdesk-api/v2/service"
"gorm.io/gorm" "gorm.io/gorm"
) )

View File

@@ -1,10 +1,10 @@
package my package my
import ( import (
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/service"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/service"
"gorm.io/gorm" "gorm.io/gorm"
"time" "time"
) )

View File

@@ -1,11 +1,11 @@
package my package my
import ( import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/service"
"github.com/gin-gonic/gin" "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"
"gorm.io/gorm" "gorm.io/gorm"
) )

View File

@@ -1,11 +1,11 @@
package my package my
import ( import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/service"
"github.com/gin-gonic/gin" "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"
"gorm.io/gorm" "gorm.io/gorm"
) )

View File

@@ -1,12 +1,12 @@
package admin package admin
import ( import (
"Gwen/global"
"Gwen/http/request/admin"
adminReq "Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/service"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
adminReq "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" "strconv"
) )
@@ -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,
}) })
} }

View File

@@ -1,11 +1,11 @@
package admin package admin
import ( import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/service"
"github.com/gin-gonic/gin" "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"
"gorm.io/gorm" "gorm.io/gorm"
"strconv" "strconv"
"time" "time"
@@ -108,6 +108,12 @@ func (ct *Peer) List(c *gin.Context) {
if query.Uuids != "" { if query.Uuids != "" {
tx.Where("uuid in (?)", query.Uuids) tx.Where("uuid in (?)", query.Uuids)
} }
if query.Username != "" {
tx.Where("username like ?", "%"+query.Username+"%")
}
if query.Ip != "" {
tx.Where("last_online_ip like ?", "%"+query.Ip+"%")
}
}) })
response.Success(c, res) response.Success(c, res)
} }

View File

@@ -1,12 +1,12 @@
package admin package admin
import ( import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/model"
"Gwen/service"
"github.com/gin-gonic/gin" "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/model"
"github.com/lejianwen/rustdesk-api/v2/service"
) )
type Rustdesk struct { type Rustdesk struct {

View File

@@ -1,11 +1,11 @@
package admin package admin
import ( import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/service"
"github.com/gin-gonic/gin" "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"
"gorm.io/gorm" "gorm.io/gorm"
) )

View File

@@ -1,11 +1,11 @@
package admin package admin
import ( import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/service"
"github.com/gin-gonic/gin" "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"
"gorm.io/gorm" "gorm.io/gorm"
"strconv" "strconv"
) )

View File

@@ -1,13 +1,13 @@
package admin package admin
import ( import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
adResp "Gwen/http/response/admin"
"Gwen/model"
"Gwen/service"
"github.com/gin-gonic/gin" "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"
adResp "github.com/lejianwen/rustdesk-api/v2/http/response/admin"
"github.com/lejianwen/rustdesk-api/v2/model"
"github.com/lejianwen/rustdesk-api/v2/service"
"gorm.io/gorm" "gorm.io/gorm"
"strconv" "strconv"
) )

View File

@@ -1,12 +1,12 @@
package admin package admin
import ( import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/model"
"Gwen/service"
"github.com/gin-gonic/gin" "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/model"
"github.com/lejianwen/rustdesk-api/v2/service"
"gorm.io/gorm" "gorm.io/gorm"
) )

View File

@@ -1,16 +1,16 @@
package api package api
import ( import (
"Gwen/global"
requstform "Gwen/http/request/api"
"Gwen/http/response"
"Gwen/http/response/api"
"Gwen/model"
"Gwen/service"
"Gwen/utils"
"encoding/json" "encoding/json"
"errors" "errors"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
requstform "github.com/lejianwen/rustdesk-api/v2/http/request/api"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/http/response/api"
"github.com/lejianwen/rustdesk-api/v2/model"
"github.com/lejianwen/rustdesk-api/v2/service"
"github.com/lejianwen/rustdesk-api/v2/utils"
"net/http" "net/http"
"strconv" "strconv"
"strings" "strings"

View File

@@ -1,12 +1,12 @@
package api package api
import ( import (
request "Gwen/http/request/api"
"Gwen/http/response"
"Gwen/model"
"Gwen/service"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding" "github.com/gin-gonic/gin/binding"
request "github.com/lejianwen/rustdesk-api/v2/http/request/api"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/model"
"github.com/lejianwen/rustdesk-api/v2/service"
"time" "time"
) )

View File

@@ -1,12 +1,12 @@
package api package api
import ( import (
apiReq "Gwen/http/request/api"
"Gwen/http/response"
apiResp "Gwen/http/response/api"
"Gwen/model"
"Gwen/service"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
apiReq "github.com/lejianwen/rustdesk-api/v2/http/request/api"
"github.com/lejianwen/rustdesk-api/v2/http/response"
apiResp "github.com/lejianwen/rustdesk-api/v2/http/response/api"
"github.com/lejianwen/rustdesk-api/v2/model"
"github.com/lejianwen/rustdesk-api/v2/service"
"net/http" "net/http"
) )
@@ -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,
})
}

View File

@@ -1,11 +1,11 @@
package api package api
import ( import (
requstform "Gwen/http/request/api"
"Gwen/http/response"
"Gwen/model"
"Gwen/service"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
requstform "github.com/lejianwen/rustdesk-api/v2/http/request/api"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/model"
"github.com/lejianwen/rustdesk-api/v2/service"
"net/http" "net/http"
"time" "time"
) )
@@ -55,9 +55,27 @@ func (i *Index) Heartbeat(c *gin.Context) {
return return
} }
//如果在40s以内则不更新 //如果在40s以内则不更新
if time.Now().Unix()-peer.LastOnlineTime > 40 { if time.Now().Unix()-peer.LastOnlineTime >= 30 {
upp := &model.Peer{RowId: peer.RowId, LastOnlineTime: time.Now().Unix(), LastOnlineIp: c.ClientIP()} upp := &model.Peer{RowId: peer.RowId, LastOnlineTime: time.Now().Unix(), LastOnlineIp: c.ClientIP()}
service.AllService.PeerService.Update(upp) service.AllService.PeerService.Update(upp)
} }
c.JSON(http.StatusOK, gin.H{}) c.JSON(http.StatusOK, gin.H{})
} }
// Version 版本
// @Tags 首页
// @Summary 版本
// @Description 版本
// @Accept json
// @Produce json
// @Success 200 {object} response.Response
// @Failure 500 {object} response.Response
// @Router /version [get]
func (i *Index) Version(c *gin.Context) {
//读取resources/version文件
v := service.AllService.AppService.GetAppVersion()
response.Success(
c,
v,
)
}

View File

@@ -1,15 +1,15 @@
package api package api
import ( import (
"Gwen/global"
"Gwen/http/request/api"
"Gwen/http/response"
apiResp "Gwen/http/response/api"
"Gwen/model"
"Gwen/service"
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/request/api"
"github.com/lejianwen/rustdesk-api/v2/http/response"
apiResp "github.com/lejianwen/rustdesk-api/v2/http/response/api"
"github.com/lejianwen/rustdesk-api/v2/model"
"github.com/lejianwen/rustdesk-api/v2/service"
"net/http" "net/http"
) )
@@ -27,6 +27,10 @@ type Login struct {
// @Failure 500 {object} response.ErrorResponse // @Failure 500 {object} response.ErrorResponse
// @Router /login [post] // @Router /login [post]
func (l *Login) Login(c *gin.Context) { func (l *Login) Login(c *gin.Context) {
if global.Config.App.DisablePwdLogin {
response.Error(c, response.TranslateMsg(c, "PwdLoginDisabled"))
return
}
f := &api.LoginForm{} f := &api.LoginForm{}
err := c.ShouldBindJSON(f) err := c.ShouldBindJSON(f)
//fmt.Println(f) //fmt.Println(f)
@@ -51,6 +55,11 @@ func (l *Login) Login(c *gin.Context) {
return return
} }
if !service.AllService.UserService.CheckUserEnable(u) {
response.Error(c, response.TranslateMsg(c, "UserDisabled"))
return
}
//根据refer判断是webclient还是app //根据refer判断是webclient还是app
ref := c.GetHeader("referer") ref := c.GetHeader("referer")
if ref != "" { if ref != "" {
@@ -85,7 +94,9 @@ func (l *Login) Login(c *gin.Context) {
// @Router /login-options [get] // @Router /login-options [get]
func (l *Login) LoginOptions(c *gin.Context) { func (l *Login) LoginOptions(c *gin.Context) {
ops := service.AllService.OauthService.GetOauthProviders() ops := service.AllService.OauthService.GetOauthProviders()
ops = append(ops, model.OauthTypeWebauth) if global.Config.App.WebSso {
ops = append(ops, model.OauthTypeWebauth)
}
var oidcItems []map[string]string var oidcItems []map[string]string
for _, v := range ops { for _, v := range ops {
oidcItems = append(oidcItems, map[string]string{"name": v}) oidcItems = append(oidcItems, map[string]string{"name": v})

View File

@@ -1,13 +1,13 @@
package api package api
import ( import (
"Gwen/global"
"Gwen/http/request/api"
"Gwen/http/response"
apiResp "Gwen/http/response/api"
"Gwen/model"
"Gwen/service"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/request/api"
"github.com/lejianwen/rustdesk-api/v2/http/response"
apiResp "github.com/lejianwen/rustdesk-api/v2/http/response/api"
"github.com/lejianwen/rustdesk-api/v2/model"
"github.com/lejianwen/rustdesk-api/v2/service"
"net/http" "net/http"
) )
@@ -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
} }

View File

@@ -1,11 +1,11 @@
package api package api
import ( import (
requstform "Gwen/http/request/api"
"Gwen/http/response"
"Gwen/service"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding" "github.com/gin-gonic/gin/binding"
requstform "github.com/lejianwen/rustdesk-api/v2/http/request/api"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/service"
"net/http" "net/http"
) )
@@ -30,7 +30,7 @@ func (p *Peer) SysInfo(c *gin.Context) {
return return
} }
fpe := f.ToPeer() fpe := f.ToPeer()
pe := service.AllService.PeerService.FindById(f.Id) pe := service.AllService.PeerService.FindByUuid(f.Uuid)
if pe.RowId == 0 { if pe.RowId == 0 {
pe = f.ToPeer() pe = f.ToPeer()
pe.UserId = service.AllService.UserService.FindLatestUserIdFromLoginLogByUuid(pe.Uuid) pe.UserId = service.AllService.UserService.FindLatestUserIdFromLoginLogByUuid(pe.Uuid)
@@ -56,3 +56,9 @@ func (p *Peer) SysInfo(c *gin.Context) {
//直接响应文本 //直接响应文本
c.String(http.StatusOK, "SYSINFO_UPDATED") c.String(http.StatusOK, "SYSINFO_UPDATED")
} }
func (p *Peer) SysInfoVer(c *gin.Context) {
//读取resources/version文件
v := service.AllService.AppService.GetAppVersion()
c.String(http.StatusOK, v)
}

View File

@@ -1,9 +1,9 @@
package api package api
import ( import (
apiResp "Gwen/http/response/api"
"Gwen/service"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
apiResp "github.com/lejianwen/rustdesk-api/v2/http/response/api"
"github.com/lejianwen/rustdesk-api/v2/service"
"net/http" "net/http"
) )
@@ -34,7 +34,7 @@ type User struct {
// @Produce json // @Produce json
// @Success 200 {object} apiResp.UserPayload // @Success 200 {object} apiResp.UserPayload
// @Failure 500 {object} response.Response // @Failure 500 {object} response.Response
// @Router /api [get] // @Router /currentUser [get]
// @Security token // @Security token
func (u *User) Info(c *gin.Context) { func (u *User) Info(c *gin.Context) {
user := service.AllService.UserService.CurUser(c) user := service.AllService.UserService.CurUser(c)

View File

@@ -1,11 +1,11 @@
package api package api
import ( import (
"Gwen/global"
"Gwen/http/response"
"Gwen/http/response/api"
"Gwen/service"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/http/response/api"
"github.com/lejianwen/rustdesk-api/v2/service"
"time" "time"
) )

View File

@@ -1,9 +1,9 @@
package web package web
import ( import (
"Gwen/global" "fmt"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"strconv" "github.com/lejianwen/rustdesk-api/v2/global"
) )
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)
} }

View File

@@ -1,10 +1,10 @@
package http package http
import ( import (
"Gwen/global"
"Gwen/http/middleware"
"Gwen/http/router"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/middleware"
"github.com/lejianwen/rustdesk-api/v2/http/router"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"net/http" "net/http"
"strings" "strings"

View File

@@ -1,13 +1,13 @@
package middleware package middleware
import ( import (
"Gwen/http/response"
"Gwen/service"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/service"
) )
// AdminAuth 后台权限验证中间件 // BackendUserAuth 后台权限验证中间件
func AdminAuth() gin.HandlerFunc { func BackendUserAuth() gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
//测试先关闭 //测试先关闭
@@ -24,6 +24,14 @@ func AdminAuth() gin.HandlerFunc {
return return
} }
if !service.AllService.UserService.CheckUserEnable(user) {
c.JSON(401, gin.H{
"error": "Unauthorized",
})
c.Abort()
return
}
c.Set("curUser", user) c.Set("curUser", user)
c.Set("token", token) c.Set("token", token)
//如果时间小于1天,token自动续期 //如果时间小于1天,token自动续期

View File

@@ -1,9 +1,9 @@
package middleware package middleware
import ( import (
"Gwen/http/response"
"Gwen/service"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/service"
) )
// AdminPrivilege ... // AdminPrivilege ...

View File

@@ -1,10 +1,10 @@
package middleware package middleware
import ( import (
"Gwen/global"
"Gwen/http/response"
"Gwen/service"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/service"
) )
func JwtAuth() gin.HandlerFunc { func JwtAuth() gin.HandlerFunc {

View File

@@ -1,8 +1,8 @@
package middleware package middleware
import ( import (
"Gwen/global"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )

View File

@@ -1,9 +1,9 @@
package middleware package middleware
import ( import (
"Gwen/global"
"Gwen/service"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/service"
) )
func RustAuth() gin.HandlerFunc { func RustAuth() gin.HandlerFunc {

View File

@@ -1,8 +1,8 @@
package admin package admin
import ( import (
"Gwen/model"
"encoding/json" "encoding/json"
"github.com/lejianwen/rustdesk-api/v2/model"
) )
type AddressBookForm struct { type AddressBookForm struct {

View File

@@ -1,6 +1,6 @@
package admin package admin
import "Gwen/model" import "github.com/lejianwen/rustdesk-api/v2/model"
type GroupForm struct { type GroupForm struct {
Id uint `json:"id"` Id uint `json:"id"`
@@ -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
}

View File

@@ -1,7 +1,7 @@
package admin package admin
import ( import (
"Gwen/model" "github.com/lejianwen/rustdesk-api/v2/model"
) )
type BindOauthForm struct { type BindOauthForm struct {
@@ -15,27 +15,31 @@ type UnBindOauthForm struct {
Op string `json:"op" binding:"required"` Op string `json:"op" binding:"required"`
} }
type OauthForm struct { type OauthForm struct {
Id uint `json:"id"` Id uint `json:"id"`
Op string `json:"op" validate:"omitempty"` Op string `json:"op" validate:"omitempty"`
OauthType string `json:"oauth_type" validate:"required"` OauthType string `json:"oauth_type" validate:"required"`
Issuer string `json:"issuer" validate:"omitempty,url"` Issuer string `json:"issuer" validate:"omitempty,url"`
Scopes string `json:"scopes" validate:"omitempty"` Scopes string `json:"scopes" validate:"omitempty"`
ClientId string `json:"client_id" validate:"required"` ClientId string `json:"client_id" validate:"required"`
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 {
oa := &model.Oauth{ oa := &model.Oauth{
Op: of.Op, Op: of.Op,
OauthType: of.OauthType, OauthType: of.OauthType,
ClientId: of.ClientId, ClientId: of.ClientId,
ClientSecret: of.ClientSecret, ClientSecret: of.ClientSecret,
RedirectUrl: of.RedirectUrl, RedirectUrl: of.RedirectUrl,
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

View File

@@ -1,6 +1,6 @@
package admin package admin
import "Gwen/model" import "github.com/lejianwen/rustdesk-api/v2/model"
type PeerForm struct { type PeerForm struct {
RowId uint `json:"row_id" ` RowId uint `json:"row_id" `
@@ -12,6 +12,7 @@ type PeerForm struct {
Username string `json:"username"` Username string `json:"username"`
Uuid string `json:"uuid"` Uuid string `json:"uuid"`
Version string `json:"version"` Version string `json:"version"`
GroupId uint `json:"group_id"`
} }
type PeerBatchDeleteForm struct { type PeerBatchDeleteForm struct {
@@ -30,6 +31,7 @@ func (f *PeerForm) ToPeer() *model.Peer {
Username: f.Username, Username: f.Username,
Uuid: f.Uuid, Uuid: f.Uuid,
Version: f.Version, Version: f.Version,
GroupId: f.GroupId,
} }
} }
@@ -39,6 +41,8 @@ type PeerQuery struct {
Id string `json:"id" form:"id"` Id string `json:"id" form:"id"`
Hostname string `json:"hostname" form:"hostname"` Hostname string `json:"hostname" form:"hostname"`
Uuids string `json:"uuids" form:"uuids"` Uuids string `json:"uuids" form:"uuids"`
Ip string `json:"ip" form:"ip"`
Username string `json:"username" form:"username"`
} }
type SimpleDataQuery struct { type SimpleDataQuery struct {

View File

@@ -1,6 +1,6 @@
package admin package admin
import "Gwen/model" import "github.com/lejianwen/rustdesk-api/v2/model"
type TagForm struct { type TagForm struct {
Id uint `json:"id"` Id uint `json:"id"`

View File

@@ -1,7 +1,7 @@
package admin package admin
import ( import (
"Gwen/model" "github.com/lejianwen/rustdesk-api/v2/model"
) )
type UserForm struct { type UserForm struct {

View File

@@ -1,9 +1,9 @@
package api package api
import ( import (
"Gwen/global"
"Gwen/model"
"encoding/json" "encoding/json"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/model"
"strconv" "strconv"
) )

View File

@@ -1,6 +1,6 @@
package api package api
import "Gwen/model" import "github.com/lejianwen/rustdesk-api/v2/model"
type AddressBookFormData struct { type AddressBookFormData struct {
Tags []string `json:"tags"` Tags []string `json:"tags"`

View File

@@ -1,6 +1,6 @@
package admin package admin
import "Gwen/model" import "github.com/lejianwen/rustdesk-api/v2/model"
type LoginPayload struct { type LoginPayload struct {
Username string `json:"username"` Username string `json:"username"`

View File

@@ -1,6 +1,6 @@
package api package api
import "Gwen/model" import "github.com/lejianwen/rustdesk-api/v2/model"
type AbList struct { type AbList struct {
Peers []*model.AddressBook `json:"peers,omitempty"` Peers []*model.AddressBook `json:"peers,omitempty"`

View File

@@ -1,6 +1,6 @@
package api package api
import "Gwen/model" import "github.com/lejianwen/rustdesk-api/v2/model"
/* /*
GroupPeerPayload GroupPeerPayload
@@ -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
} }

View File

@@ -1,6 +1,6 @@
package api package api
import "Gwen/model" import "github.com/lejianwen/rustdesk-api/v2/model"
/* /*
pub enum UserStatus { pub enum UserStatus {

View File

@@ -1,7 +1,7 @@
package api package api
import ( import (
"Gwen/model" "github.com/lejianwen/rustdesk-api/v2/model"
"time" "time"
) )

View File

@@ -1,9 +1,9 @@
package response package response
import ( import (
"Gwen/global"
"fmt" "fmt"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/nicksnyder/go-i18n/v2/i18n" "github.com/nicksnyder/go-i18n/v2/i18n"
"net/http" "net/http"
) )

View File

@@ -1,12 +1,12 @@
package router package router
import ( import (
_ "Gwen/docs/admin"
"Gwen/global"
"Gwen/http/controller/admin"
"Gwen/http/controller/admin/my"
"Gwen/http/middleware"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
_ "github.com/lejianwen/rustdesk-api/v2/docs/admin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/controller/admin"
"github.com/lejianwen/rustdesk-api/v2/http/controller/admin/my"
"github.com/lejianwen/rustdesk-api/v2/http/middleware"
swaggerFiles "github.com/swaggo/files" swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger" ginSwagger "github.com/swaggo/gin-swagger"
) )
@@ -22,7 +22,10 @@ func Init(g *gin.Engine) {
adg := g.Group("/api/admin") adg := g.Group("/api/admin")
LoginBind(adg) LoginBind(adg)
adg.POST("/user/register", (&admin.User{}).Register) adg.POST("/user/register", (&admin.User{}).Register)
adg.Use(middleware.AdminAuth())
ConfigBind(adg)
adg.Use(middleware.BackendUserAuth())
//FileBind(adg) //FileBind(adg)
UserBind(adg) UserBind(adg)
GroupBind(adg) GroupBind(adg)
@@ -35,7 +38,6 @@ func Init(g *gin.Engine) {
AddressBookCollectionBind(adg) AddressBookCollectionBind(adg)
AddressBookCollectionRuleBind(adg) AddressBookCollectionRuleBind(adg)
UserTokenBind(adg) UserTokenBind(adg)
ConfigBind(adg)
//deprecated by ConfigBind //deprecated by ConfigBind
//rs := &admin.Rustdesk{} //rs := &admin.Rustdesk{}
@@ -47,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"))
} }
@@ -104,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())
{ {
@@ -221,9 +235,13 @@ func UserTokenBind(rg *gin.RouterGroup) {
func ConfigBind(rg *gin.RouterGroup) { func ConfigBind(rg *gin.RouterGroup) {
aR := rg.Group("/config") aR := rg.Group("/config")
rs := &admin.Config{} rs := &admin.Config{}
aR.GET("/admin", rs.AdminConfig)
aR.Use(middleware.BackendUserAuth())
aR.GET("/server", rs.ServerConfig) aR.GET("/server", rs.ServerConfig)
aR.GET("/app", rs.AppConfig) aR.GET("/app", rs.AppConfig)
aR.GET("/admin", rs.AdminConfig)
} }
/* /*

View File

@@ -1,11 +1,11 @@
package router package router
import ( import (
_ "Gwen/docs/api"
"Gwen/global"
"Gwen/http/controller/api"
"Gwen/http/middleware"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
_ "github.com/lejianwen/rustdesk-api/v2/docs/api"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/controller/api"
"github.com/lejianwen/rustdesk-api/v2/http/middleware"
swaggerFiles "github.com/swaggo/files" swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger" ginSwagger "github.com/swaggo/gin-swagger"
"net/http" "net/http"
@@ -18,13 +18,18 @@ 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")
i := &api.Index{} {
frg.GET("/", i.Index) i := &api.Index{}
frg.GET("/", i.Index)
frg.GET("/version", i.Version)
frg.POST("/heartbeat", i.Heartbeat) frg.POST("/heartbeat", i.Heartbeat)
}
{ {
l := &api.Login{} l := &api.Login{}
@@ -33,6 +38,7 @@ func ApiInit(g *gin.Engine) {
frg.POST("/login", l.Login) frg.POST("/login", l.Login)
} }
{ {
o := &api.Oauth{} o := &api.Oauth{}
// [method:POST] [uri:/api/oidc/auth] // [method:POST] [uri:/api/oidc/auth]
@@ -47,16 +53,21 @@ func ApiInit(g *gin.Engine) {
pe := &api.Peer{} pe := &api.Peer{}
//提交系统信息 //提交系统信息
frg.POST("/sysinfo", pe.SysInfo) frg.POST("/sysinfo", pe.SysInfo)
frg.POST("/sysinfo_ver", pe.SysInfoVer)
} }
if global.Config.App.WebClient == 1 { if global.Config.App.WebClient == 1 {
WebClientRoutes(frg) WebClientRoutes(frg)
} }
au := &api.Audit{}
//[method:POST] [uri:/api/audit/conn] {
frg.POST("/audit/conn", au.AuditConn) au := &api.Audit{}
//[method:POST] [uri:/api/audit/file] //[method:POST] [uri:/api/audit/conn]
frg.POST("/audit/file", au.AuditFile) frg.POST("/audit/conn", au.AuditConn)
//[method:POST] [uri:/api/audit/file]
frg.POST("/audit/file", au.AuditFile)
}
frg.Use(middleware.RustAuth()) frg.Use(middleware.RustAuth())
{ {
u := &api.User{} u := &api.User{}
@@ -71,6 +82,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)
} }
{ {
@@ -80,6 +93,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"))

View File

@@ -1,9 +1,9 @@
package router package router
import ( import (
"Gwen/global"
"Gwen/http/controller/web"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/controller/web"
"net/http" "net/http"
) )

View File

@@ -21,7 +21,7 @@ type Config struct {
func New(c *Config) *log.Logger { func New(c *Config) *log.Logger {
log.SetFormatter(&nested.Formatter{ log.SetFormatter(&nested.Formatter{
// HideKeys: true, // HideKeys: true,
TimestampFormat: "2006-01-02 15:04:05", TimestampFormat: "[2006-01-02 15:04:05]",
NoColors: true, NoColors: true,
NoFieldsColors: true, NoFieldsColors: true,
//FieldsOrder: []string{"name", "age"}, //FieldsOrder: []string{"name", "age"},

View File

@@ -1,8 +1,8 @@
package orm package orm
import ( import (
"Gwen/global"
"fmt" "fmt"
"github.com/lejianwen/rustdesk-api/v2/global"
"gorm.io/driver/mysql" "gorm.io/driver/mysql"
"gorm.io/gorm" "gorm.io/gorm"
"gorm.io/gorm/logger" "gorm.io/gorm/logger"

View File

@@ -1,8 +1,8 @@
package orm package orm
import ( import (
"Gwen/global"
"fmt" "fmt"
"github.com/lejianwen/rustdesk-api/v2/global"
"gorm.io/driver/sqlite" "gorm.io/driver/sqlite"
"gorm.io/gorm" "gorm.io/gorm"
"gorm.io/gorm/logger" "gorm.io/gorm/logger"

View File

@@ -1,6 +1,6 @@
package model package model
import "Gwen/model/custom_types" import "github.com/lejianwen/rustdesk-api/v2/model/custom_types"
// final String id; // final String id;
// String hash; // personal ab hash password // String hash; // personal ab hash password

View File

@@ -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
}

View File

@@ -1,7 +1,7 @@
package model package model
import ( import (
"Gwen/model/custom_types" "github.com/lejianwen/rustdesk-api/v2/model/custom_types"
) )
type StatusCode int type StatusCode int

View File

@@ -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
} }

View File

@@ -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
} }

View File

@@ -2,11 +2,11 @@ package model
type UserToken struct { type UserToken struct {
IdModel IdModel
UserId uint `json:"user_id" gorm:"default:0;not null;index"` UserId uint `json:"user_id" gorm:"default:0;not null;index"`
DeviceUuid string `json:"device_uuid" gorm:"default:'';omitempty;"` DeviceUuid string `json:"device_uuid" gorm:"default:'';omitempty;"`
DeviceId string `json:"device_id" gorm:"default:'';omitempty;"` DeviceId string `json:"device_id" gorm:"default:'';omitempty;"`
Token string `json:"token" gorm:"default:'';not null;index"` Token string `json:"token" gorm:"default:'';not null;index"`
ExpiredAt int64 `json:"expired_at" gorm:"default:0;not null;"` ExpiredAt int64 `json:"expired_at" gorm:"default:0;not null;"`
TimeModel TimeModel
} }

View File

@@ -133,3 +133,8 @@ other = "Captcha required."
description = "Captcha error." description = "Captcha error."
one = "Captcha error." one = "Captcha error."
other = "Captcha error." other = "Captcha error."
[PwdLoginDisabled]
description = "Password login disabled."
one = "Password login disabled."
other = "Password login disabled."

View File

@@ -141,4 +141,9 @@ other = "Captcha requerido."
[CaptchaError] [CaptchaError]
description = "Captcha error." description = "Captcha error."
one = "Error de captcha." one = "Error de captcha."
other = "Error de captcha." other = "Error de captcha."
[PwdLoginDisabled]
description = "Password login disabled."
one = "Inicio de sesión con contraseña deshabilitado."
other = "Inicio de sesión con contraseña deshabilitado."

View File

@@ -142,3 +142,8 @@ other = "Captcha requis."
description = "Captcha error." description = "Captcha error."
one = "Erreur de captcha." one = "Erreur de captcha."
other = "Erreur de captcha." other = "Erreur de captcha."
[PwdLoginDisabled]
description = "Password login disabled."
one = "Connexion par mot de passe désactivée."
other = "Connexion par mot de passe désactivée."

View File

@@ -135,4 +135,9 @@ other = "Captcha가 필요합니다."
[CaptchaError] [CaptchaError]
description = "Captcha error." description = "Captcha error."
one = "Captcha 오류." one = "Captcha 오류."
other = "Captcha 오류." other = "Captcha 오류."
[PwdLoginDisabled]
description = "Password login disabled."
one = "비밀번호 로그인이 비활성화되었습니다."
other = "비밀번호 로그인이 비활성화되었습니다."

View File

@@ -141,4 +141,9 @@ other = "Требуется капча."
[CaptchaError] [CaptchaError]
description = "Captcha error." description = "Captcha error."
one = "Ошибка капчи." one = "Ошибка капчи."
other = "Ошибка капчи." other = "Ошибка капчи."
[PwdLoginDisabled]
description = "Password login disabled."
one = "Вход по паролю отключен."
other = "Вход по паролю отключен."

View File

@@ -134,4 +134,9 @@ other = "需要验证码。"
[CaptchaError] [CaptchaError]
description = "Captcha error." description = "Captcha error."
one = "验证码错误。" one = "验证码错误。"
other = "验证码错误。" other = "验证码错误。"
[PwdLoginDisabled]
description = "Password login disabled."
one = "密码登录已禁用。"
other = "密码登录已禁用。"

142
resources/i18n/zh_TW.toml Normal file
View File

@@ -0,0 +1,142 @@
[Test]
description = "test"
one = "測試1 {{.P0}}"
other = "測試2 {{.P0}}"
[ParamsError]
description = "Params validation failed."
one = "引數錯誤。"
other = "引數錯誤。"
[OperationFailed]
description = "OperationFailed."
one = "操作失敗。"
other = "操作失敗。"
[OperationSuccess]
description = "OperationSuccess."
one = "操作成功。"
other = "操作成功。"
[ItemExists]
description = "Item already exists."
one = "資料已存在。"
other = "資料已存在。"
[ItemNotFound]
description = "Item not found."
one = "資料不存在。"
other = "資料不存在。"
[NoAccess]
description = "No access."
one = "無許可權。"
other = "無許可權。"
[UsernameOrPasswordError]
description = "Username or password error."
one = "使用者名稱或密碼錯誤。"
other = "使用者名稱或密碼錯誤。"
[SystemError]
description = "System error."
one = "系統錯誤。"
other = "系統錯誤。"
[ConfigNotFound]
description = "Config not found."
one = "配置不存在。"
other = "配置不存在。"
#授權過期
[OauthExpired]
description = "Oauth expired."
one = "授權過期,請重新授權。"
other = "授權過期,請重新授權。"
[OauthFailed]
description = "Oauth failed."
one = "授權失敗。"
other = "授權失敗。"
[OauthHasBindOtherUser]
description = "Oauth has bind other user."
one = "授權已繫結其他使用者。"
other = "授權已繫結其他使用者。"
[ParamIsEmpty]
description = "Param is empty."
one = "{{.P0}} 為空。"
other = "{{.P0}} 為空。"
[BindFail]
description = "Bind fail."
one = "繫結失敗。"
other = "繫結失敗。"
[BindSuccess]
description = "Bind success."
one = "繫結成功。"
other = "繫結成功。"
[OauthHasBeenSuccess]
description = "Oauth has been success."
one = "授權已成功。"
other = "授權已成功。"
[OauthSuccess]
description = "Oauth success."
one = "授權成功。"
other = "授權成功。"
[OauthRegisterSuccess]
description = "Oauth register success."
one = "授權註冊成功。"
other = "授權註冊成功。"
[OauthRegisterFailed]
description = "Oauth register failed."
one = "授權註冊失敗。"
other = "授權註冊失敗。"
[GetOauthTokenError]
description = "Get oauth token error."
one = "獲取授權token失敗。"
other = "獲取授權token失敗。"
[GetOauthUserInfoError]
description = "Get oauth user info error."
one = "獲取授權使用者資訊失敗。"
other = "獲取授權使用者資訊失敗。"
[DecodeOauthUserInfoError]
description = "Decode oauth user info error."
one = "解析授權使用者資訊失敗。"
other = "解析授權使用者資訊失敗。"
[OldPasswordError]
description = "Old password error."
one = "舊密碼錯誤。"
other = "舊密碼錯誤。"
[DefaultGroup]
description = "Default group."
one = "預設組"
other = "預設組"
[ShareGroup]
description = "Share group."
one = "共享組"
other = "共享組"
[RegisterClosed]
description = "Register closed."
one = "註冊已關閉。"
other = "註冊已關閉。"
[CaptchaRequired]
description = "Captcha required."
one = "需要驗證碼。"
other = "需要驗證碼。"
[CaptchaError]
description = "Captcha error."
one = "驗證碼錯誤。"
other = "驗證碼錯誤。"
[PwdLoginDisabled]
description = "Password login disabled."
one = "密碼登錄已禁用。"
other = "密碼登錄已禁用。"

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

Some files were not shown because too many files have changed in this diff Show More