add login fail warn &

add web client on/off &
up admin peer filter &
upgrade web client
This commit is contained in:
ljw
2024-10-14 10:43:29 +08:00
parent 0eb733cc33
commit b4965e8885
20 changed files with 463 additions and 379 deletions

View File

@@ -151,6 +151,8 @@
```yaml ```yaml
lang: "en" lang: "en"
app:
web-client: 1 # 1:启用 0:禁用
gin: gin:
api-addr: "0.0.0.0:21114" api-addr: "0.0.0.0:21114"
mode: "release" mode: "release"
@@ -181,12 +183,13 @@ logger:
变量名前缀是`RUSTDESK_API`,环境变量如果存在将覆盖配置文件中的配置 变量名前缀是`RUSTDESK_API`,环境变量如果存在将覆盖配置文件中的配置
| 变量名 | 说明 | 示例 | | 变量名 | 说明 | 示例 |
|-------------------------------------|--------------------------------------|-----------------------------| |------------------------------------|--------------------------------------|-----------------------------|
| TZ | 时区 | Asia/Shanghai | | TZ | 时区 | Asia/Shanghai |
| RUSTDESK_API_LANG | 语言 | `en`,`zh-CN` | | RUSTDESK_API_LANG | 语言 | `en`,`zh-CN` |
| RUSTDESK_API_APP_WEB_CLIENT | 是否启用web-client; 1:启用,0:不启用; 默认启用 | 1 |
| -----GIN配置----- | ---------- | ---------- | | -----GIN配置----- | ---------- | ---------- |
| RUSTDESK_API_GIN_TRUST_PROXY | 信任的代理IP列表以`,`分割,默认信任所有 | 192.168.1.2,192.168.1.3 | | RUSTDESK_API_GIN_TRUST_PROXY | 信任的代理IP列表以`,`分割,默认信任所有 | 192.168.1.2,192.168.1.3 |
| -----------GORM配置------------------ | ------------------------------------ | --------------------------- | | -----------GORM配置---------------- | ------------------------------------ | --------------------------- |
| RUSTDESK_API_GORM_TYPE | 数据库类型sqlite或者mysql默认sqlite | sqlite | | RUSTDESK_API_GORM_TYPE | 数据库类型sqlite或者mysql默认sqlite | sqlite |
| RUSTDESK_API_GORM_MAX_IDLE_CONNS | 数据库最大空闲连接数 | 10 | | RUSTDESK_API_GORM_MAX_IDLE_CONNS | 数据库最大空闲连接数 | 10 |
| RUSTDESK_API_GORM_MAX_OPEN_CONNS | 数据库最大打开连接数 | 100 | | RUSTDESK_API_GORM_MAX_OPEN_CONNS | 数据库最大打开连接数 | 100 |

View File

