Feature(Go): Web auth, WebSocket signaling and live device list (Phase 3)
This commit is contained in:
@@ -8,13 +8,16 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/yuanyuanxiang/SimpleRemoter/server/go/auth"
|
||||
"github.com/yuanyuanxiang/SimpleRemoter/server/go/connection"
|
||||
"github.com/yuanyuanxiang/SimpleRemoter/server/go/hub"
|
||||
"github.com/yuanyuanxiang/SimpleRemoter/server/go/logger"
|
||||
"github.com/yuanyuanxiang/SimpleRemoter/server/go/protocol"
|
||||
"github.com/yuanyuanxiang/SimpleRemoter/server/go/server"
|
||||
"github.com/yuanyuanxiang/SimpleRemoter/server/go/web"
|
||||
"github.com/yuanyuanxiang/SimpleRemoter/server/go/wsauth"
|
||||
)
|
||||
|
||||
// MyHandler implements the server.Handler interface
|
||||
@@ -22,6 +25,7 @@ type MyHandler struct {
|
||||
log *logger.Logger
|
||||
auth *auth.Authenticator
|
||||
srv *server.Server
|
||||
hub *hub.Hub
|
||||
}
|
||||
|
||||
// OnConnect is called when a client connects
|
||||
@@ -37,6 +41,7 @@ func (h *MyHandler) OnDisconnect(ctx *connection.Context) {
|
||||
"clientID", info.ClientID,
|
||||
"computer", info.ComputerName,
|
||||
)
|
||||
h.hub.Unregister(info.ClientID)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,8 +115,40 @@ func (h *MyHandler) handleLogin(ctx *connection.Context, data []byte) {
|
||||
"version", info.ModuleVersion,
|
||||
"path", clientInfo.FilePath,
|
||||
)
|
||||
|
||||
// PCName carries "ComputerName/Group"; ModuleVersion carries "Version-Capability".
|
||||
// strings.Cut returns the full string as the head when the separator is
|
||||
// absent, which gives us the natural "no group / no capability" fallback.
|
||||
name, group, _ := strings.Cut(info.PCName, "/")
|
||||
version, capability, _ := strings.Cut(info.ModuleVersion, "-")
|
||||
|
||||
// Reserved field 10 (ClientLoc) is the client-reported geo string.
|
||||
location := ""
|
||||
if len(reserved) > 10 {
|
||||
location = info.GetReservedField(10)
|
||||
}
|
||||
|
||||
// Register with hub so the web side can list this device. Sub-connections
|
||||
// (screen / terminal etc.) reuse the MasterID and will overwrite this entry
|
||||
// harmlessly, but only the main login carries enough info to be useful here.
|
||||
h.hub.Register(&hub.Device{
|
||||
ID: clientID,
|
||||
Name: name,
|
||||
Group: group,
|
||||
Version: version,
|
||||
Capability: capability,
|
||||
OS: info.OsVerInfo,
|
||||
CPU: clientInfo.CPU,
|
||||
FilePath: clientInfo.FilePath,
|
||||
InstallTime: info.StartTime,
|
||||
Location: location,
|
||||
PeerIP: ctx.GetPeerIP(),
|
||||
PublicIP: clientInfo.IP,
|
||||
ConnectedAt: time.Now(),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// handleAuth handles authorization request (TOKEN_AUTH = 100)
|
||||
func (h *MyHandler) handleAuth(ctx *connection.Context, data []byte) {
|
||||
result := h.auth.Authenticate(data)
|
||||
@@ -160,6 +197,25 @@ func (h *MyHandler) handleHeartbeat(ctx *connection.Context, data []byte) {
|
||||
uint64(data[5])<<32 | uint64(data[6])<<40 | uint64(data[7])<<48 | uint64(data[8])<<56
|
||||
}
|
||||
|
||||
// Forward live fields (ActiveWnd + Ping) to the hub so the web UI can
|
||||
// display current latency and foreground window per device. Skip until
|
||||
// login has happened — the hub is keyed by MasterID, which only exists
|
||||
// post-login.
|
||||
if info := ctx.GetInfo(); info.ClientID != "" {
|
||||
var rtt int32
|
||||
var activeWindow string
|
||||
// ActiveWnd at data[9..521] is a 512-byte GBK-encoded string.
|
||||
if len(data) >= 9+512 {
|
||||
activeWindow = protocol.GbkToUTF8(data[9 : 9+512])
|
||||
}
|
||||
// Ping at data[521..525] is a little-endian int32.
|
||||
if len(data) >= 525 {
|
||||
rtt = int32(uint32(data[521]) | uint32(data[522])<<8 |
|
||||
uint32(data[523])<<16 | uint32(data[524])<<24)
|
||||
}
|
||||
h.hub.UpdateLive(info.ClientID, int(rtt), activeWindow)
|
||||
}
|
||||
|
||||
// Authenticate heartbeat if it contains authorization info
|
||||
// data[1:] skips the command byte to get the raw Heartbeat structure
|
||||
var authorized byte = 0
|
||||
@@ -269,6 +325,27 @@ func main() {
|
||||
// Create authenticator (shared by all servers)
|
||||
authenticator := auth.New(authCfg)
|
||||
|
||||
// Shared device registry — every TCP handler reports devices into it,
|
||||
// the HTTP server reads from it.
|
||||
deviceHub := hub.New()
|
||||
|
||||
// Web user authenticator. Bootstrap admin from env var YAMA_WEB_ADMIN_PASS;
|
||||
// if unset, fall back to YAMA_PWD (same secret the TCP authorization uses)
|
||||
// so a single password env var is enough to bring up the whole stack.
|
||||
// If neither is set, no admin is registered and login will always fail —
|
||||
// the user must define a password before browsers can log in.
|
||||
webAuth := wsauth.New()
|
||||
adminPass := os.Getenv("YAMA_WEB_ADMIN_PASS")
|
||||
if adminPass == "" {
|
||||
adminPass = os.Getenv("YAMA_PWD")
|
||||
}
|
||||
if adminPass != "" {
|
||||
webAuth.AddAdminFromPlainPassword("admin", adminPass)
|
||||
log.Info("Web admin user configured")
|
||||
} else {
|
||||
log.Warn("Neither YAMA_WEB_ADMIN_PASS nor YAMA_PWD is set; web login will be unavailable")
|
||||
}
|
||||
|
||||
// Create servers for each port
|
||||
var servers []*server.Server
|
||||
for _, port := range ports {
|
||||
@@ -284,6 +361,7 @@ func main() {
|
||||
log: log.WithPrefix(fmt.Sprintf("Handler:%d", port)),
|
||||
auth: authenticator,
|
||||
srv: srv,
|
||||
hub: deviceHub,
|
||||
}
|
||||
srv.SetHandler(handler)
|
||||
|
||||
@@ -297,8 +375,9 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
// Start HTTP server for web UI (Phase 1: serves index.html only)
|
||||
httpSrv := web.New(*httpPort, log.WithPrefix("Web"))
|
||||
// Start HTTP server for web UI. Hub gives it read-only access to the
|
||||
// device registry; the authenticator owns user accounts and session tokens.
|
||||
httpSrv := web.New(*httpPort, log.WithPrefix("Web"), deviceHub, webAuth)
|
||||
if err := httpSrv.Start(); err != nil {
|
||||
log.Fatal("Failed to start HTTP server: %v", err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user