Compare commits

...

14 Commits

Author SHA1 Message Date
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
29 changed files with 8253 additions and 7632 deletions

View File

@@ -84,6 +84,15 @@ jobs:
- name: 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
run: |
go install github.com/swaggo/swag/cmd/swag@latest

7
.gitignore vendored
View File

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

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)
@@ -191,6 +193,7 @@ jwt:
| RUSTDESK_API_APP_WEB_CLIENT | 是否启用web-client; 1:启用,0:不启用; 默认启用 | 1 |
| RUSTDESK_API_APP_REGISTER | 是否开启注册; `true`, `false` 默认`false` | `false` |
| RUSTDESK_API_APP_SHOW_SWAGGER | 是否可见swagger文档;`1`显示,`0`不显示,默认`0`不显示 | `1` |
| RUSTDESK_API_APP_TOKEN_EXPIRE | token有效时长 | `3600` |
| -----ADMIN配置----- | ---------- | ---------- |
| RUSTDESK_API_ADMIN_TITLE | 后台标题 | `RustDesk Api Admin` |
| RUSTDESK_API_ADMIN_HELLO | 后台欢迎语,可以使用`html` | |
@@ -330,4 +333,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
@@ -194,6 +194,7 @@ The prefix for variable names is `RUSTDESK_API`. If environment variables exist,
| 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` |
| RUSTDESK_API_APP_TOKEN_EXPIRE | token expire duration(second) | `3600` |
| ----- ADMIN Configuration----- | ---------- | ---------- |
| RUSTDESK_API_ADMIN_TITLE | Admin Title | `RustDesk Api Admin` |
| RUSTDESK_API_ADMIN_HELLO | Admin welcome message, you can use `html` | |
@@ -334,4 +335,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

@@ -39,7 +39,7 @@ var rootCmd = &cobra.Command{
InitGlobal()
},
Run: func(cmd *cobra.Command, args []string) {
//gin
global.Logger.Info("API SERVER START")
http.ApiInit()
},
}
@@ -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

@@ -3,6 +3,7 @@ app:
web-client: 1 # 1:启用 0:禁用
register: false #是否开启注册
show-swagger: 0 # 1:启用 0:禁用
token-expire: 360000
admin:
title: "RustDesk Api Admin"
hello-file: "./conf/admin/hello.html" #优先使用file
@@ -31,7 +32,7 @@ rustdesk:
webclient-magic-queryonline: 0
logger:
path: "./runtime/log.txt"
level: "debug" #trace,debug,info,warn,error,fatal
level: "info" #trace,debug,info,warn,error,fatal
report-caller: true
proxy:
enable: false

View File

@@ -17,6 +17,7 @@ type App struct {
WebClient int `mapstructure:"web-client"`
Register bool `mapstructure:"register"`
ShowSwagger int `mapstructure:"show-swagger"`
TokenExpire int `mapstructure:"token-expire"`
}
type Admin struct {
Title string `mapstructure:"title"`
@@ -40,7 +41,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 +62,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])
}
}

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": {
"post": {
"security": [
@@ -1881,7 +1847,7 @@ const docTemplateadmin = `{
}
}
},
"/admin/login_log/delete": {
"/admin/login_log/batchDelete": {
"post": {
"security": [
{
@@ -1926,6 +1892,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}": {
"get": {
"security": [
@@ -2757,6 +2768,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": {
"get": {
"security": [
@@ -3884,40 +4051,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": {
"post": {
"security": [
@@ -5768,6 +5901,9 @@ const docTemplateadmin = `{
"ip": {
"type": "string"
},
"is_deleted": {
"type": "integer"
},
"platform": {
"description": "windows,linux,mac,android,ios",
"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": {
"post": {
"security": [
@@ -1874,7 +1840,7 @@
}
}
},
"/admin/login_log/delete": {
"/admin/login_log/batchDelete": {
"post": {
"security": [
{
@@ -1919,6 +1885,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}": {
"get": {
"security": [
@@ -2750,6 +2761,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": {
"get": {
"security": [
@@ -3877,40 +4044,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": {
"post": {
"security": [
@@ -5761,6 +5894,9 @@
"ip": {
"type": "string"
},
"is_deleted": {
"type": "integer"
},
"platform": {
"description": "windows,linux,mac,android,ios",
"type": "string"

View File

@@ -531,6 +531,8 @@ definitions:
type: integer
ip:
type: string
is_deleted:
type: integer
platform:
description: windows,linux,mac,android,ios
type: string
@@ -1347,27 +1349,6 @@ paths:
summary: 地址簿规则编辑
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:
post:
consumes:
@@ -1892,7 +1873,7 @@ paths:
summary: 登录选项
tags:
- 登录
/admin/login_log/delete:
/admin/login_log/batchDelete:
post:
consumes:
- application/json
@@ -1920,6 +1901,34 @@ paths:
summary: 登录日志批量删除
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}:
get:
consumes:
@@ -2413,6 +2422,101 @@ paths:
summary: 地址簿规则编辑
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:
get:
consumes:
@@ -3088,27 +3192,6 @@ paths:
summary: 设备编辑
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:
post:
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": {
"post": {
"description": "审计连接",
@@ -767,6 +733,40 @@ 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"
}
}
}
}
},
"/heartbeat": {
"post": {
"description": "心跳",
@@ -1178,43 +1178,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": {
"get": {
"security": [
@@ -1289,6 +1252,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": {

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": {
"post": {
"description": "审计连接",
@@ -760,6 +726,40 @@
}
}
},
"/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"
}
}
}
}
},
"/heartbeat": {
"post": {
"description": "心跳",
@@ -1171,43 +1171,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": {
"get": {
"security": [
@@ -1282,6 +1245,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": {

View File

@@ -598,27 +598,6 @@ paths:
summary: 标签
tags:
- 地址[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:
post:
consumes:
@@ -671,6 +650,27 @@ paths:
summary: 审计文件
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:
- 用户
/heartbeat:
post:
consumes:
@@ -936,29 +936,6 @@ paths:
summary: 提交系统信息
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:
get:
consumes:
@@ -1004,6 +981,25 @@ paths:
summary: 用户列表
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:
BearerAuth:
in: header

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

@@ -7,6 +7,7 @@ import (
"Gwen/service"
"github.com/gin-gonic/gin"
"net/http"
"os"
"time"
)
@@ -61,3 +62,25 @@ func (i *Index) Heartbeat(c *gin.Context) {
}
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, err := os.ReadFile("resources/version")
if err != nil {
response.Fail(c, 101, err.Error())
return
}
response.Success(
c,
string(v),
)
}

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

@@ -21,10 +21,13 @@ func ApiInit(g *gin.Engine) {
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{}
@@ -33,6 +36,7 @@ func ApiInit(g *gin.Engine) {
frg.POST("/login", l.Login)
}
{
o := &api.Oauth{}
// [method:POST] [uri:/api/oidc/auth]
@@ -52,11 +56,15 @@ func ApiInit(g *gin.Engine) {
if global.Config.App.WebClient == 1 {
WebClientRoutes(frg)
}
au := &api.Audit{}
//[method:POST] [uri:/api/audit/conn]
frg.POST("/audit/conn", au.AuditConn)
//[method:POST] [uri:/api/audit/file]
frg.POST("/audit/file", au.AuditFile)
{
au := &api.Audit{}
//[method:POST] [uri:/api/audit/conn]
frg.POST("/audit/conn", au.AuditConn)
//[method:POST] [uri:/api/audit/file]
frg.POST("/audit/file", au.AuditFile)
}
frg.Use(middleware.RustAuth())
{
u := &api.User{}

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 = "驗證碼錯誤。"

1
resources/version Normal file
View File

@@ -0,0 +1 @@
v1.0.0

View File

@@ -32,7 +32,7 @@
<title>RustDesk</title>
<script src="/webclient-config/index.js"></script>
<link rel="manifest" href="manifest.json" />
<script type="module" crossorigin src="js/dist/index.js?v=1bbc8b94"></script>
<script type="module" crossorigin src="js/dist/index.js?v=cabfd933"></script>
<link rel="modulepreload" href="js/dist/vendor.js?v=0b990c6e" />
<style>
html,
@@ -259,7 +259,7 @@
}
scriptLoaded = true;
var scriptTag = document.createElement("script");
scriptTag.src = "main.dart.js?v=f6f842b3";
scriptTag.src = "main.dart.js?v=060a626e";
scriptTag.type = "application/javascript";
document.body.append(scriptTag);
}

View File

@@ -18,7 +18,6 @@ var h = (u, e, i) => (ue(u, e, "read from private field"), i ? i.call(u) : e.get
}
}), je = (u, e, i) => (ue(u, e, "access private method"), i);
const sa = function () {
const e = document.createElement("link").relList;
if (e && e.supports && e.supports("modulepreload")) return;
@@ -7590,7 +7589,7 @@ class N4 {
let i = new Uint8Array(e.data);
this._recvDataCount += i.length;
const o = this._secretKey;
o && (o[2] += 1, i = Mn(i, o[2], o[0]));
o && (o[2] += 1, i = Kn(i, o[2], o[0]));
let a;
i.length == 0 ? a = new Uint8Array : a = this._isRendezvous ? this.parseRendezvous(i) : this.parseMessage(i), this._buf.push(a), this._eventHandlers.message && (this._isProcessing || this.processQueue())
}
@@ -7817,7 +7816,7 @@ const Co = {
RShift: "RShift",
CTRL_ALT_DEL: "CtrlAltDel",
LOCK_SCREEN: "LockScreen"
}, se = "1.3.6", po = "2024-12-22 23:23";
}, se = "1.3.7", po = "2025-01-21 01:12";
class A {
static setItem(e, i) {
@@ -8036,7 +8035,7 @@ async function zo() {
}
function Z(u) {
return Tn(u)
return Ln(u)
}
function c4(u) {
@@ -8044,7 +8043,7 @@ function c4(u) {
}
async function Po() {
return await Jn()
return await $n()
}
function O4() {
@@ -9176,7 +9175,7 @@ async function rn(u) {
function xe(u, e = void 0) {
const i = () => {
try {
Kn(new TextDecoder().decode(u.content)), fe(), e == null || e()
Xn(new TextDecoder().decode(u.content)), fe(), e == null || e()
} catch (o) {
console.error("Failed to copy to clipboard, ", o), document.hasFocus() || (q4 = u)
}
@@ -9240,7 +9239,7 @@ async function Nt(u, e, i = void 0) {
}
function $i() {
In("info", "Clipboard is synchronized", 2e3)
Un("info", "Clipboard is synchronized", 2e3)
}
window.addEventListener("focus", function () {
@@ -9887,7 +9886,7 @@ class Wt {
(f4 = this._ws) == null || f4.sendMessage({public_key: T});
return
}
const [E, c] = jn(), C = Nn(), D = On(C, l, E), B = K.fromPartial({asymmetric_value: c, symmetric_value: D});
const [E, c] = Wn(), C = Vn(), D = qn(C, l, E), B = K.fromPartial({asymmetric_value: c, symmetric_value: D});
return (x4 = this._ws) == null || x4.sendMessage({public_key: B}), (Ie = this._ws) == null || Ie.setSecretKey(C), console.log("secured"), !0
}
@@ -9905,7 +9904,7 @@ class Wt {
Re(o.colors, !1, a => {
a && (o.colors = a, m("cursor_data", o))
})
} else if (e != null && e.cursor_id) m("cursor_id", {id: e == null ? void 0 : e.cursor_id}); else if (e != null && e.cursor_position) m("cursor_position", e == null ? void 0 : e.cursor_position); else if (e != null && e.misc) this.handleMisc(e == null ? void 0 : e.misc); else if (e != null && e.audio_frame) Vn(e == null ? void 0 : e.audio_frame.data); else if (e != null && e.message_box) this.handleMsgBox(e == null ? void 0 : e.message_box); else if (e != null && e.peer_info) this.handleSyncPeerInfo(e.peer_info); else if (e.file_response) await this.handleFileResponse(e.file_response); else if (e.file_action) {
} else if (e != null && e.cursor_id) m("cursor_id", {id: e == null ? void 0 : e.cursor_id}); else if (e != null && e.cursor_position) m("cursor_position", e == null ? void 0 : e.cursor_position); else if (e != null && e.misc) this.handleMisc(e == null ? void 0 : e.misc); else if (e != null && e.audio_frame) Zn(e == null ? void 0 : e.audio_frame.data); else if (e != null && e.message_box) this.handleMsgBox(e == null ? void 0 : e.message_box); else if (e != null && e.peer_info) this.handleSyncPeerInfo(e.peer_info); else if (e.file_response) await this.handleFileResponse(e.file_response); else if (e.file_action) {
const o = e.file_action;
await this.handleFileAction(o)
}
@@ -10324,7 +10323,7 @@ class Wt {
}
handleMisc(e) {
if (e.audio_format) Wn(e.audio_format.channels, e.audio_format.sample_rate); else if (e.chat_message) m("chat_client_mode", {text: e.chat_message.text}); else if (e.permission_info) {
if (e.audio_format) Gn(e.audio_format.channels, e.audio_format.sample_rate); else if (e.chat_message) m("chat_client_mode", {text: e.chat_message.text}); else if (e.permission_info) {
const i = e.permission_info;
console.info("Change permission " + i.permission + " -> " + i.enabled);
let o;
@@ -10494,7 +10493,7 @@ class Wt {
inputKey(e, i, o, a, t, s, l) {
var c;
const E = So(e, Rn());
const E = So(e, Mn());
!E || (a && (e == "VK_MENU" || e == "RAlt") && (a = !1), t && (e == "VK_CONTROL" || e == "RControl") && (t = !1), s && (e == "VK_SHIFT" || e == "RShift") && (s = !1), l && (e == "Meta" || e == "RWin") && (l = !1), E.down = i, E.press = o, E.modifiers = this.getMod(a, t, s, l), (c = this._ws) == null || c.sendMessage({key_event: E}))
}
@@ -11117,7 +11116,6 @@ function Sn(u) {
return u.indexOf(":") > 0 ? u.split(":")[0] : u
}
const at = (u, e, i) => e && u.type == "SharedAb" ? Z(Zu([u.value, i.salt])) === Z(e) : !1,
ot = (u, e) => e && u.type == "PersonalAb" ? Z(u.value) === Z(e) : !1;
@@ -11241,14 +11239,66 @@ async function Pn(u) {
o.close(), console.error("Failed to query online states, no online response")
}
}
const Rn = "rustdesk-client";
function In() {
if (typeof navigator != "undefined") {
const u = navigator.platform.toLowerCase();
return u.includes("win") ? "windows" : u.includes("mac") ? "macos" : u.includes("linux") ? "linux" : u
}
return "unknown"
}
function Tn() {
const u = In();
return u === "windows" ? navigator.userAgent.includes("Win64") ? "x86_64" : "x86" : u === "macos" ? navigator.userAgent.includes("Intel") ? "x86_64" : "arm64" : navigator.userAgent.includes("x64") ? "x86_64" : "x86"
}
function jn() {
const u = navigator.userAgent;
let e = "", i = "";
if (u.includes("Windows")) {
e = "windows";
const o = u.match(/Windows NT (\d+\.\d+)/);
o && (i = o[1])
} else if (u.includes("Mac OS X")) {
e = "macos";
const o = u.match(/Mac OS X (\d+[._]\d+[._]\d+)/);
o && (i = o[1].replace(/_/g, "."))
} else if (u.includes("Linux")) {
e = "linux";
const o = u.match(/Linux\s*([\d.]+)?/);
o && o[1] && (i = o[1])
} else e = "unknown", i = "";
return e += "-" + navigator.userAgent, {os: e, os_version: i}
}
async function Nn(u) {
const e = "https://api.rustdesk.com/version/latest", {os: i, os_version: o} = jn(), a = Tn();
return [{os: i, os_version: o, arch: a, device_id: [], typ: u}, e]
}
async function On() {
try {
const [u, e] = await Nn(Rn);
return await (await fetch(e, {
method: "POST",
headers: {"Content-Type": "application/json"},
body: JSON.stringify(u)
})).json()
} catch {
return null
}
}
window.curConn = void 0;
window.isMobile = () => /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|ipad|iris|kindle|Android|Silk|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(navigator.userAgent) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(navigator.userAgent.substr(0, 4));
const ye = zt(), Yu = ye === Y4, H4 = ye === Be, Qu = ye === me;
function Rn() {
function Mn() {
return !isMobile()
}
@@ -11267,7 +11317,7 @@ function Vt(u, e, i, o) {
}
}
function In(u, e, i) {
function Un(u, e, i) {
onGlobalEvent(JSON.stringify({name: "toast", type: u, text: e, dur_msec: i}))
}
@@ -11340,20 +11390,20 @@ function Zt(u) {
return q.from_base64(u, q.base64_variants.ORIGINAL)
}
function Tn(u) {
function Ln(u) {
return q.to_base64(u, q.base64_variants.ORIGINAL)
}
function jn() {
function Wn() {
const u = q.crypto_box_keypair(), e = u.privateKey, i = u.publicKey;
return [e, i]
}
function Nn() {
function Vn() {
return q.crypto_secretbox_keygen()
}
function On(u, e, i) {
function qn(u, e, i) {
const o = Uint8Array.from(Array(24).fill(0));
return q.crypto_box_easy(u, o, e, i)
}
@@ -11370,7 +11420,7 @@ function st(u, e, i) {
return q.crypto_secretbox_easy(u, $4(e), i)
}
function Mn(u, e, i) {
function Kn(u, e, i) {
return q.crypto_secretbox_open_easy(u, $4(e), i)
}
@@ -11428,7 +11478,7 @@ window.setByName = (u, e) => {
e = JSON.parse(e), Ho(curConn, e.usb_hid, e.down == "true", e.lock_modes);
break;
case"send_mouse":
Un(e);
Hn(e);
break;
case"send_2fa":
curConn == null || curConn.send2fa(e);
@@ -11455,7 +11505,7 @@ window.setByName = (u, e) => {
e = JSON.parse(e), curConn.setFlutterUiOption(e.name, e.value);
break;
case"option:user:default":
Gn(e);
ur(e);
break;
case"option:session":
e = JSON.parse(e), curConn.setOption(e.name, e.value);
@@ -11473,12 +11523,12 @@ window.setByName = (u, e) => {
curConn.inputOsPassword(e);
break;
case"session_add_sync":
return Xn(e);
return tr(e);
case"session_start":
Yn();
ar();
break;
case"session_close":
$n();
or();
break;
case"elevate_direct":
curConn.elevateDirect();
@@ -11500,13 +11550,13 @@ window.setByName = (u, e) => {
curConn.changePreferCodec(e);
break;
case"cursor":
Hn(e);
Yn(e);
break;
case"enter_or_leave":
curConn == null || curConn.enterOrLeave(e);
break;
case"fullscreen":
e == "Y" ? er() : ir();
e == "Y" ? rr() : sr();
break;
case"send_note":
const i = Yt("conn");
@@ -11549,7 +11599,7 @@ window.setByName = (u, e) => {
curConn == null || curConn.sendChat(e);
break;
case"load_ab":
or();
dr();
break;
case"save_ab":
_o(e);
@@ -11558,7 +11608,7 @@ window.setByName = (u, e) => {
vo();
break;
case"load_group":
nr();
cr();
break;
case"save_group":
ko(e);
@@ -11575,7 +11625,7 @@ window.setByName = (u, e) => {
}
};
function Un(u) {
function Hn(u) {
if (!curConn) return;
let e = 0;
switch (u = JSON.parse(u), u.type) {
@@ -11618,11 +11668,11 @@ function Un(u) {
}
window.getByName = (u, e) => {
let i = Ln(u, e);
let i = Jn(u, e);
return typeof i == "string" || i instanceof String ? i : i == null || i == null ? "" : JSON.stringify(i)
};
function Ln(u, e) {
function Jn(u, e) {
var o, a, t, s;
switch (u) {
case"remember":
@@ -11669,10 +11719,10 @@ function Ln(u, e) {
case"version":
return se;
case"load_recent_peers":
Zn();
er();
break;
case"load_fav_peers":
Qn();
ir();
break;
case"fav":
return (a = A.getItem("fav")) != null ? a : "[]";
@@ -11726,7 +11776,7 @@ function Ln(u, e) {
case"peer_has_password":
return ((t = (Cu()[e] || {}).password) != null ? t : "") !== "";
case"fullscreen":
return tr() ? "Y" : "N";
return lr() ? "Y" : "N";
case"platform":
return curConn.getPlatform();
case"enable_trusted_devices":
@@ -11737,11 +11787,11 @@ function Ln(u, e) {
let ze = new Worker("./libopus.js?v=02816afa"), Qt;
function Wn(u, e) {
Qt = qn(u, e), ze.postMessage({channels: u, sampleRate: e})
function Gn(u, e) {
Qt = Qn(u, e), ze.postMessage({channels: u, sampleRate: e})
}
function Vn(u) {
function Zn(u) {
ze.postMessage(u, [u.buffer])
}
@@ -11749,7 +11799,7 @@ window.init = async () => {
try {
ze.onmessage = u => {
Qt.feed(u.data)
}, await Jt(), await zo(), await Pa(), await N.init(), console.log("init done"), onInitFinished()
}, await Jt(), await zo(), await Pa(), await N.init(), console.log("init done"), onInitFinished(), await On()
} catch (u) {
console.error("Failed to init: " + u.message), onInitFinished()
}
@@ -11758,11 +11808,11 @@ window.onunload = () => {
console.log("window close"), Ia()
};
function qn(u, e) {
function Qn(u, e) {
return new ra({channels: u, sampleRate: e, flushingTime: 2e3})
}
function Kn(u) {
function Xn(u) {
if (window.clipboardData && window.clipboardData.setData) return window.clipboardData.setData("Text", u);
if (document.queryCommandSupported && document.queryCommandSupported("copy")) {
var e = document.createElement("textarea");
@@ -11791,7 +11841,7 @@ function Q(u) {
}
}
function Hn(u) {
function Yn(u) {
let e = "auto";
if (u != "auto") try {
const t = JSON.parse(u);
@@ -11807,13 +11857,13 @@ function Hn(u) {
}
}
async function Jn() {
async function $n() {
await T4.ready;
const u = T4.crypto_sign_keypair();
return {publicKey: u.publicKey, privateKey: u.privateKey}
}
function Gn(u) {
function ur(u) {
try {
const e = JSON.parse(u), i = JSON.parse(A.getItem("user-default-options")) || {};
i[e.name] = e.value, A.setItem("user-default-options", JSON.stringify(i))
@@ -11855,22 +11905,23 @@ function Pe() {
return u.sort().reverse().map(e => e[2])
}
function Zn() {
function er() {
const u = Pe();
u && be("load_recent_peers", {peers: JSON.stringify(u)})
}
function Qn() {
function ir() {
var u;
try {
const e = (u = A.getItem("fav")) != null ? u : "[]", i = JSON.parse(e), o = Pe().filter(a => i.includes(a.id));
const e = (u = A.getItem("fav")) != null ? u : "[]", i = JSON.parse(e),
o = Pe().filter(a => i.includes(a.id));
o && be("load_fav_peers", {peers: JSON.stringify(o)})
} catch (e) {
console.error("Failed to load fav peers: " + e.message)
}
}
function Xn(u) {
function tr(u) {
var e;
try {
const i = JSON.parse(u), o = i.id;
@@ -11884,7 +11935,7 @@ function Xn(u) {
}
}
function Yn(u) {
function ar(u) {
try {
if (!e0()) return;
Kt()
@@ -11893,11 +11944,11 @@ function Yn(u) {
}
}
function $n(u) {
function or(u) {
Se()
}
function ur(u, e) {
function nr(u, e) {
function i(o) {
return /^([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}$/.test(o)
}
@@ -11925,7 +11976,7 @@ function Xt() {
if (u) return u;
const e = A.getItem("custom-rendezvous-server");
if (e) {
let i = ur(e, -2);
let i = nr(e, -2);
return i == e ? `http://${i}:${Ut - 2}` : `http://${i}`
}
return "https://admin.rustdesk.com"
@@ -11985,28 +12036,28 @@ async function ea(u, e) {
})
}
function er() {
function rr() {
const u = document.documentElement;
u.requestFullscreen ? u.requestFullscreen() : u.mozRequestFullScreen ? u.mozRequestFullScreen() : u.webkitRequestFullscreen ? u.webkitRequestFullscreen() : u.msRequestFullscreen && u.msRequestFullscreen()
}
function ir() {
function sr() {
document.exitFullscreen ? document.exitFullscreen() : document.mozCancelFullScreen ? document.mozCancelFullScreen() : document.webkitExitFullscreen ? document.webkitExitFullscreen() : document.msExitFullscreen && document.msExitFullscreen()
}
function tr() {
function lr() {
return document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement
}
var lt = !1;
function ar() {
function Er() {
lt || (console.log("listen fullscreen"), lt = !0, document.addEventListener("fullscreenchange", () => onFullscreenChanged(!!document.fullscreenElement)), document.addEventListener("mozfullscreenchange", () => onFullscreenChanged(!!document.mozFullScreen)), document.addEventListener("webkitfullscreenchange", () => onFullscreenChanged(!!document.webkitFullscreenElement)), document.addEventListener("msfullscreenchange", () => onFullscreenChanged(!!document.msFullscreenElement)))
}
ar();
Er();
async function or() {
async function dr() {
try {
let u = await xt();
onLoadAbFinished(JSON.stringify(u))
@@ -12015,7 +12066,7 @@ async function or() {
}
}
async function nr() {
async function cr() {
try {
let u = await go();
onLoadGroupFinished(JSON.stringify(u))
@@ -12077,4 +12128,4 @@ if (Et) {
const i = document.querySelector("input#password").value;
i && (document.querySelector("div#password").style.display = "none", e0().login(i))
}
}
}

View File

@@ -4685,7 +4685,7 @@ Kui soovid juurdep\xE4\xE4su seadmele avalikus serveris, sisesta "<id>@public",
}, sl: {
Status: "Stanje",
"Your Desktop": "Va\u0161e namizje",
desk_tip: "Do va\u0161ega namizja lahko dostopate s spodnjim IDjem in geslom",
desk_tip: "S spodnjim IDjem in geslom omogo\u010Dite oddaljeni nadzor va\u0161ega ra\u010Dunalnika",
Password: "Geslo",
Ready: "Pripravljen",
Established: "Povezava vzpostavljena",
@@ -4872,7 +4872,7 @@ Kui soovid juurdep\xE4\xE4su seadmele avalikus serveris, sisesta "<id>@public",
"Logging in...": "Prijavljanje...",
"Enable RDP session sharing": "Omogo\u010Di deljenje RDP seje",
"Auto Login": "Samodejna prijava",
"Enable direct IP access": "Omogo\u010Di neposredni dostop preko IP",
"Enable direct IP access": "Omogo\u010Di neposredni dostop preko IP naslova",
Rename: "Preimenuj",
Space: "Prazno",
"Create desktop shortcut": "Ustvari bli\u017Enjico na namizju",
@@ -5046,7 +5046,7 @@ Kui soovid juurdep\xE4\xE4su seadmele avalikus serveris, sisesta "<id>@public",
Recording: "Snemanje",
Directory: "Imenik",
"Automatically record incoming sessions": "Samodejno snemaj vhodne seje",
"Automatically record outgoing sessions": "",
"Automatically record outgoing sessions": "Samodejno snemaj odhodne seje",
Change: "Spremeni",
"Start session recording": "Za\u010Dni snemanje seje",
"Stop session recording": "Ustavi snemanje seje",
@@ -5094,8 +5094,8 @@ Kui soovid juurdep\xE4\xE4su seadmele avalikus serveris, sisesta "<id>@public",
"Select local keyboard type": "Izberite lokalno vrsto tipkovnice",
software_render_tip: "\u010Ce na Linuxu uporabljate Nvidino grafi\u010Dno kartico in se oddaljeno okno zapre takoj po vzpostavitvi povezave, lahko pomaga preklop na odprtokodni gonilnik Nouveau in uporaba programskega upodabljanja. Potreben je ponovni zagon programa.",
"Always use software rendering": "Vedno uporabi programsko upodabljanje",
config_input: "Za nadzor oddaljenega namizja s tipkovnico, rabi RustDesk pravico \xBBNadzor vnosa\xAB.",
config_microphone: "Za zajem zvoka, rabi RustDesk pravico \xBBSnemanje zvoka\xAB.",
config_input: "RustDesk potrebuje pravico \xBBNadzor vnosa\xAB za nadzor oddaljenega namizja s tipkovnico.",
config_microphone: "RustDesk potrebuje pravico \xBBSnemanje zvoka\xAB za zajemanje zvoka.",
request_elevation_tip: "Lahko tudi zaprosite za dvig pravic, \u010De je kdo na oddaljeni strani.",
Wait: "\u010Cakaj",
"Elevation Error": "Napaka pri povzdigovanju",
@@ -5128,7 +5128,7 @@ Kui soovid juurdep\xE4\xE4su seadmele avalikus serveris, sisesta "<id>@public",
"Voice call": "Glasovni klic",
"Text chat": "Besedilni klepet",
"Stop voice call": "Prekini glasovni klic",
relay_hint_tip: "Morda neposredna povezava ni mo\u017Ena; lahko se poikusite povezati preko posrednika. \u010Ce \u017Eelite uporabiti posrednika ob prvem poizkusu vzpotavljanja povezave, lahko na konec IDja dodate \xBB/r\xAB, ali pa izberete mo\u017Enost \xBBVedno pove\u017Ei preko posrednika\xAB v kartici nedavnih sej, \u010De le-ta obstja.",
relay_hint_tip: "Morda neposredna povezava ni mo\u017Ena; lahko se poizkusite povezati preko posrednika. \u010Ce \u017Eelite uporabiti posrednika ob prvem poizkusu vzpotavljanja povezave, lahko na konec IDja dodate \xBB/r\xAB, ali pa izberete mo\u017Enost \xBBVedno pove\u017Ei preko posrednika\xAB v kartici nedavnih sej, \u010De le-ta obstja.",
Reconnect: "Ponovna povezava",
Codec: "Kodek",
Resolution: "Lo\u010Dljivost",
@@ -5343,13 +5343,13 @@ Lahko se pove\u017Eete na druge naprave, druge naprave pa se k vam ne morejo pov
web_id_input_tip: `Vnesete lahko ID iz istega stre\u017Enika, neposredni dostop preko IP naslova v spletnem odjemalcu ni podprt.
\u010Ce \u017Eelite dostopati do naprave na drugem stre\u017Eniku, pripnite naslov stre\u017Enika (<id>@<naslov_stre\u017Enika>?key=<klju\u010D>), npr. 9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.
\u010Ce \u017Eelite dostopati do naprave na javnem stre\u017Eniku, vnesite \xBB<id>@public\xAB; klju\u010D za javni stre\u017Enik ni potreben.`,
Download: "",
"Upload folder": "",
"Upload files": "",
"Clipboard is synchronized": "",
"Update client clipboard": "",
Untagged: "",
"new-version-of-{}-tip": ""
Download: "Prenos",
"Upload folder": "Nalo\u017Ei mapo",
"Upload files": "Nalo\u017Ei datoteke",
"Clipboard is synchronized": "Odlo\u017Ei\u0161\u010De je usklajeno",
"Update client clipboard": "Osve\u017Ei odjemal\u010Devo odlo\u017Ei\u0161\u010De",
Untagged: "Neozna\u010Deno",
"new-version-of-{}-tip": "Na voljo je nova razli\u010Dica {}"
}, ko: {
Status: "\uC0C1\uD0DC",
"Your Desktop": "\uB0B4 \uB370\uC2A4\uD06C\uD0D1",
@@ -6683,7 +6683,7 @@ Ja v\u0113laties piek\u013C\u016Bt ier\u012Bcei publiskaj\u0101 server\u012B, l\
"Clipboard is synchronized": "Starpliktuve ir sinhroniz\u0113ta",
"Update client clipboard": "Atjaunin\u0101t klienta starpliktuvi",
Untagged: "Neatz\u012Bm\u0113ts",
"new-version-of-{}-tip": ""
"new-version-of-{}-tip": "Ir pieejama jauna {} versija"
}, pl: {
Status: "Status",
"Your Desktop": "Tw\xF3j pulpit",
@@ -18966,7 +18966,7 @@ H\xE3y t\xECm ai \u0111\xF3 \u0111\u1EC3 k\u1EBFt n\u1ED1i c\xF9ng v\xE0 th\xEAm
Dark: "\u9ED1\u6697",
Light: "\u660E\u4EAE",
"Follow System": "\u8DDF\u968F\u7CFB\u7EDF",
"Enable hardware codec": "\u4F7F\u80FD\u786C\u4EF6\u7F16\u89E3\u7801",
"Enable hardware codec": "\u542F\u7528\u786C\u4EF6\u7F16\u89E3\u7801",
"Unlock Security Settings": "\u89E3\u9501\u5B89\u5168\u8BBE\u7F6E",
"Enable audio": "\u5141\u8BB8\u4F20\u8F93\u97F3\u9891",
"Unlock Network Settings": "\u89E3\u9501\u7F51\u7EDC\u8BBE\u7F6E",
@@ -21967,8 +21967,8 @@ Si quieres accedder a un dispositivo en un servidor p\xFAblico, por favor, intro
"Upload files": "Subir archivos",
"Clipboard is synchronized": "Portapapeles sincronizado",
"Update client clipboard": "Actualizar portapapeles del cliente",
Untagged: "",
"new-version-of-{}-tip": ""
Untagged: "Sin itiquetar",
"new-version-of-{}-tip": "Hay una nueva versi\xF3n de {} disponible"
}, sr: {
Status: "Status",
"Your Desktop": "Va\u0161a radna povr\u0161ina",
@@ -23662,7 +23662,7 @@ Ha egy nyilv\xE1nos kiszolg\xE1l\xF3n l\xE9v\u0151 eszk\xF6zh\xF6z szeretne hozz
Recording: "\u9304\u88FD",
Directory: "\u8DEF\u5F91",
"Automatically record incoming sessions": "\u81EA\u52D5\u9304\u88FD\u9023\u5165\u7684\u5DE5\u4F5C\u968E\u6BB5",
"Automatically record outgoing sessions": "",
"Automatically record outgoing sessions": "\u81EA\u52D5\u9304\u88FD\u9023\u51FA\u7684\u5DE5\u4F5C\u968E\u6BB5",
Change: "\u8B8A\u66F4",
"Start session recording": "\u958B\u59CB\u9304\u5F71",
"Stop session recording": "\u505C\u6B62\u9304\u5F71",

File diff suppressed because one or more lines are too long

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

@@ -29,6 +29,7 @@ func (s *TagService) ListByUserId(userId uint) (res *model.TagList) {
func (s *TagService) ListByUserIdAndCollectionId(userId, cid uint) (res *model.TagList) {
res = s.List(1, 1000, func(tx *gorm.DB) {
tx.Where("user_id = ? and collection_id = ?", userId, cid)
tx.Order("name asc")
})
return
}

View File

@@ -82,7 +82,7 @@ func (us *UserService) Login(u *model.User, llog *model.LoginLog) *model.UserTok
Token: token,
DeviceUuid: llog.Uuid,
DeviceId: llog.DeviceId,
ExpiredAt: time.Now().Add(time.Hour * 24 * 7).Unix(),
ExpiredAt: time.Now().Add(time.Second * time.Duration(global.Config.App.TokenExpire)).Unix(),
}
global.DB.Create(ut)
llog.UserTokenId = ut.UserId
@@ -452,7 +452,7 @@ func (us *UserService) getAdminUserCount() int64 {
}
func (us *UserService) RefreshAccessToken(ut *model.UserToken) {
ut.ExpiredAt = time.Now().Add(time.Hour * 24 * 7).Unix()
ut.ExpiredAt = time.Now().Add(time.Second * time.Duration(global.Config.App.TokenExpire)).Unix()
global.DB.Model(ut).Update("expired_at", ut.ExpiredAt)
}
func (us *UserService) AutoRefreshAccessToken(ut *model.UserToken) {