@@ -157,6 +157,8 @@ installation are `admin` `admin`, please change the password immediately.
```yaml ```yaml
lang: "en" lang: "en"
app:
web-client: 1 # web client route 1:open 0:close
gin: gin:
api-addr: "0.0.0.0:21114" api-addr: "0.0.0.0:21114"
mode: "release" mode: "release"
@@ -187,22 +189,23 @@ logger:
The prefix for variable names is `RUSTDESK_API`. If environment variables exist, they will override the configurations in the configuration file. 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 | | Variable Name | Description | Example |
|------------------------------------|-----------------------------------------------------------|--------------------------------| |------------------------------------|-----------------------------------------------------------|-------------------------------|
| TZ | timezone | Asia/Shanghai | | TZ | timezone | Asia/Shanghai |
| RUSTDESK_API_LANG | Language | `en`,`zh-CN` | | RUSTDESK_API_LANG | Language | `en`,`zh-CN` |
| ----- GIN Configuration ----- | --------------------------------------- | ------------------------------ | | RUSTDESK_API_APP_WEB_CLIENT | web client on/off; 1: on, 0 off, deault 1 | 1 |
| ----- GIN Configuration ----- | --------------------------------------- | ----------------------------- |
| RUSTDESK_API_GIN_TRUST_PROXY | Trusted proxy IPs, separated by commas. | 192.168.1.2,192.168.1.3 | | RUSTDESK_API_GIN_TRUST_PROXY | Trusted proxy IPs, separated by commas. | 192.168.1.2,192.168.1.3 |
| ----- GORM Configuration ----- | --------------------------------------- | ------------------------------ | | ----- GORM Configuration ----- | --------------------------------------- | ----------------------------- |
| RUSTDESK_API_GORM_TYPE | Database type (`sqlite` or `mysql`). Default is `sqlite`. | sqlite | | 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_IDLE_CONNS | Maximum idle connections | 10 |
| RUSTDESK_API_GORM_MAX_OPEN_CONNS | Maximum open connections | 100 | | RUSTDESK_API_GORM_MAX_OPEN_CONNS | Maximum open connections | 100 |
| RUSTDESK_API_RUSTDESK_PERSONAL | Open Personal Api 1:Enable,0:Disable | 1 | | RUSTDESK_API_RUSTDESK_PERSONAL | Open Personal Api 1:Enable,0:Disable | 1 |
| ----- MYSQL Configuration ----- | --------------------------------------- | ------------------------------ | | ----- MYSQL Configuration ----- | --------------------------------------- | ----------------------------- |
| RUSTDESK_API_MYSQL_USERNAME | MySQL username | root | | RUSTDESK_API_MYSQL_USERNAME | MySQL username | root |
| RUSTDESK_API_MYSQL_PASSWORD | MySQL password | 111111 | | RUSTDESK_API_MYSQL_PASSWORD | MySQL password | 111111 |
| RUSTDESK_API_MYSQL_ADDR | MySQL address | 192.168.1.66:3306 | | RUSTDESK_API_MYSQL_ADDR | MySQL address | 192.168.1.66:3306 |
| RUSTDESK_API_MYSQL_DBNAME | MySQL database name | rustdesk | | RUSTDESK_API_MYSQL_DBNAME | MySQL database name | rustdesk |
| ----- RUSTDESK Configuration ----- | --------------------------------------- | ------------------------------ | | ----- RUSTDESK Configuration ----- | --------------------------------------- | ----------------------------- |
| RUSTDESK_API_RUSTDESK_ID_SERVER | Rustdesk ID server address | 192.168.1.66:21116 | | 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_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_API_SERVER | Rustdesk API server address | http://192.168.1.66:21114 |

View File

@@ -1,4 +1,6 @@
lang: "zh-CN" lang: "zh-CN"
app:
web-client: 1 # 1:启用 0:禁用
gin: gin:
api-addr: "0.0.0.0:21114" api-addr: "0.0.0.0:21114"
mode: "release" #release,debug,test mode: "release" #release,debug,test
@@ -16,7 +18,7 @@ mysql:
rustdesk: rustdesk:
id-server: "192.168.1.66:21116" id-server: "192.168.1.66:21116"
relay-server: "192.168.1.66:21117" relay-server: "192.168.1.66:21117"
api-server: "http://192.168.1.66:21114" api-server: "http://127.0.0.1:21114"
key: "123456789" key: "123456789"
personal: 1 personal: 1
logger: logger:

View File

