Compare commits

..

1 Commits

Author SHA1 Message Date
Josh Arrington 50ce877a2f
Merge 87f36167f3 into 69461140e3 2025-02-18 08:39:26 +01:00
5 changed files with 29 additions and 64 deletions

View File

@ -24,18 +24,6 @@ type CloudRegisterRequest struct {
ClientId string `json:"clientId"`
}
const (
// CloudWebSocketConnectTimeout is the timeout for the websocket connection to the cloud
CloudWebSocketConnectTimeout = 1 * time.Minute
// CloudAPIRequestTimeout is the timeout for cloud API requests
CloudAPIRequestTimeout = 10 * time.Second
// CloudOidcRequestTimeout is the timeout for OIDC token verification requests
// should be lower than the websocket response timeout set in cloud-api
CloudOidcRequestTimeout = 10 * time.Second
// CloudWebSocketPingInterval is the interval at which the websocket client sends ping messages to the cloud
CloudWebSocketPingInterval = 15 * time.Second
)
func handleCloudRegister(c *gin.Context) {
var req CloudRegisterRequest
@ -56,31 +44,22 @@ func handleCloudRegister(c *gin.Context) {
return
}
client := &http.Client{Timeout: CloudAPIRequestTimeout}
apiReq, err := http.NewRequest(http.MethodPost, config.CloudURL+"/devices/token", bytes.NewBuffer(jsonPayload))
if err != nil {
c.JSON(500, gin.H{"error": "Failed to create register request: " + err.Error()})
return
}
apiReq.Header.Set("Content-Type", "application/json")
apiResp, err := client.Do(apiReq)
resp, err := http.Post(req.CloudAPI+"/devices/token", "application/json", bytes.NewBuffer(jsonPayload))
if err != nil {
c.JSON(500, gin.H{"error": "Failed to exchange token: " + err.Error()})
return
}
defer apiResp.Body.Close()
defer resp.Body.Close()
if apiResp.StatusCode != http.StatusOK {
c.JSON(apiResp.StatusCode, gin.H{"error": "Failed to exchange token: " + apiResp.Status})
if resp.StatusCode != http.StatusOK {
c.JSON(resp.StatusCode, gin.H{"error": "Failed to exchange token: " + resp.Status})
return
}
var tokenResp struct {
SecretToken string `json:"secretToken"`
}
if err := json.NewDecoder(apiResp.Body).Decode(&tokenResp); err != nil {
if err := json.NewDecoder(resp.Body).Decode(&tokenResp); err != nil {
c.JSON(500, gin.H{"error": "Failed to parse token response: " + err.Error()})
return
}
@ -91,7 +70,7 @@ func handleCloudRegister(c *gin.Context) {
}
if config.CloudToken == "" {
cloudLogger.Info("Starting websocket client due to adoption")
logger.Info("Starting websocket client due to adoption")
go RunWebsocketClient()
}
@ -143,7 +122,7 @@ func runWebsocketClient() error {
header := http.Header{}
header.Set("X-Device-ID", GetDeviceID())
header.Set("Authorization", "Bearer "+config.CloudToken)
dialCtx, cancelDial := context.WithTimeout(context.Background(), CloudWebSocketConnectTimeout)
dialCtx, cancelDial := context.WithTimeout(context.Background(), time.Minute)
defer cancelDial()
c, _, err := websocket.Dial(dialCtx, wsURL.String(), &websocket.DialOptions{
HTTPHeader: header,
@ -152,15 +131,15 @@ func runWebsocketClient() error {
return err
}
defer c.CloseNow()
cloudLogger.Infof("websocket connected to %s", wsURL.String())
logger.Infof("WS connected to %v", wsURL.String())
runCtx, cancelRun := context.WithCancel(context.Background())
defer cancelRun()
go func() {
for {
time.Sleep(CloudWebSocketPingInterval)
time.Sleep(15 * time.Second)
err := c.Ping(runCtx)
if err != nil {
cloudLogger.Warnf("websocket ping error: %v", err)
logger.Warnf("websocket ping error: %v", err)
cancelRun()
return
}
@ -178,30 +157,24 @@ func runWebsocketClient() error {
var req WebRTCSessionRequest
err = json.Unmarshal(msg, &req)
if err != nil {
cloudLogger.Warnf("unable to parse ws message: %v", string(msg))
logger.Warnf("unable to parse ws message: %v", string(msg))
continue
}
cloudLogger.Infof("new session request: %v", req.OidcGoogle)
cloudLogger.Tracef("session request info: %v", req)
err = handleSessionRequest(runCtx, c, req)
if err != nil {
cloudLogger.Infof("error starting new session: %v", err)
logger.Infof("error starting new session: %v", err)
continue
}
}
}
func handleSessionRequest(ctx context.Context, c *websocket.Conn, req WebRTCSessionRequest) error {
oidcCtx, cancelOIDC := context.WithTimeout(ctx, CloudOidcRequestTimeout)
oidcCtx, cancelOIDC := context.WithTimeout(ctx, time.Minute)
defer cancelOIDC()
provider, err := oidc.NewProvider(oidcCtx, "https://accounts.google.com")
if err != nil {
_ = wsjson.Write(context.Background(), c, gin.H{
"error": fmt.Sprintf("failed to initialize OIDC provider: %v", err),
})
cloudLogger.Errorf("failed to initialize OIDC provider: %v", err)
fmt.Println("Failed to initialize OIDC provider:", err)
return err
}
@ -217,7 +190,6 @@ func handleSessionRequest(ctx context.Context, c *websocket.Conn, req WebRTCSess
googleIdentity := idToken.Audience[0] + ":" + idToken.Subject
if config.GoogleIdentity != googleIdentity {
_ = wsjson.Write(context.Background(), c, gin.H{"error": "google identity mismatch"})
return fmt.Errorf("google identity mismatch")
}
@ -244,9 +216,6 @@ func handleSessionRequest(ctx context.Context, c *websocket.Conn, req WebRTCSess
_ = peerConn.Close()
}()
}
cloudLogger.Info("new session accepted")
cloudLogger.Tracef("new session accepted: %v", session)
currentSession = session
_ = wsjson.Write(context.Background(), c, gin.H{"sd": sd})
return nil
@ -256,7 +225,7 @@ func RunWebsocketClient() {
for {
err := runWebsocketClient()
if err != nil {
cloudLogger.Errorf("websocket client error: %v", err)
fmt.Println("Websocket client error:", err)
time.Sleep(5 * time.Second)
}
}
@ -285,7 +254,7 @@ func rpcDeregisterDevice() error {
}
req.Header.Set("Authorization", "Bearer "+config.CloudToken)
client := &http.Client{Timeout: CloudAPIRequestTimeout}
client := &http.Client{Timeout: 10 * time.Second}
resp, err := client.Do(req)
if err != nil {
return fmt.Errorf("failed to send deregister request: %w", err)

View File

@ -47,9 +47,6 @@ var (
)
func LoadConfig() {
configLock.Lock()
defer configLock.Unlock()
if config != nil {
logger.Info("config already loaded, skipping")
return

View File

@ -60,9 +60,6 @@ make build_dev
# Change directory to the binary output directory
cd bin
# Kill any existing instances of the application
ssh "${REMOTE_USER}@${REMOTE_HOST}" "killall jetkvm_app_debug || true"
# Copy the binary to the remote host
cat jetkvm_app | ssh "${REMOTE_USER}@${REMOTE_HOST}" "cat > $REMOTE_PATH/jetkvm_app_debug"
@ -84,7 +81,8 @@ cd "$REMOTE_PATH"
chmod +x jetkvm_app_debug
# Run the application in the background
PION_LOG_TRACE=jetkvm,cloud ./jetkvm_app_debug
./jetkvm_app_debug
EOF
echo "Deployment complete."

1
log.go
View File

@ -6,4 +6,3 @@ import "github.com/pion/logging"
// ref: https://github.com/pion/webrtc/wiki/Debugging-WebRTC
var logger = logging.NewDefaultLoggerFactory().NewLogger("jetkvm")
var usbLogger = logging.NewDefaultLoggerFactory().NewLogger("usb")
var cloudLogger = logging.NewDefaultLoggerFactory().NewLogger("cloud")

View File

@ -1,19 +1,21 @@
#!/bin/bash
# Check if an IP address was provided as an argument
if [ -z "$1" ]; then
echo "Usage: $0 <JetKVM IP Address>"
exit 1
fi
ip_address="$1"
# Print header
echo "┌──────────────────────────────────────┐"
echo "│ JetKVM Development Setup │"
echo "└──────────────────────────────────────┘"
# Prompt for IP address
printf "Please enter the IP address of your JetKVM device: "
read ip_address
# Validate input is not empty
if [ -z "$ip_address" ]; then
echo "Error: IP address cannot be empty"
exit 1
fi
# Set the environment variable and run Vite
echo "Starting development server with JetKVM device at: $ip_address"
sleep 1
JETKVM_PROXY_URL="http://$ip_address" npx vite dev --mode=device
JETKVM_PROXY_URL="http://$ip_address" vite dev --mode=device