Compare commits

...

13 Commits

Author SHA1 Message Date
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
lejianwen
a951b982b3 fix: Jwt 2025-01-15 20:26:26 +08:00
lejianwen
a33be66504 docs: Up readme 2025-01-15 20:09:08 +08:00
lejianwen
f41b9d5887 feat!: Add JWT
- `RUSTDESK_API_JWT_KEY`如果设置,将会启用JWT,token自动续期功能将失效
- 此功能是为了server端校验token的合法性
2025-01-15 19:25:28 +08:00
lejianwen
3c608463e6 docs: Up readme 2025-01-12 23:12:00 +08:00
lejianwen
eeffbe124a docs: Up readme 2025-01-12 21:35:34 +08:00
22 changed files with 470 additions and 123 deletions

117
README.md
View File

@@ -49,21 +49,22 @@
### API 服务
基本实现了PC端基础的接口。支持Personal版本接口可以通过配置文件`rustdesk.personal`或环境变量`RUSTDESK_API_RUSTDESK_PERSONAL`来控制是否启用
#### 登录
- 添加了`github`, `google` 以及`OIDC`授权登录需要在后台配置好就可以用了具体可看后台OAuth配置
- 添加了web后台授权登录,点击后直接登录后台就自动登录客户端了
![pc_login](docs/pc_login.png)
#### 地址簿
![pc_ab](docs/pc_ab.png)
#### 群组
群组分为`共享组``普通组`,共享组中所有人都能看到小组成员的设备,普通组只有管理员能看到所有小组成员的设备
![pc_gr](docs/pc_gr.png)
<table>
<tr>
<td width="50%" align="center" colspan="2"><b>登录</b></td>
</tr>
<tr>
<td width="50%" align="center" colspan="2"><img src="docs/pc_login.png"></td>
</tr>
<tr>
<td width="50%" align="center"><b>地址簿</b></td>
<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>
</tr>
</table>
### Web Admin:
@@ -75,15 +76,16 @@
![web_admin](docs/web_admin.png)
2. 普通用户界面
![web_user](docs/web_admin_user.png)
右上角可以更改密码,可以切换语言,可以切换`白天/黑夜`模式
![web_resetpwd](docs/web_resetpwd.png)
3. 分组可以自定义,方便管理,暂时支持两种类型: `共享组``普通组`
![web_admin_gr](docs/web_admin_gr.png)
4. 可以直接打开webclient方便使用也可以分享给游客游客可以直接通过webclient远程到设备
3. 每个用户可以多个地址簿,也可以将地址簿共享给其他用户
4. 分组可以自定义,方便管理,暂时支持两种类型: `共享组``普通组`
5. 可以直接打开webclient方便使用也可以分享给游客游客可以直接通过webclient远程到设备
![web_webclient](docs/admin_webclient.png)
5. 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`不需要填写.
- 对于`OIDC`, `Issuer`是必须的。`Scopes`是可选的,默认为 `openid,profile,email`. 确保可以获取 `sub`,`email``preferred_username`
@@ -91,6 +93,21 @@
中创建,地址 [https://github.com/settings/developers](https://github.com/settings/developers)
- `Authorization callback URL`填写`http://<your server[:port]>/api/oauth/callback`
,比如`http://127.0.0.1:21114/api/oauth/callback`
7. 登录日志
8. 链接日志
9. 文件传输日志
10. server控制
- `简易模式`,已经界面化了一些简单的指令,可以直接在后台执行
![rustdesk_command_simple](./docs/rustdesk_command_simple.png)
- `高级模式`,直接在后台执行指令
* 可以官方指令
* 可以添加自定义指令
* 可以执行自定义指令
![rustdesk_command_advance](./docs/rustdesk_command_advance.png)
### Web Client:
@@ -161,6 +178,9 @@ logger:
proxy:
enable: false
host: ""
jwt:
key: ""
expire-duration: 360000
```
### 环境变量
@@ -199,6 +219,9 @@ proxy:
| ----PROXY配置----- | --------------- | ---------- |
| RUSTDESK_API_PROXY_ENABLE | 是否启用代理:`false`, `true` | `false` |
| RUSTDESK_API_PROXY_HOST | 代理地址 | `http://127.0.0.1:1080` |
| ----JWT配置---- | -------- | -------- |
| RUSTDESK_API_JWT_KEY | 自定义JWT KEY,为空则不启用JWT | |
| RUSTDESK_API_JWT_EXPIRE_DURATION | JWT有效时间 | 360000 |
### 运行
@@ -264,8 +287,60 @@ proxy:
6. 打开浏览器访问`http://<your server[:port]>/_admin/`,默认用户名密码为`admin`,请及时更改密码。
#### 使用我fork后的server-s6镜像运行
- github https://github.com/lejianwen/rustdesk-server
- docker hub https://hub.docker.com/r/lejianwen/rustdesk-server-s6
```yaml
networks:
rustdesk-net:
external: false
services:
rustdesk:
ports:
- 21114:21114
- 21115:21115
- 21116:21116
- 21116:21116/udp
- 21117:21117
- 21118:21118
- 21119:21119
image: lejianwen/rustdesk-server-s6:latest
environment:
- RELAY=<relay_server[:port]>
- ENCRYPTED_ONLY=1
- MUST_LOGIN=N
- TZ=Asia/Shanghai
- RUSTDESK_API_RUSTDESK_ID_SERVER=<id_server[:21116]>
- RUSTDESK_API_RUSTDESK_RELAY_SERVER=<relay_server[:21117]>
- RUSTDESK_API_RUSTDESK_API_SERVER=http://<api_server[:21114]>
- RUSTDESK_API_KEY_FILE=/data/id_ed25519.pub
- RUSTDESK_API_JWT_KEY=xxxxxx # jwt key
volumes:
- /data/rustdesk/server:/data
- /data/rustdesk/api:/app/data #将数据库挂载
networks:
- rustdesk-net
restart: unless-stopped
```
## 其他
- [WIKI](https://github.com/lejianwen/rustdesk-api/wiki)
- [链接超时问题](https://github.com/lejianwen/rustdesk-api/issues/92)
- [修改客户端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

@@ -47,22 +47,22 @@ desktop software that provides self-hosted solutions.
### 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.
#### Login
- Added `GitHub`, `Google` and `OIDC` login, which can be used after configuration in the admin panel. See the OAuth
configuration section for details.
- Added authorization login for the web admin panel.
![pc_login](docs/en_img/pc_login.png)
#### Address Book
![pc_ab](docs/en_img/pc_ab.png)
#### Groups
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.
![pc_gr](docs/en_img/pc_gr.png)
<table>
<tr>
<td width="50%" align="center" colspan="2"><b>Login</b></td>
</tr>
<tr>
<td width="50%" align="center" colspan="2"><img src="docs/en_img/pc_login.png"></td>
</tr>
<tr>
<td width="50%" align="center"><b>Address Book</b></td>
<td width="50%" align="center"><b>Groups</b></td>
</tr>
<tr>
<td width="50%" align="center"><img src="docs/en_img/pc_ab.png"></td>
<td width="50%" align="center"><img src="docs/en_img/pc_gr.png"></td>
</tr>
</table>
### Web Admin
@@ -79,12 +79,11 @@ installation are `admin` `admin`, please change the password immediately.
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. Groups can be customized for easy management. Currently, two types are supported: `shared group` and `regular group`.
![web_admin_gr](docs/en_img/web_admin_gr.png)
4. 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.
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`.
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)
5. 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.
![web_admin_oauth](docs/en_img/web_admin_oauth.png)
- For `Google` and `Github`, you don't need to fill the `Issuer` and `Scpoes`
@@ -93,6 +92,23 @@ installation are `admin` `admin`, please change the password immediately.
at `Settings` -> `Developer settings` -> `OAuth Apps` -> `New OAuth App` [here](https://github.com/settings/developers).
- Set the `Authorization callback URL` to `http://<your server[:port]>/api/oauth/callback`,
e.g., `http://127.0.0.1:21114/api/oauth/callback`.
7. Login logs
8. Connection logs
9. File transfer logs
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)
- `Advanced mode`, commands can be executed directly in the backend
* Official commands can be used
* Custom commands can be added
* Custom commands can be executed
![rustdesk_command_advance](./docs/en_img/rustdesk_command_advance.png)
### Web Client:
@@ -163,44 +179,50 @@ logger:
proxy:
enable: false
host: ""
jwt:
key: ""
expire-duration: 360000
```
### Environment Variables
The prefix for variable names is `RUSTDESK_API`. If environment variables exist, they will override the configurations in the configuration file.
| Variable Name | Description | Example |
|-----------------------------------------------------|--------------------------------------------------------------------------------------------------------------|-------------------------------|
| TZ | timezone | Asia/Shanghai |
| 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_REGISTER | register enable; `true`, `false`; default:`false` | `false` |
| RUSTDESK_API_APP_SHOW_SWAGGER | swagger visible; 1: yes, 0: no; default: 0 | `0` |
| ----- ADMIN Configuration----- | ---------- | ---------- |
| RUSTDESK_API_ADMIN_TITLE | Admin Title | `RustDesk Api Admin` |
| RUSTDESK_API_ADMIN_HELLO | Admin welcome message, you can use `html` | |
| RUSTDESK_API_ADMIN_HELLO_FILE | Admin welcome message file,<br>will override `RUSTDESK_API_ADMIN_HELLO` | `./conf/admin/hello.html` |
| ----- GIN Configuration ----- | --------------------------------------- | ----------------------------- |
| RUSTDESK_API_GIN_TRUST_PROXY | Trusted proxy IPs, separated by commas. | 192.168.1.2,192.168.1.3 |
| ----- GORM Configuration ----- | --------------------------------------- | ----------------------------- |
| RUSTDESK_API_GORM_TYPE | Database type (`sqlite` or `mysql`). Default is `sqlite`. | sqlite |
| RUSTDESK_API_GORM_MAX_IDLE_CONNS | Maximum idle connections | 10 |
| RUSTDESK_API_GORM_MAX_OPEN_CONNS | Maximum open connections | 100 |
| RUSTDESK_API_RUSTDESK_PERSONAL | Open Personal Api 1:Enable,0:Disable | 1 |
| ----- MYSQL Configuration ----- | --------------------------------------- | ----------------------------- |
| RUSTDESK_API_MYSQL_USERNAME | MySQL username | root |
| RUSTDESK_API_MYSQL_PASSWORD | MySQL password | 111111 |
| RUSTDESK_API_MYSQL_ADDR | MySQL address | 192.168.1.66:3306 |
| RUSTDESK_API_MYSQL_DBNAME | MySQL database name | rustdesk |
| ----- RUSTDESK Configuration ----- | --------------------------------------- | ----------------------------- |
| RUSTDESK_API_RUSTDESK_ID_SERVER | Rustdesk ID server address | 192.168.1.66:21116 |
| RUSTDESK_API_RUSTDESK_RELAY_SERVER | Rustdesk relay server address | 192.168.1.66:21117 |
| RUSTDESK_API_RUSTDESK_API_SERVER | Rustdesk API server address | http://192.168.1.66:21114 |
| RUSTDESK_API_RUSTDESK_KEY | Rustdesk key | 123456789 |
| RUSTDESK_API_RUSTDESK_KEY_FILE | Rustdesk key file | `./conf/data/id_ed25519.pub` |
| 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` |
| ---- PROXY ----- | --------------- | ---------- |
| RUSTDESK_API_PROXY_ENABLE | proxy_enable :`false`, `true` | `false` |
| RUSTDESK_API_PROXY_HOST | proxy_host | `http://127.0.0.1:1080` |
| Variable Name | Description | Example |
|---------------------------------------------------|--------------------------------------------------------------------------------------------------------------|-------------------------------|
| TZ | timezone | Asia/Shanghai |
| 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_REGISTER | register enable; `true`, `false`; default:`false` | `false` |
| RUSTDESK_API_APP_SHOW_SWAGGER | swagger visible; 1: yes, 0: no; default: 0 | `0` |
| ----- ADMIN Configuration----- | ---------- | ---------- |
| RUSTDESK_API_ADMIN_TITLE | Admin Title | `RustDesk Api Admin` |
| RUSTDESK_API_ADMIN_HELLO | Admin welcome message, you can use `html` | |
| RUSTDESK_API_ADMIN_HELLO_FILE | Admin welcome message file,<br>will override `RUSTDESK_API_ADMIN_HELLO` | `./conf/admin/hello.html` |
| ----- GIN Configuration ----- | --------------------------------------- | ----------------------------- |
| RUSTDESK_API_GIN_TRUST_PROXY | Trusted proxy IPs, separated by commas. | 192.168.1.2,192.168.1.3 |
| ----- GORM Configuration ----- | --------------------------------------- | ----------------------------- |
| RUSTDESK_API_GORM_TYPE | Database type (`sqlite` or `mysql`). Default is `sqlite`. | sqlite |
| RUSTDESK_API_GORM_MAX_IDLE_CONNS | Maximum idle connections | 10 |
| RUSTDESK_API_GORM_MAX_OPEN_CONNS | Maximum open connections | 100 |
| RUSTDESK_API_RUSTDESK_PERSONAL | Open Personal Api 1:Enable,0:Disable | 1 |
| ----- MYSQL Configuration ----- | --------------------------------------- | ----------------------------- |
| RUSTDESK_API_MYSQL_USERNAME | MySQL username | root |
| RUSTDESK_API_MYSQL_PASSWORD | MySQL password | 111111 |
| RUSTDESK_API_MYSQL_ADDR | MySQL address | 192.168.1.66:3306 |
| RUSTDESK_API_MYSQL_DBNAME | MySQL database name | rustdesk |
| ----- RUSTDESK Configuration ----- | --------------------------------------- | ----------------------------- |
| RUSTDESK_API_RUSTDESK_ID_SERVER | Rustdesk ID server address | 192.168.1.66:21116 |
| RUSTDESK_API_RUSTDESK_RELAY_SERVER | Rustdesk relay server address | 192.168.1.66:21117 |
| RUSTDESK_API_RUSTDESK_API_SERVER | Rustdesk API server address | http://192.168.1.66:21114 |
| RUSTDESK_API_RUSTDESK_KEY | Rustdesk key | 123456789 |
| RUSTDESK_API_RUSTDESK_KEY_FILE | Rustdesk key file | `./conf/data/id_ed25519.pub` |
| 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` |
| ---- PROXY ----- | --------------- | ---------- |
| RUSTDESK_API_PROXY_ENABLE | proxy_enable :`false`, `true` | `false` |
| RUSTDESK_API_PROXY_HOST | proxy_host | `http://127.0.0.1:1080` |
| ----JWT---- | -------- | -------- |
| RUSTDESK_API_JWT_KEY | JWT KEY. Set empty to disable jwt | |
| RUSTDESK_API_JWT_EXPIRE_DURATION | JWT expire duration | 360000 |
### Installation Steps
@@ -269,9 +291,55 @@ Download the release from [release](https://github.com/lejianwen/rustdesk-api/re
6. Open your browser and visit `http://<your server[:port]>/_admin/`, with default credentials `admin admin`. Please
change the password promptly.
#### Running with my forked server-s6 image
- github https://github.com/lejianwen/rustdesk-server
- docker hub https://hub.docker.com/r/lejianwen/rustdesk-server-s6
```yaml
networks:
rustdesk-net:
external: false
services:
rustdesk:
ports:
- 21114:21114
- 21115:21115
- 21116:21116
- 21116:21116/udp
- 21117:21117
- 21118:21118
- 21119:21119
image: lejianwen/rustdesk-server-s6:latest
environment:
- RELAY=<relay_server[:port]>
- ENCRYPTED_ONLY=1
- MUST_LOGIN=N
- TZ=Asia/Shanghai
- RUSTDESK_API_RUSTDESK_ID_SERVER=<id_server[:21116]>
- RUSTDESK_API_RUSTDESK_RELAY_SERVER=<relay_server[:21117]>
- RUSTDESK_API_RUSTDESK_API_SERVER=http://<api_server[:21114]>
- RUSTDESK_API_KEY_FILE=/data/id_ed25519.pub
- RUSTDESK_API_JWT_KEY=xxxxxx # jwt key
volumes:
- /data/rustdesk/server:/data
- /data/rustdesk/api:/app/data #将数据库挂载
networks:
- rustdesk-net
restart: unless-stopped
```
## Others
- [WIKI](https://github.com/lejianwen/rustdesk-api/wiki)
- [Connection Timeout](https://github.com/lejianwen/rustdesk-api/issues/92)
- [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>

View File

@@ -5,6 +5,7 @@ import (
"Gwen/global"
"Gwen/http"
"Gwen/lib/cache"
"Gwen/lib/jwt"
"Gwen/lib/lock"
"Gwen/lib/logger"
"Gwen/lib/orm"
@@ -17,6 +18,7 @@ import (
"github.com/spf13/cobra"
"os"
"strconv"
"time"
)
// @title 管理系统API
@@ -100,9 +102,6 @@ func InitGlobal() {
//配置解析
global.Viper = config.Init(&global.Config, global.ConfigPath)
//从配置文件中加载密钥
config.LoadKeyFile(&global.Config.Rustdesk)
//日志
global.Logger = logger.New(&logger.Config{
Path: global.Config.Logger.Path,
@@ -163,7 +162,7 @@ func InitGlobal() {
//jwt
//fmt.Println(global.Config.Jwt.PrivateKey)
//global.Jwt = jwt.NewJwt(global.Config.Jwt.PrivateKey, global.Config.Jwt.ExpireDuration*time.Second)
global.Jwt = jwt.NewJwt(global.Config.Jwt.Key, global.Config.Jwt.ExpireDuration*time.Second)
//locker
global.Lock = lock.NewLocal()

View File

@@ -26,7 +26,7 @@ rustdesk:
relay-server: "192.168.1.66:21117"
api-server: "http://127.0.0.1:21114"
key: ""
key-file: "./conf/data/id_ed25519.pub"
key-file: "/data/id_ed25519.pub"
personal: 1
webclient-magic-queryonline: 0
logger:
@@ -36,6 +36,9 @@ logger:
proxy:
enable: false
host: "http://127.0.0.1:1080"
jwt:
key: ""
expire-duration: 360000
redis:
addr: "127.0.0.1:6379"
password: ""
@@ -53,6 +56,4 @@ oss:
callback-url: ""
expire-time: 30
max-byte: 10240
jwt:
private-key: "./conf/jwt_pri.pem"
expire-duration: 360000

View File

View File

@@ -40,7 +40,7 @@ type Config struct {
}
// Init 初始化配置
func Init(rowVal interface{}, path string) *viper.Viper {
func Init(rowVal *Config, path string) *viper.Viper {
if path == "" {
path = DefaultConfig
}
@@ -61,11 +61,14 @@ func Init(rowVal interface{}, path string) *viper.Viper {
if err2 := v.Unmarshal(rowVal); err2 != nil {
fmt.Println(err2)
}
rowVal.Rustdesk.LoadKeyFile()
rowVal.Rustdesk.ParsePort()
})
if err := v.Unmarshal(rowVal); err != nil {
fmt.Println(err)
}
rowVal.Rustdesk.LoadKeyFile()
rowVal.Rustdesk.ParsePort()
return v
}

View File

@@ -3,6 +3,6 @@ package config
import "time"
type Jwt struct {
PrivateKey string `mapstructure:"private-key"`
Key string `mapstructure:"key"`
ExpireDuration time.Duration `mapstructure:"expire-duration"`
}

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 48 KiB

View File

@@ -4,18 +4,19 @@ import (
"github.com/gin-gonic/gin"
"github.com/go-playground/locales/en"
"github.com/go-playground/locales/es"
"github.com/go-playground/locales/fr"
"github.com/go-playground/locales/ko"
"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_Hant"
ut "github.com/go-playground/universal-translator"
"github.com/go-playground/validator/v10"
en_translations "github.com/go-playground/validator/v10/translations/en"
es_translations "github.com/go-playground/validator/v10/translations/es"
fr_translations "github.com/go-playground/validator/v10/translations/fr"
ru_translations "github.com/go-playground/validator/v10/translations/ru"
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"
)
@@ -29,8 +30,9 @@ func ApiInitValidator() {
ruT := ru.New()
esT := es.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")
zhTrans, _ := uni.GetTranslator("zh_Hans_CN")
@@ -38,6 +40,7 @@ func ApiInitValidator() {
ruTrans, _ := uni.GetTranslator("ru")
esTrans, _ := uni.GetTranslator("es")
frTrans, _ := uni.GetTranslator("fr")
zhTwTrans, _ := uni.GetTranslator("zh_Hant")
err := zh_translations.RegisterDefaultTranslations(validate, zhTrans)
if err != nil {
@@ -65,6 +68,10 @@ func ApiInitValidator() {
if err != nil {
panic(err)
}
err = zh_tw_translations.RegisterDefaultTranslations(validate, zhTwTrans)
if err != nil {
panic(err)
}
validate.RegisterTagNameFunc(func(field reflect.StructField) string {
label := field.Tag.Get("label")
@@ -125,6 +132,13 @@ func getTranslatorForLang(lang string) ut.Translator {
case "zh":
trans, _ := Validator.UT.GetTranslator("zh_Hans_CN")
return trans
case "zh_TW":
fallthrough
case "zh-TW":
fallthrough
case "zh-tw":
trans, _ := Validator.UT.GetTranslator("zh_Hant")
return trans
case "ko":
trans, _ := Validator.UT.GetTranslator("ko")
return trans

View File

@@ -34,7 +34,7 @@ type User struct {
// @Produce json
// @Success 200 {object} apiResp.UserPayload
// @Failure 500 {object} response.Response
// @Router /api [get]
// @Router /currentUser [get]
// @Security token
func (u *User) Info(c *gin.Context) {
user := service.AllService.UserService.CurUser(c)

View File

@@ -1,6 +1,7 @@
package middleware
import (
"Gwen/global"
"Gwen/service"
"github.com/gin-gonic/gin"
)
@@ -27,7 +28,21 @@ func RustAuth() gin.HandlerFunc {
//提取token格式是Bearer {token}
//这里只是简单的提取
token = token[7:]
//验证token
//检查是否设置了jwt key
if len(global.Jwt.Key) > 0 {
uid, _ := service.AllService.UserService.VerifyJWT(token)
if uid == 0 {
c.JSON(401, gin.H{
"error": "Unauthorized",
})
c.Abort()
return
}
}
user, ut := service.AllService.UserService.InfoByAccessToken(token)
if user.Id == 0 {
c.JSON(401, gin.H{
@@ -38,7 +53,7 @@ func RustAuth() gin.HandlerFunc {
}
if !service.AllService.UserService.CheckUserEnable(user) {
c.JSON(401, gin.H{
"error": "账号已被禁用",
"error": "Unauthorized",
})
c.Abort()
return

View File

@@ -1,14 +1,13 @@
package jwt
import (
"crypto/rsa"
"fmt"
"github.com/golang-jwt/jwt/v5"
"os"
"time"
)
type Jwt struct {
privateKey *rsa.PrivateKey
Key []byte
TokenExpireDuration time.Duration
}
@@ -17,31 +16,28 @@ type UserClaims struct {
jwt.RegisteredClaims
}
func NewJwt(privateKeyFile string, tokenExpireDuration time.Duration) *Jwt {
privateKeyContent, err := os.ReadFile(privateKeyFile)
if err != nil {
panic(err)
}
privateKey, err := jwt.ParseRSAPrivateKeyFromPEM(privateKeyContent)
if err != nil {
panic(err)
}
func NewJwt(key string, tokenExpireDuration time.Duration) *Jwt {
return &Jwt{
privateKey: privateKey,
Key: []byte(key),
TokenExpireDuration: tokenExpireDuration,
}
}
func (s *Jwt) GenerateToken(userId uint) string {
t := jwt.NewWithClaims(jwt.SigningMethodRS256,
if len(s.Key) == 0 {
fmt.Println("jwt key is nil")
return ""
}
t := jwt.NewWithClaims(jwt.SigningMethodHS256,
UserClaims{
UserId: userId,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(s.TokenExpireDuration)),
},
})
token, err := t.SignedString(s.privateKey)
token, err := t.SignedString(s.Key)
if err != nil {
fmt.Printf("jwt token generate error: %v", err)
return ""
}
return token
@@ -49,7 +45,7 @@ func (s *Jwt) GenerateToken(userId uint) string {
func (s *Jwt) ParseToken(tokenString string) (uint, error) {
token, err := jwt.ParseWithClaims(tokenString, &UserClaims{}, func(token *jwt.Token) (interface{}, error) {
return s.privateKey.Public(), nil
return s.Key, nil
})
if err != nil {
return 0, err

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

@@ -0,0 +1,137 @@
[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 = "驗證碼錯誤。"

View File

@@ -41,7 +41,14 @@ func (is *ServerCmdService) Create(u *model.ServerCmd) error {
}
// SendCmd 发送命令
func (is *ServerCmdService) SendCmd(port string, cmd string, arg string) (string, error) {
func (is *ServerCmdService) SendCmd(target string, cmd string, arg string) (string, error) {
port := 0
switch target {
case model.ServerCmdTargetIdServer:
port = global.Config.Rustdesk.IdServerPort - 1
case model.ServerCmdTargetRelayServer:
port = global.Config.Rustdesk.RelayServerPort
}
//组装命令
cmd = cmd + " " + arg
res, err := is.SendSocketCmd("v6", port, cmd)
@@ -57,14 +64,14 @@ func (is *ServerCmdService) SendCmd(port string, cmd string, arg string) (string
}
// SendSocketCmd
func (is *ServerCmdService) SendSocketCmd(ty string, port string, cmd string) (string, error) {
func (is *ServerCmdService) SendSocketCmd(ty string, port int, cmd string) (string, error) {
addr := "[::1]"
tcp := "tcp6"
if ty == "v4" {
tcp = "tcp"
addr = "127.0.0.1"
}
conn, err := net.Dial(tcp, fmt.Sprintf("%s:%s", addr, port))
conn, err := net.Dial(tcp, fmt.Sprintf("%s:%v", addr, port))
if err != nil {
global.Logger.Debugf("%s connect to id server failed: %v", ty, err)
return "", err

View File

@@ -68,6 +68,9 @@ func (us *UserService) InfoByAccessToken(token string) (*model.User, *model.User
// GenerateToken 生成token
func (us *UserService) GenerateToken(u *model.User) string {
if len(global.Jwt.Key) > 0 {
return global.Jwt.GenerateToken(u.Id)
}
return utils.Md5(u.Username + time.Now().String())
}
@@ -461,3 +464,7 @@ func (us *UserService) AutoRefreshAccessToken(ut *model.UserToken) {
func (us *UserService) BatchDeleteUserToken(ids []uint) error {
return global.DB.Where("id in ?", ids).Delete(&model.UserToken{}).Error
}
func (us *UserService) VerifyJWT(token string) (uint, error) {
return global.Jwt.ParseToken(token)
}