@@ -14,8 +14,13 @@ const (
DefaultConfig = "conf/config.yaml" DefaultConfig = "conf/config.yaml"
) )
type App struct {
WebClient int `mapstructure:"web-client"`
}
type Config struct { type Config struct {
Lang string `mapstructure:"lang"` Lang string `mapstructure:"lang"`
App App
Gorm Gorm Gorm Gorm
Mysql Mysql Mysql Mysql
Gin Gin Gin Gin

View File

@@ -7,6 +7,7 @@ import (
adResp "Gwen/http/response/admin" adResp "Gwen/http/response/admin"
"Gwen/model" "Gwen/model"
"Gwen/service" "Gwen/service"
"fmt"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
@@ -28,18 +29,21 @@ func (ct *Login) Login(c *gin.Context) {
f := &admin.Login{} f := &admin.Login{}
err := c.ShouldBindJSON(f) err := c.ShouldBindJSON(f)
if err != nil { if err != nil {
global.Logger.Warn(fmt.Sprintf("Login Fail: %s %s %s", "ParamsError", c.RemoteIP(), c.ClientIP()))
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error()) response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return return
} }
errList := global.Validator.ValidStruct(c, f) errList := global.Validator.ValidStruct(c, f)
if len(errList) > 0 { if len(errList) > 0 {
global.Logger.Warn(fmt.Sprintf("Login Fail: %s %s %s", "ParamsError", c.RemoteIP(), c.ClientIP()))
response.Fail(c, 101, errList[0]) response.Fail(c, 101, errList[0])
return return
} }
u := service.AllService.UserService.InfoByUsernamePassword(f.Username, f.Password) u := service.AllService.UserService.InfoByUsernamePassword(f.Username, f.Password)
if u.Id == 0 { if u.Id == 0 {
global.Logger.Warn(fmt.Sprintf("Login Fail: %s %s %s", "UsernameOrPasswordError", c.RemoteIP(), c.ClientIP()))
response.Fail(c, 101, response.TranslateMsg(c, "UsernameOrPasswordError")) response.Fail(c, 101, response.TranslateMsg(c, "UsernameOrPasswordError"))
return return
} }

View File

@@ -96,6 +96,12 @@ func (ct *Peer) List(c *gin.Context) {
lt := time.Now().Unix() + int64(query.TimeAgo) lt := time.Now().Unix() + int64(query.TimeAgo)
tx.Where("last_online_time > ?", lt) tx.Where("last_online_time > ?", lt)
} }
if query.Id != "" {
tx.Where("id like ?", "%"+query.Id+"%")
}
if query.Hostname != "" {
tx.Where("hostname like ?", "%"+query.Hostname+"%")
}
}) })
response.Success(c, res) response.Success(c, res)
} }

View File

@@ -9,9 +9,9 @@ import (
type Rustdesk struct { type Rustdesk struct {
} }
// ServerConfig 服务配置 // ServerConfig RUSTDESK服务配置
// @Tags ADMIN // @Tags ADMIN
// @Summary 服务配置 // @Summary RUSTDESK服务配置
// @Description 服务配置,给webclient提供api-server // @Description 服务配置,给webclient提供api-server
// @Accept json // @Accept json
// @Produce json // @Produce json
@@ -28,3 +28,19 @@ func (r *Rustdesk) ServerConfig(c *gin.Context) {
} }
response.Success(c, cf) response.Success(c, cf)
} }
// AppConfig APP服务配置
// @Tags ADMIN
// @Summary APP服务配置
// @Description APP服务配置
// @Accept json
// @Produce json
// @Success 200 {object} response.Response
// @Failure 500 {object} response.Response
// @Router /admin/app-config [get]
// @Security token
func (r *Rustdesk) AppConfig(c *gin.Context) {
response.Success(c, &gin.H{
"web_client": global.Config.App.WebClient,
})
}

View File

@@ -8,7 +8,6 @@ import (
"Gwen/model" "Gwen/model"
"Gwen/service" "Gwen/service"
"encoding/json" "encoding/json"
"fmt"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"net/http" "net/http"
"strconv" "strconv"
@@ -402,7 +401,7 @@ func (a *Ab) PeerAdd(c *gin.Context) {
response.Error(c, response.TranslateMsg(c, "ParamsError")+err.Error()) response.Error(c, response.TranslateMsg(c, "ParamsError")+err.Error())
return return
} }
fmt.Println(f) //fmt.Println(f)
u := service.AllService.UserService.CurUser(c) u := service.AllService.UserService.CurUser(c)
f.UserId = u.Id f.UserId = u.Id
ab := f.ToAddressBook() ab := f.ToAddressBook()

