Compare commits

...

8 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
12 changed files with 262 additions and 58 deletions

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,6 +76,7 @@
![web_admin](docs/web_admin.png)
2. 普通用户界面
![web_user](docs/web_admin_user.png)
右上角可以更改密码,可以切换语言,可以切换`白天/黑夜`模式
![web_resetpwd](docs/web_resetpwd.png)
@@ -330,4 +332,15 @@ jwt:
- [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
@@ -334,4 +334,12 @@ Download the release from [release](https://github.com/lejianwen/rustdesk-api/re
- [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

@@ -102,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,

View File

@@ -31,7 +31,7 @@ rustdesk:
webclient-magic-queryonline: 0
logger:
path: "./runtime/log.txt"
level: "debug" #trace,debug,info,warn,error,fatal
level: "warn" #trace,debug,info,warn,error,fatal
report-caller: true
proxy:
enable: false

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

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

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 67 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)

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