View File

@@ -8,6 +8,7 @@ import (
"Gwen/model" "Gwen/model"
"Gwen/service" "Gwen/service"
"encoding/json" "encoding/json"
"fmt"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"net/http" "net/http"
) )
@@ -30,12 +31,14 @@ func (l *Login) Login(c *gin.Context) {
err := c.ShouldBindJSON(f) err := c.ShouldBindJSON(f)
//fmt.Println(f) //fmt.Println(f)
if err != nil { if err != nil {
global.Logger.Warn(fmt.Sprintf("Login Fail: %s %s %s", "ParamsError", c.RemoteIP(), c.ClientIP()))
response.Error(c, response.TranslateMsg(c, "ParamsError")+err.Error()) response.Error(c, response.TranslateMsg(c, "ParamsError")+err.Error())
return return
} }
errList := global.Validator.ValidStruct(c, f) errList := global.Validator.ValidStruct(c, f)
if len(errList) > 0 { if len(errList) > 0 {
global.Logger.Warn(fmt.Sprintf("Login Fail: %s %s %s", "ParamsError", c.RemoteIP(), c.ClientIP()))
response.Error(c, errList[0]) response.Error(c, errList[0])
return return
} }
@@ -43,6 +46,7 @@ func (l *Login) Login(c *gin.Context) {
u := service.AllService.UserService.InfoByUsernamePassword(f.Username, f.Password) u := service.AllService.UserService.InfoByUsernamePassword(f.Username, f.Password)
if u.Id == 0 { if u.Id == 0 {
global.Logger.Warn(fmt.Sprintf("Login Fail: %s %s %s", "UsernameOrPasswordError", c.RemoteIP(), c.ClientIP()))
response.Error(c, response.TranslateMsg(c, "UsernameOrPasswordError")) response.Error(c, response.TranslateMsg(c, "UsernameOrPasswordError"))
return return
} }

View File

@@ -22,7 +22,6 @@ type Peer struct {
// @Success 200 {string} string "SYSINFO_UPDATED,ID_NOT_FOUND" // @Success 200 {string} string "SYSINFO_UPDATED,ID_NOT_FOUND"
// @Failure 500 {object} response.ErrorResponse // @Failure 500 {object} response.ErrorResponse
// @Router /sysinfo [post] // @Router /sysinfo [post]
// @Security BearerAuth
func (p *Peer) SysInfo(c *gin.Context) { func (p *Peer) SysInfo(c *gin.Context) {
f := &requstform.PeerForm{} f := &requstform.PeerForm{}
err := c.ShouldBindBodyWith(f, binding.JSON) err := c.ShouldBindBodyWith(f, binding.JSON)
@@ -30,17 +29,28 @@ func (p *Peer) SysInfo(c *gin.Context) {
response.Error(c, response.TranslateMsg(c, "ParamsError")+err.Error()) response.Error(c, response.TranslateMsg(c, "ParamsError")+err.Error())
return return
} }
fpe := f.ToPeer()
pe := service.AllService.PeerService.FindById(f.Id) pe := service.AllService.PeerService.FindById(f.Id)
if pe == nil || pe.RowId == 0 { if pe.RowId == 0 {
pe = f.ToPeer() pe = f.ToPeer()
pe.UserId = service.AllService.UserService.FindLatestUserIdFromLoginLogByUuid(pe.Uuid)
err = service.AllService.PeerService.Create(pe) err = service.AllService.PeerService.Create(pe)
if err != nil { if err != nil {
response.Error(c, response.TranslateMsg(c, "OperationFailed")+err.Error()) response.Error(c, response.TranslateMsg(c, "OperationFailed")+err.Error())
return return
} }
} else {
if pe.UserId == 0 {
pe.UserId = service.AllService.UserService.FindLatestUserIdFromLoginLogByUuid(pe.Uuid)
}
fpe.RowId = pe.RowId
fpe.UserId = pe.UserId
err = service.AllService.PeerService.Update(fpe)
if err != nil {
response.Error(c, response.TranslateMsg(c, "OperationFailed")+err.Error())
return
}
} }
//SYSINFO_UPDATED 上传成功 //SYSINFO_UPDATED 上传成功
//ID_NOT_FOUND 下次心跳会上传 //ID_NOT_FOUND 下次心跳会上传
//直接响应文本 //直接响应文本

View File

@@ -7,7 +7,7 @@ import (
func RustAuth() gin.HandlerFunc { func RustAuth() gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
//fmt.Println(c.Request.Header) //fmt.Println(c.Request.URL, c.Request.Header)
//获取HTTP_AUTHORIZATION //获取HTTP_AUTHORIZATION
token := c.GetHeader("Authorization") token := c.GetHeader("Authorization")
if token == "" { if token == "" {

View File

@@ -36,4 +36,6 @@ func (f *PeerForm) ToPeer() *model.Peer {
type PeerQuery struct { type PeerQuery struct {
PageQuery PageQuery
TimeAgo int `json:"time_ago" form:"time_ago"` TimeAgo int `json:"time_ago" form:"time_ago"`
Id string `json:"id" form:"id"`
Hostname string `json:"hostname" form:"hostname"`
} }

View File

@@ -30,6 +30,7 @@ func Init(g *gin.Engine) {
rs := &admin.Rustdesk{} rs := &admin.Rustdesk{}
adg.GET("/server-config", rs.ServerConfig) adg.GET("/server-config", rs.ServerConfig)
adg.GET("/app-config", rs.AppConfig)
//访问静态文件 //访问静态文件
//g.StaticFS("/upload", http.Dir(global.Config.Gin.ResourcesPath+"/upload")) //g.StaticFS("/upload", http.Dir(global.Config.Gin.ResourcesPath+"/upload"))

View File

@@ -47,17 +47,11 @@ func ApiInit(g *gin.Engine) {
frg.POST("/sysinfo", pe.SysInfo) frg.POST("/sysinfo", pe.SysInfo)
} }
{ if global.Config.App.WebClient == 1 {
w := &api.WebClient{} WebClientRoutes(frg)
frg.POST("/shared-peer", w.SharedPeer)
} }
frg.Use(middleware.RustAuth()) frg.Use(middleware.RustAuth())
{
w := &api.WebClient{}
frg.POST("/server-config", w.ServerConfig)
}
{ {
u := &api.User{} u := &api.User{}
frg.GET("/user/info", u.Info) frg.GET("/user/info", u.Info)
@@ -115,3 +109,14 @@ func PersonalRoutes(frg *gin.RouterGroup) {
} }
} }
func WebClientRoutes(frg *gin.RouterGroup) {
w := &api.WebClient{}
{
frg.POST("/shared-peer", w.SharedPeer)
}
{
frg.POST("/server-config", middleware.RustAuth(), w.ServerConfig)
}
}

View File

@@ -10,7 +10,13 @@ import (
func WebInit(g *gin.Engine) { func WebInit(g *gin.Engine) {
i := &web.Index{} i := &web.Index{}
g.GET("/", i.Index) g.GET("/", i.Index)
if global.Config.App.WebClient == 1 {
g.GET("/webclient-config/index.js", i.ConfigJs) g.GET("/webclient-config/index.js", i.ConfigJs)
}
if global.Config.App.WebClient == 1 {
g.StaticFS("/webclient", http.Dir(global.Config.Gin.ResourcesPath+"/web")) g.StaticFS("/webclient", http.Dir(global.Config.Gin.ResourcesPath+"/web"))
}
g.StaticFS("/_admin", http.Dir(global.Config.Gin.ResourcesPath+"/admin")) g.StaticFS("/_admin", http.Dir(global.Config.Gin.ResourcesPath+"/admin"))
} }

View File

@@ -16,6 +16,7 @@ type SqliteConfig struct {
func NewSqlite(sqliteConf *SqliteConfig) *gorm.DB { func NewSqlite(sqliteConf *SqliteConfig) *gorm.DB {
db, err := gorm.Open(sqlite.Open("./data/rustdeskapi.db"), &gorm.Config{ db, err := gorm.Open(sqlite.Open("./data/rustdeskapi.db"), &gorm.Config{
DisableForeignKeyConstraintWhenMigrating: true,
Logger: logger.New( Logger: logger.New(
global.Logger, // io writer global.Logger, // io writer
logger.Config{ logger.Config{

File diff suppressed because one or more lines are too long

View File

@@ -1,11 +1,14 @@
import Connection from "./connection"; import Connection from "./connection";
import _sodium from "libsodium-wrappers"; import _sodium from "libsodium-wrappers";
import { CursorData } from "./message";
import {loadVp9} from "./codec"; import {loadVp9} from "./codec";
import {checkIfRetry, version} from "./gen_js_from_hbb"; import {checkIfRetry, version} from "./gen_js_from_hbb";
import {initZstd, translate} from "./common"; import {initZstd, translate} from "./common";
import PCMPlayer from "pcm-player"; import PCMPlayer from "pcm-player";
import {getServerConf} from "./ljw";
window.myconsole = (...args) => {
console.log(args);
}
window.curConn = undefined; window.curConn = undefined;
window.isMobile = () => { window.isMobile = () => {
return /(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) return /(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)
@@ -126,6 +129,7 @@ export function newConn() {
} }
let sodium; let sodium;
export async function verify(signed, pk) { export async function verify(signed, pk) {
if (!sodium) { if (!sodium) {
await _sodium.ready; await _sodium.ready;
@@ -178,6 +182,7 @@ export function decrypt(signed, nonce, key) {
} }
window.setByName = (name, value) => { window.setByName = (name, value) => {
myconsole('setByName', name, value);
switch (name) { switch (name) {
case 'remote_id': case 'remote_id':
localStorage.setItem('remote-id', value); localStorage.setItem('remote-id', value);
@@ -256,6 +261,9 @@ window.setByName = (name, value) => {
case 'option': case 'option':
value = JSON.parse(value); value = JSON.parse(value);
localStorage.setItem(value.name, value.value); localStorage.setItem(value.name, value.value);
if (value.name === 'access_token' && value.value) {
getServerConf(value.value);
}
break; break;
case 'peer_option': case 'peer_option':
value = JSON.parse(value); value = JSON.parse(value);
@@ -271,6 +279,7 @@ window.setByName = (name, value) => {
window.getByName = (name, arg) => { window.getByName = (name, arg) => {
let v = _getByName(name, arg); let v = _getByName(name, arg);
myconsole('getByName', name, arg, v);
if (typeof v == 'string' || v instanceof String) return v; if (typeof v == 'string' || v instanceof String) return v;
if (v == undefined || v == null) return ''; if (v == undefined || v == null) return '';
return JSON.stringify(v); return JSON.stringify(v);
@@ -299,7 +308,11 @@ function _getByName(name, arg) {
case 'toggle_option': case 'toggle_option':
return curConn.getOption(arg) || false; return curConn.getOption(arg) || false;
case 'option': case 'option':
return localStorage.getItem(arg); const v = localStorage.getItem(arg);
if (arg === 'access_token' && v) {
getServerConf(v);
}
return v;
case 'image_quality': case 'image_quality':
return curConn.getImageQuality(); return curConn.getImageQuality();
case 'translate': case 'translate':
@@ -336,7 +349,8 @@ window.init = async () => {
opusWorker.onmessage = (e) => { opusWorker.onmessage = (e) => {
pcmPlayer.feed(e.data); pcmPlayer.feed(e.data);
} }
loadVp9(() => { }); loadVp9(() => {
});
await initZstd(); await initZstd();
console.log('init done'); console.log('init done');
} }
@@ -362,8 +376,7 @@ export function copyToClipboard(text) {
// Internet Explorer-specific code path to prevent textarea being shown while dialog is visible. // Internet Explorer-specific code path to prevent textarea being shown while dialog is visible.
return window.clipboardData.setData("Text", text); return window.clipboardData.setData("Text", text);
} } else if (document.queryCommandSupported && document.queryCommandSupported("copy")) {
else if (document.queryCommandSupported && document.queryCommandSupported("copy")) {
var textarea = document.createElement("textarea"); var textarea = document.createElement("textarea");
textarea.textContent = text; textarea.textContent = text;
textarea.style.position = "fixed"; // Prevent scrolling to bottom of page in Microsoft Edge. textarea.style.position = "fixed"; // Prevent scrolling to bottom of page in Microsoft Edge.
@@ -371,12 +384,10 @@ export function copyToClipboard(text) {
textarea.select(); textarea.select();
try { try {
return document.execCommand("copy"); // Security exception may be thrown by some browsers. return document.execCommand("copy"); // Security exception may be thrown by some browsers.
} } catch (ex) {
catch (ex) {
console.warn("Copy to clipboard failed.", ex); console.warn("Copy to clipboard failed.", ex);
// return prompt("Copy to clipboard: Ctrl+C, Enter", text); // return prompt("Copy to clipboard: Ctrl+C, Enter", text);
} } finally {
finally {
document.body.removeChild(textarea); document.body.removeChild(textarea);
} }
} }

View File

@@ -49,11 +49,13 @@ if (share_token) {
}) })
} }
let fetching = false
const autoWriteServer = () => { export function getServerConf(token){
return setTimeout(() => { console.log('getServerConf', token)
const token = localStorage.getItem('access_token') if(fetching){
if (token && apiserver) { return
}
fetching = true
fetch(apiserver + "/api/server-config", { fetch(apiserver + "/api/server-config", {
method: 'POST', method: 'POST',
headers: { headers: {
@@ -62,12 +64,12 @@ const autoWriteServer = () => {
} }
} }
).then(res => res.json()).then(res => { ).then(res => res.json()).then(res => {
fetching = false
if (res.code === 0) { if (res.code === 0) {
if (!localStorage.getItem('custom-rendezvous-server') || !localStorage.getItem('key')) { if (!localStorage.getItem('custom-rendezvous-server') || !localStorage.getItem('key')) {
localStorage.setItem('custom-rendezvous-server', res.data.id_server) localStorage.setItem('custom-rendezvous-server', res.data.id_server)
localStorage.setItem('key', res.data.key) localStorage.setItem('key', res.data.key)
} }
if (res.data.peers) { if (res.data.peers) {
const oldPeers = JSON.parse(localStorage.getItem('peers')) || {} const oldPeers = JSON.parse(localStorage.getItem('peers')) || {}
let needUpdate = false let needUpdate = false
@@ -91,10 +93,7 @@ const autoWriteServer = () => {
} }
} }
} }
}).catch(_ => {
fetching = false
}) })
} else {
autoWriteServer()
} }
}, 1000)
}
autoWriteServer()

View File

@@ -262,3 +262,10 @@ func (us *UserService) UserThirdInfo(userId uint, op string) *model.UserThird {
global.DB.Where("user_id = ? and third_type = ?", userId, op).First(ut) global.DB.Where("user_id = ? and third_type = ?", userId, op).First(ut)
return ut return ut
} }
// FindLatestUserIdFromLoginLogByUuid 根据uuid查找最后登录的用户id
func (us *UserService) FindLatestUserIdFromLoginLogByUuid(uuid string) uint {
llog := &model.LoginLog{}
global.DB.Where("uuid = ?", uuid).Order("id desc").First(llog)
return llog.UserId
}