Compare commits

..

1 Commits

Author SHA1 Message Date
Marc Brooks 9df1bdf265
refactor(ui): Refactor the keyboardLayouts
Add missing keyboard mappings for most layouts
Change  pasteModel.tsx to use the new structure and vastly clarified the way that keys are emitted.
Make each layout export just the KeyboardLayout object (which is a package of isoCode, name, and chars)
Made keyboardLayouts.ts export a function to select keyboard by `isoCode`, export the keyboards as label . value pairs (for a select list) and the list of keyboards.
Changed devices.$id.settings.keyboard.tsx use the exported keyboard option list.
2025-05-30 11:16:20 -05:00
19 changed files with 299 additions and 441 deletions

View File

@ -1,17 +0,0 @@
version: 2
updates:
- package-ecosystem: gomod
directory: /
schedule:
interval: monthly
open-pull-requests-limit: 10
- package-ecosystem: github-actions
directory: /
schedule:
interval: monthly
open-pull-requests-limit: 10
- package-ecosystem: npm
directory: /ui
open-pull-requests-limit: 10
schedule:
interval: monthly

View File

@ -23,9 +23,9 @@ jobs:
cache: "npm" cache: "npm"
cache-dependency-path: "**/package-lock.json" cache-dependency-path: "**/package-lock.json"
- name: Set up Golang - name: Set up Golang
uses: actions/setup-go@v5 uses: actions/setup-go@v4
with: with:
go-version: "1.24.4" go-version: "1.24.3"
- name: Build frontend - name: Build frontend
run: | run: |
make frontend make frontend

View File

@ -24,9 +24,9 @@ jobs:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Install Go - name: Install Go
uses: actions/setup-go@19bb51245e9c80abacb2e91cc42b33fa478b8639 # v4.2.1 uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5.1.0
with: with:
go-version: 1.24.4 go-version: 1.24.3
- name: Create empty resource directory - name: Create empty resource directory
run: | run: |
mkdir -p static && touch static/.gitkeep mkdir -p static && touch static/.gitkeep

View File

@ -104,9 +104,9 @@ jobs:
EOF EOF
ssh jkci "cat /tmp/device-tests.json" > device-tests.json ssh jkci "cat /tmp/device-tests.json" > device-tests.json
- name: Set up Golang - name: Set up Golang
uses: actions/setup-go@v5 uses: actions/setup-go@v4
with: with:
go-version: "1.24.4" go-version: "1.24.3"
- name: Golang Test Report - name: Golang Test Report
uses: becheran/go-testreport@v0.3.2 uses: becheran/go-testreport@v0.3.2
with: with:

View File

@ -2,14 +2,12 @@ BRANCH ?= $(shell git rev-parse --abbrev-ref HEAD)
BUILDDATE ?= $(shell date -u +%FT%T%z) BUILDDATE ?= $(shell date -u +%FT%T%z)
BUILDTS ?= $(shell date -u +%s) BUILDTS ?= $(shell date -u +%s)
REVISION ?= $(shell git rev-parse HEAD) REVISION ?= $(shell git rev-parse HEAD)
VERSION_DEV := 0.4.4-dev$(shell date +%Y%m%d%H%M) VERSION_DEV := 0.4.2-dev$(shell date +%Y%m%d%H%M)
VERSION := 0.4.3 VERSION := 0.4.1
PROMETHEUS_TAG := github.com/prometheus/common/version PROMETHEUS_TAG := github.com/prometheus/common/version
KVM_PKG_NAME := github.com/jetkvm/kvm KVM_PKG_NAME := github.com/jetkvm/kvm
GO_BUILD_ARGS := -tags netgo
GO_RELEASE_BUILD_ARGS := -trimpath $(GO_BUILD_ARGS)
GO_LDFLAGS := \ GO_LDFLAGS := \
-s -w \ -s -w \
-X $(PROMETHEUS_TAG).Branch=$(BRANCH) \ -X $(PROMETHEUS_TAG).Branch=$(BRANCH) \
@ -29,7 +27,7 @@ build_dev: hash_resource
@echo "Building..." @echo "Building..."
$(GO_CMD) build \ $(GO_CMD) build \
-ldflags="$(GO_LDFLAGS) -X $(KVM_PKG_NAME).builtAppVersion=$(VERSION_DEV)" \ -ldflags="$(GO_LDFLAGS) -X $(KVM_PKG_NAME).builtAppVersion=$(VERSION_DEV)" \
$(GO_RELEASE_BUILD_ARGS) \ -trimpath \
-o $(BIN_DIR)/jetkvm_app cmd/main.go -o $(BIN_DIR)/jetkvm_app cmd/main.go
build_test2json: build_test2json:
@ -52,7 +50,6 @@ build_dev_test: build_test2json build_gotestsum
test_filename=$$(echo $$test_pkg_name | sed 's/\//__/g')_test; \ test_filename=$$(echo $$test_pkg_name | sed 's/\//__/g')_test; \
$(GO_CMD) test -v \ $(GO_CMD) test -v \
-ldflags="$(GO_LDFLAGS) -X $(KVM_PKG_NAME).builtAppVersion=$(VERSION_DEV)" \ -ldflags="$(GO_LDFLAGS) -X $(KVM_PKG_NAME).builtAppVersion=$(VERSION_DEV)" \
$(GO_BUILD_ARGS) \
-c -o $(BIN_DIR)/tests/$$test_filename $$test; \ -c -o $(BIN_DIR)/tests/$$test_filename $$test; \
echo "runTest ./$$test_filename $$test_pkg_full_name" >> $(BIN_DIR)/tests/run_all_tests; \ echo "runTest ./$$test_filename $$test_pkg_full_name" >> $(BIN_DIR)/tests/run_all_tests; \
done; \ done; \
@ -74,7 +71,7 @@ build_release: frontend hash_resource
@echo "Building release..." @echo "Building release..."
$(GO_CMD) build \ $(GO_CMD) build \
-ldflags="$(GO_LDFLAGS) -X $(KVM_PKG_NAME).builtAppVersion=$(VERSION)" \ -ldflags="$(GO_LDFLAGS) -X $(KVM_PKG_NAME).builtAppVersion=$(VERSION)" \
$(GO_RELEASE_BUILD_ARGS) \ -trimpath \
-o bin/jetkvm_app cmd/main.go -o bin/jetkvm_app cmd/main.go
release: release:

View File

@ -51,34 +51,34 @@ var (
) )
metricCloudConnectionEstablishedTimestamp = promauto.NewGauge( metricCloudConnectionEstablishedTimestamp = promauto.NewGauge(
prometheus.GaugeOpts{ prometheus.GaugeOpts{
Name: "jetkvm_cloud_connection_established_timestamp_seconds", Name: "jetkvm_cloud_connection_established_timestamp",
Help: "The timestamp when the cloud connection was established", Help: "The timestamp when the cloud connection was established",
}, },
) )
metricConnectionLastPingTimestamp = promauto.NewGaugeVec( metricConnectionLastPingTimestamp = promauto.NewGaugeVec(
prometheus.GaugeOpts{ prometheus.GaugeOpts{
Name: "jetkvm_connection_last_ping_timestamp_seconds", Name: "jetkvm_connection_last_ping_timestamp",
Help: "The timestamp when the last ping response was received", Help: "The timestamp when the last ping response was received",
}, },
[]string{"type", "source"}, []string{"type", "source"},
) )
metricConnectionLastPingReceivedTimestamp = promauto.NewGaugeVec( metricConnectionLastPingReceivedTimestamp = promauto.NewGaugeVec(
prometheus.GaugeOpts{ prometheus.GaugeOpts{
Name: "jetkvm_connection_last_ping_received_timestamp_seconds", Name: "jetkvm_connection_last_ping_received_timestamp",
Help: "The timestamp when the last ping request was received", Help: "The timestamp when the last ping request was received",
}, },
[]string{"type", "source"}, []string{"type", "source"},
) )
metricConnectionLastPingDuration = promauto.NewGaugeVec( metricConnectionLastPingDuration = promauto.NewGaugeVec(
prometheus.GaugeOpts{ prometheus.GaugeOpts{
Name: "jetkvm_connection_last_ping_duration_seconds", Name: "jetkvm_connection_last_ping_duration",
Help: "The duration of the last ping response", Help: "The duration of the last ping response",
}, },
[]string{"type", "source"}, []string{"type", "source"},
) )
metricConnectionPingDuration = promauto.NewHistogramVec( metricConnectionPingDuration = promauto.NewHistogramVec(
prometheus.HistogramOpts{ prometheus.HistogramOpts{
Name: "jetkvm_connection_ping_duration_seconds", Name: "jetkvm_connection_ping_duration",
Help: "The duration of the ping response", Help: "The duration of the ping response",
Buckets: []float64{ Buckets: []float64{
0.1, 0.5, 1, 10, 0.1, 0.5, 1, 10,
@ -88,28 +88,28 @@ var (
) )
metricConnectionTotalPingSentCount = promauto.NewCounterVec( metricConnectionTotalPingSentCount = promauto.NewCounterVec(
prometheus.CounterOpts{ prometheus.CounterOpts{
Name: "jetkvm_connection_ping_sent_total", Name: "jetkvm_connection_total_ping_sent",
Help: "The total number of pings sent to the connection", Help: "The total number of pings sent to the connection",
}, },
[]string{"type", "source"}, []string{"type", "source"},
) )
metricConnectionTotalPingReceivedCount = promauto.NewCounterVec( metricConnectionTotalPingReceivedCount = promauto.NewCounterVec(
prometheus.CounterOpts{ prometheus.CounterOpts{
Name: "jetkvm_connection_ping_received_total", Name: "jetkvm_connection_total_ping_received",
Help: "The total number of pings received from the connection", Help: "The total number of pings received from the connection",
}, },
[]string{"type", "source"}, []string{"type", "source"},
) )
metricConnectionSessionRequestCount = promauto.NewCounterVec( metricConnectionSessionRequestCount = promauto.NewCounterVec(
prometheus.CounterOpts{ prometheus.CounterOpts{
Name: "jetkvm_connection_session_requests_total", Name: "jetkvm_connection_session_total_requests",
Help: "The total number of session requests received", Help: "The total number of session requests received",
}, },
[]string{"type", "source"}, []string{"type", "source"},
) )
metricConnectionSessionRequestDuration = promauto.NewHistogramVec( metricConnectionSessionRequestDuration = promauto.NewHistogramVec(
prometheus.HistogramOpts{ prometheus.HistogramOpts{
Name: "jetkvm_connection_session_request_duration_seconds", Name: "jetkvm_connection_session_request_duration",
Help: "The duration of session requests", Help: "The duration of session requests",
Buckets: []float64{ Buckets: []float64{
0.1, 0.5, 1, 10, 0.1, 0.5, 1, 10,
@ -119,7 +119,7 @@ var (
) )
metricConnectionLastSessionRequestTimestamp = promauto.NewGaugeVec( metricConnectionLastSessionRequestTimestamp = promauto.NewGaugeVec(
prometheus.GaugeOpts{ prometheus.GaugeOpts{
Name: "jetkvm_connection_last_session_request_timestamp_seconds", Name: "jetkvm_connection_last_session_request_timestamp",
Help: "The timestamp of the last session request", Help: "The timestamp of the last session request",
}, },
[]string{"type", "source"}, []string{"type", "source"},
@ -133,7 +133,7 @@ var (
) )
metricCloudConnectionFailureCount = promauto.NewCounter( metricCloudConnectionFailureCount = promauto.NewCounter(
prometheus.CounterOpts{ prometheus.CounterOpts{
Name: "jetkvm_cloud_connection_failure_total", Name: "jetkvm_cloud_connection_failure_count",
Help: "The number of times the cloud connection has failed", Help: "The number of times the cloud connection has failed",
}, },
) )

View File

@ -174,7 +174,7 @@ cd "${REMOTE_PATH}"
chmod +x jetkvm_app_debug chmod +x jetkvm_app_debug
# Run the application in the background # Run the application in the background
PION_LOG_TRACE=${LOG_TRACE_SCOPES} GODEBUG=netdns=1 ./jetkvm_app_debug PION_LOG_TRACE=${LOG_TRACE_SCOPES} ./jetkvm_app_debug
EOF EOF
echo "Deployment complete." echo "Deployment complete."

View File

@ -339,18 +339,10 @@ func startBacklightTickers() {
return return
} }
// Stop existing tickers to prevent multiple active instances on repeated calls if dimTicker == nil && config.DisplayDimAfterSec != 0 {
if dimTicker != nil {
dimTicker.Stop()
}
if offTicker != nil {
offTicker.Stop()
}
if config.DisplayDimAfterSec != 0 {
displayLogger.Info().Msg("dim_ticker has started") displayLogger.Info().Msg("dim_ticker has started")
dimTicker = time.NewTicker(time.Duration(config.DisplayDimAfterSec) * time.Second) dimTicker = time.NewTicker(time.Duration(config.DisplayDimAfterSec) * time.Second)
defer dimTicker.Stop()
go func() { go func() {
for { //nolint:staticcheck for { //nolint:staticcheck
@ -362,9 +354,10 @@ func startBacklightTickers() {
}() }()
} }
if config.DisplayOffAfterSec != 0 { if offTicker == nil && config.DisplayOffAfterSec != 0 {
displayLogger.Info().Msg("off_ticker has started") displayLogger.Info().Msg("off_ticker has started")
offTicker = time.NewTicker(time.Duration(config.DisplayOffAfterSec) * time.Second) offTicker = time.NewTicker(time.Duration(config.DisplayOffAfterSec) * time.Second)
defer offTicker.Stop()
go func() { go func() {
for { //nolint:staticcheck for { //nolint:staticcheck

56
go.mod
View File

@ -5,33 +5,33 @@ go 1.23.4
toolchain go1.24.3 toolchain go1.24.3
require ( require (
github.com/Masterminds/semver/v3 v3.3.1 github.com/Masterminds/semver/v3 v3.3.0
github.com/beevik/ntp v1.4.3 github.com/beevik/ntp v1.3.1
github.com/coder/websocket v1.8.13 github.com/coder/websocket v1.8.13
github.com/coreos/go-oidc/v3 v3.11.0 github.com/coreos/go-oidc/v3 v3.11.0
github.com/creack/pty v1.1.23 github.com/creack/pty v1.1.23
github.com/fsnotify/fsnotify v1.9.0 github.com/fsnotify/fsnotify v1.9.0
github.com/gin-contrib/logger v1.2.6 github.com/gin-contrib/logger v1.2.5
github.com/gin-gonic/gin v1.10.1 github.com/gin-gonic/gin v1.10.0
github.com/google/uuid v1.6.0 github.com/google/uuid v1.6.0
github.com/guregu/null/v6 v6.0.0 github.com/guregu/null/v6 v6.0.0
github.com/gwatts/rootcerts v0.0.0-20240401182218-3ab9db955caf github.com/gwatts/rootcerts v0.0.0-20240401182218-3ab9db955caf
github.com/hanwen/go-fuse/v2 v2.8.0 github.com/hanwen/go-fuse/v2 v2.5.1
github.com/pion/logging v0.2.3 github.com/pion/logging v0.2.2
github.com/pion/mdns/v2 v2.0.7 github.com/pion/mdns/v2 v2.0.7
github.com/pion/webrtc/v4 v4.0.16 github.com/pion/webrtc/v4 v4.0.0
github.com/pojntfx/go-nbd v0.3.2 github.com/pojntfx/go-nbd v0.3.2
github.com/prometheus/client_golang v1.22.0 github.com/prometheus/client_golang v1.21.0
github.com/prometheus/common v0.62.0 github.com/prometheus/common v0.62.0
github.com/prometheus/procfs v0.16.1 github.com/prometheus/procfs v0.15.1
github.com/psanford/httpreadat v0.1.0 github.com/psanford/httpreadat v0.1.0
github.com/rs/zerolog v1.34.0 github.com/rs/zerolog v1.34.0
github.com/sourcegraph/tf-dag v0.2.2-0.20250131204052-3e8ff1477b4f github.com/sourcegraph/tf-dag v0.2.2-0.20250131204052-3e8ff1477b4f
github.com/stretchr/testify v1.10.0 github.com/stretchr/testify v1.10.0
github.com/vishvananda/netlink v1.3.0 github.com/vishvananda/netlink v1.3.0
go.bug.st/serial v1.6.2 go.bug.st/serial v1.6.2
golang.org/x/crypto v0.39.0 golang.org/x/crypto v0.38.0
golang.org/x/net v0.41.0 golang.org/x/net v0.40.0
golang.org/x/sys v0.33.0 golang.org/x/sys v0.33.0
) )
@ -45,14 +45,16 @@ require (
github.com/cloudwego/base64x v0.1.5 // indirect github.com/cloudwego/base64x v0.1.5 // indirect
github.com/creack/goselect v0.1.2 // indirect github.com/creack/goselect v0.1.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/gabriel-vasile/mimetype v1.4.9 // indirect github.com/gabriel-vasile/mimetype v1.4.8 // indirect
github.com/gin-contrib/sse v1.1.0 // indirect github.com/gin-contrib/sse v1.0.0 // indirect
github.com/go-jose/go-jose/v4 v4.0.5 // indirect github.com/go-jose/go-jose/v4 v4.0.2 // indirect
github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.26.0 // indirect github.com/go-playground/validator/v10 v10.26.0 // indirect
github.com/goccy/go-json v0.10.5 // indirect github.com/goccy/go-json v0.10.5 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.17.11 // indirect
github.com/klauspost/cpuid/v2 v2.2.10 // indirect github.com/klauspost/cpuid/v2 v2.2.10 // indirect
github.com/leodido/go-urn v1.4.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-colorable v0.1.14 // indirect
@ -60,21 +62,21 @@ require (
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/pelletier/go-toml/v2 v2.2.3 // indirect
github.com/pilebones/go-udev v0.9.0 // indirect github.com/pilebones/go-udev v0.9.0 // indirect
github.com/pion/datachannel v1.5.10 // indirect github.com/pion/datachannel v1.5.9 // indirect
github.com/pion/dtls/v3 v3.0.6 // indirect github.com/pion/dtls/v3 v3.0.3 // indirect
github.com/pion/ice/v4 v4.0.10 // indirect github.com/pion/ice/v4 v4.0.2 // indirect
github.com/pion/interceptor v0.1.40 // indirect github.com/pion/interceptor v0.1.37 // indirect
github.com/pion/randutil v0.1.0 // indirect github.com/pion/randutil v0.1.0 // indirect
github.com/pion/rtcp v1.2.15 // indirect github.com/pion/rtcp v1.2.14 // indirect
github.com/pion/rtp v1.8.18 // indirect github.com/pion/rtp v1.8.9 // indirect
github.com/pion/sctp v1.8.39 // indirect github.com/pion/sctp v1.8.33 // indirect
github.com/pion/sdp/v3 v3.0.13 // indirect github.com/pion/sdp/v3 v3.0.9 // indirect
github.com/pion/srtp/v3 v3.0.5 // indirect github.com/pion/srtp/v3 v3.0.4 // indirect
github.com/pion/stun/v3 v3.0.0 // indirect github.com/pion/stun/v3 v3.0.0 // indirect
github.com/pion/transport/v3 v3.0.7 // indirect github.com/pion/transport/v3 v3.0.7 // indirect
github.com/pion/turn/v4 v4.0.2 // indirect github.com/pion/turn/v4 v4.0.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/client_model v0.6.1 // indirect
github.com/rogpeppe/go-internal v1.11.0 // indirect github.com/rogpeppe/go-internal v1.11.0 // indirect
@ -82,9 +84,9 @@ require (
github.com/ugorji/go/codec v1.2.12 // indirect github.com/ugorji/go/codec v1.2.12 // indirect
github.com/vishvananda/netns v0.0.4 // indirect github.com/vishvananda/netns v0.0.4 // indirect
github.com/wlynxg/anet v0.0.5 // indirect github.com/wlynxg/anet v0.0.5 // indirect
golang.org/x/arch v0.17.0 // indirect golang.org/x/arch v0.15.0 // indirect
golang.org/x/oauth2 v0.24.0 // indirect golang.org/x/oauth2 v0.24.0 // indirect
golang.org/x/text v0.26.0 // indirect golang.org/x/text v0.25.0 // indirect
google.golang.org/protobuf v1.36.6 // indirect google.golang.org/protobuf v1.36.6 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
) )

122
go.sum
View File

@ -1,7 +1,7 @@
github.com/Masterminds/semver/v3 v3.3.1 h1:QtNSWtVZ3nBfk8mAOu/B6v7FMJ+NHTIgUPi7rj+4nv4= github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0=
github.com/Masterminds/semver/v3 v3.3.1/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
github.com/beevik/ntp v1.4.3 h1:PlbTvE5NNy4QHmA4Mg57n7mcFTmr1W1j3gcK7L1lqho= github.com/beevik/ntp v1.3.1 h1:Y/srlT8L1yQr58kyPWFPZIxRL8ttx2SRIpVYJqZIlAM=
github.com/beevik/ntp v1.4.3/go.mod h1:Unr8Zg+2dRn7d8bHFuehIMSvvUYssHMxW3Q5Nx4RW5Q= github.com/beevik/ntp v1.3.1/go.mod h1:fT6PylBq86Tsq23ZMEe47b7QQrZfYBFPnpzt0a9kJxw=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bytedance/sonic v1.13.2 h1:8/H1FempDZqC4VqjptGo14QQlJx8VdZJegxs6wwfqpQ= github.com/bytedance/sonic v1.13.2 h1:8/H1FempDZqC4VqjptGo14QQlJx8VdZJegxs6wwfqpQ=
@ -30,16 +30,16 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY= github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok= github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
github.com/gin-contrib/logger v1.2.6 h1:EPolruKUTzNXMVBD9LuAFQmRjTs7AH7yKGuXgYqrKWc= github.com/gin-contrib/logger v1.2.5 h1:qVQI4omayQecuN4zX9ZZnsOq7w9J/ZLds3J/FMn8ypM=
github.com/gin-contrib/logger v1.2.6/go.mod h1:7niPrd7F0Nscw/zvgz8RiGJxSdbKM2yfQNy8xCHcm64= github.com/gin-contrib/logger v1.2.5/go.mod h1:/bj+vNMuA2xOEQ1aRHoJ1m9+uyaaXIAxQTvM2llsc6I=
github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= github.com/gin-contrib/sse v1.0.0 h1:y3bT1mUWUxDpW4JLQg/HnTqV4rozuW4tC9eFKTxYI9E=
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM= github.com/gin-contrib/sse v1.0.0/go.mod h1:zNuFdwarAygJBht0NTKiSi3jRf6RbqeILZ9Sp6Slhe0=
github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ= github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE= github.com/go-jose/go-jose/v4 v4.0.2 h1:R3l3kkBds16bO7ZFAEEcofK0MkrAJt3jlJznWZG0nvk=
github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA= github.com/go-jose/go-jose/v4 v4.0.2/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
@ -60,12 +60,12 @@ github.com/guregu/null/v6 v6.0.0 h1:N14VRS+4di81i1PXRiprbQJ9EM9gqBa0+KVMeS/QSjQ=
github.com/guregu/null/v6 v6.0.0/go.mod h1:hrMIhIfrOZeLPZhROSn149tpw2gHkidAqxoXNyeX3iQ= github.com/guregu/null/v6 v6.0.0/go.mod h1:hrMIhIfrOZeLPZhROSn149tpw2gHkidAqxoXNyeX3iQ=
github.com/gwatts/rootcerts v0.0.0-20240401182218-3ab9db955caf h1:JO6ISZIvEUitto5zjQ3/VEnDM5rPbqIFuOhS0U0ByeA= github.com/gwatts/rootcerts v0.0.0-20240401182218-3ab9db955caf h1:JO6ISZIvEUitto5zjQ3/VEnDM5rPbqIFuOhS0U0ByeA=
github.com/gwatts/rootcerts v0.0.0-20240401182218-3ab9db955caf/go.mod h1:5Kt9XkWvkGi2OHOq0QsGxebHmhCcqJ8KCbNg/a6+n+g= github.com/gwatts/rootcerts v0.0.0-20240401182218-3ab9db955caf/go.mod h1:5Kt9XkWvkGi2OHOq0QsGxebHmhCcqJ8KCbNg/a6+n+g=
github.com/hanwen/go-fuse/v2 v2.8.0 h1:wV8rG7rmCz8XHSOwBZhG5YcVqcYjkzivjmbaMafPlAs= github.com/hanwen/go-fuse/v2 v2.5.1 h1:OQBE8zVemSocRxA4OaFJbjJ5hlpCmIWbGr7r0M4uoQQ=
github.com/hanwen/go-fuse/v2 v2.8.0/go.mod h1:yE6D2PqWwm3CbYRxFXV9xUd8Md5d6NG0WBs5spCswmI= github.com/hanwen/go-fuse/v2 v2.5.1/go.mod h1:xKwi1cF7nXAOBCXujD5ie0ZKsxc8GGSA1rlMJc+8IJs=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE= github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
@ -77,6 +77,7 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
@ -88,8 +89,8 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg= github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vygl78=
github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4= github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@ -97,53 +98,53 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
github.com/pilebones/go-udev v0.9.0 h1:N1uEO/SxUwtIctc0WLU0t69JeBxIYEYnj8lT/Nabl9Q= github.com/pilebones/go-udev v0.9.0 h1:N1uEO/SxUwtIctc0WLU0t69JeBxIYEYnj8lT/Nabl9Q=
github.com/pilebones/go-udev v0.9.0/go.mod h1:T2eI2tUSK0hA2WS5QLjXJUfQkluZQu+18Cqvem3CaXI= github.com/pilebones/go-udev v0.9.0/go.mod h1:T2eI2tUSK0hA2WS5QLjXJUfQkluZQu+18Cqvem3CaXI=
github.com/pion/datachannel v1.5.10 h1:ly0Q26K1i6ZkGf42W7D4hQYR90pZwzFOjTq5AuCKk4o= github.com/pion/datachannel v1.5.9 h1:LpIWAOYPyDrXtU+BW7X0Yt/vGtYxtXQ8ql7dFfYUVZA=
github.com/pion/datachannel v1.5.10/go.mod h1:p/jJfC9arb29W7WrxyKbepTU20CFgyx5oLo8Rs4Py/M= github.com/pion/datachannel v1.5.9/go.mod h1:kDUuk4CU4Uxp82NH4LQZbISULkX/HtzKa4P7ldf9izE=
github.com/pion/dtls/v3 v3.0.6 h1:7Hkd8WhAJNbRgq9RgdNh1aaWlZlGpYTzdqjy9x9sK2E= github.com/pion/dtls/v3 v3.0.3 h1:j5ajZbQwff7Z8k3pE3S+rQ4STvKvXUdKsi/07ka+OWM=
github.com/pion/dtls/v3 v3.0.6/go.mod h1:iJxNQ3Uhn1NZWOMWlLxEEHAN5yX7GyPvvKw04v9bzYU= github.com/pion/dtls/v3 v3.0.3/go.mod h1:weOTUyIV4z0bQaVzKe8kpaP17+us3yAuiQsEAG1STMU=
github.com/pion/ice/v4 v4.0.10 h1:P59w1iauC/wPk9PdY8Vjl4fOFL5B+USq1+xbDcN6gT4= github.com/pion/ice/v4 v4.0.2 h1:1JhBRX8iQLi0+TfcavTjPjI6GO41MFn4CeTBX+Y9h5s=
github.com/pion/ice/v4 v4.0.10/go.mod h1:y3M18aPhIxLlcO/4dn9X8LzLLSma84cx6emMSu14FGw= github.com/pion/ice/v4 v4.0.2/go.mod h1:DCdqyzgtsDNYN6/3U8044j3U7qsJ9KFJC92VnOWHvXg=
github.com/pion/interceptor v0.1.40 h1:e0BjnPcGpr2CFQgKhrQisBU7V3GXK6wrfYrGYaU6Jq4= github.com/pion/interceptor v0.1.37 h1:aRA8Zpab/wE7/c0O3fh1PqY0AJI3fCSEM5lRWJVorwI=
github.com/pion/interceptor v0.1.40/go.mod h1:Z6kqH7M/FYirg3frjGJ21VLSRJGBXB/KqaTIrdqnOic= github.com/pion/interceptor v0.1.37/go.mod h1:JzxbJ4umVTlZAf+/utHzNesY8tmRkM2lVmkS82TTj8Y=
github.com/pion/logging v0.2.3 h1:gHuf0zpoh1GW67Nr6Gj4cv5Z9ZscU7g/EaoC/Ke/igI= github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
github.com/pion/logging v0.2.3/go.mod h1:z8YfknkquMe1csOrxK5kc+5/ZPAzMxbKLX5aXpbpC90= github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
github.com/pion/mdns/v2 v2.0.7 h1:c9kM8ewCgjslaAmicYMFQIde2H9/lrZpjBkN8VwoVtM= github.com/pion/mdns/v2 v2.0.7 h1:c9kM8ewCgjslaAmicYMFQIde2H9/lrZpjBkN8VwoVtM=
github.com/pion/mdns/v2 v2.0.7/go.mod h1:vAdSYNAT0Jy3Ru0zl2YiW3Rm/fJCwIeM0nToenfOJKA= github.com/pion/mdns/v2 v2.0.7/go.mod h1:vAdSYNAT0Jy3Ru0zl2YiW3Rm/fJCwIeM0nToenfOJKA=
github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA= github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA=
github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8= github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=
github.com/pion/rtcp v1.2.15 h1:LZQi2JbdipLOj4eBjK4wlVoQWfrZbh3Q6eHtWtJBZBo= github.com/pion/rtcp v1.2.14 h1:KCkGV3vJ+4DAJmvP0vaQShsb0xkRfWkO540Gy102KyE=
github.com/pion/rtcp v1.2.15/go.mod h1:jlGuAjHMEXwMUHK78RgX0UmEJFV4zUKOFHR7OP+D3D0= github.com/pion/rtcp v1.2.14/go.mod h1:sn6qjxvnwyAkkPzPULIbVqSKI5Dv54Rv7VG0kNxh9L4=
github.com/pion/rtp v1.8.18 h1:yEAb4+4a8nkPCecWzQB6V/uEU18X1lQCGAQCjP+pyvU= github.com/pion/rtp v1.8.9 h1:E2HX740TZKaqdcPmf4pw6ZZuG8u5RlMMt+l3dxeu6Wk=
github.com/pion/rtp v1.8.18/go.mod h1:bAu2UFKScgzyFqvUKmbvzSdPr+NGbZtv6UB2hesqXBk= github.com/pion/rtp v1.8.9/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU=
github.com/pion/sctp v1.8.39 h1:PJma40vRHa3UTO3C4MyeJDQ+KIobVYRZQZ0Nt7SjQnE= github.com/pion/sctp v1.8.33 h1:dSE4wX6uTJBcNm8+YlMg7lw1wqyKHggsP5uKbdj+NZw=
github.com/pion/sctp v1.8.39/go.mod h1:cNiLdchXra8fHQwmIoqw0MbLLMs+f7uQ+dGMG2gWebE= github.com/pion/sctp v1.8.33/go.mod h1:beTnqSzewI53KWoG3nqB282oDMGrhNxBdb+JZnkCwRM=
github.com/pion/sdp/v3 v3.0.13 h1:uN3SS2b+QDZnWXgdr69SM8KB4EbcnPnPf2Laxhty/l4= github.com/pion/sdp/v3 v3.0.9 h1:pX++dCHoHUwq43kuwf3PyJfHlwIj4hXA7Vrifiq0IJY=
github.com/pion/sdp/v3 v3.0.13/go.mod h1:88GMahN5xnScv1hIMTqLdu/cOcUkj6a9ytbncwMCq2E= github.com/pion/sdp/v3 v3.0.9/go.mod h1:B5xmvENq5IXJimIO4zfp6LAe1fD9N+kFv+V/1lOdz8M=
github.com/pion/srtp/v3 v3.0.5 h1:8XLB6Dt3QXkMkRFpoqC3314BemkpMQK2mZeJc4pUKqo= github.com/pion/srtp/v3 v3.0.4 h1:2Z6vDVxzrX3UHEgrUyIGM4rRouoC7v+NiF1IHtp9B5M=
github.com/pion/srtp/v3 v3.0.5/go.mod h1:r1G7y5r1scZRLe2QJI/is+/O83W2d+JoEsuIexpw+uM= github.com/pion/srtp/v3 v3.0.4/go.mod h1:1Jx3FwDoxpRaTh1oRV8A/6G1BnFL+QI82eK4ms8EEJQ=
github.com/pion/stun/v3 v3.0.0 h1:4h1gwhWLWuZWOJIJR9s2ferRO+W3zA/b6ijOI6mKzUw= github.com/pion/stun/v3 v3.0.0 h1:4h1gwhWLWuZWOJIJR9s2ferRO+W3zA/b6ijOI6mKzUw=
github.com/pion/stun/v3 v3.0.0/go.mod h1:HvCN8txt8mwi4FBvS3EmDghW6aQJ24T+y+1TKjB5jyU= github.com/pion/stun/v3 v3.0.0/go.mod h1:HvCN8txt8mwi4FBvS3EmDghW6aQJ24T+y+1TKjB5jyU=
github.com/pion/transport/v3 v3.0.7 h1:iRbMH05BzSNwhILHoBoAPxoB9xQgOaJk+591KC9P1o0= github.com/pion/transport/v3 v3.0.7 h1:iRbMH05BzSNwhILHoBoAPxoB9xQgOaJk+591KC9P1o0=
github.com/pion/transport/v3 v3.0.7/go.mod h1:YleKiTZ4vqNxVwh77Z0zytYi7rXHl7j6uPLGhhz9rwo= github.com/pion/transport/v3 v3.0.7/go.mod h1:YleKiTZ4vqNxVwh77Z0zytYi7rXHl7j6uPLGhhz9rwo=
github.com/pion/turn/v4 v4.0.2 h1:ZqgQ3+MjP32ug30xAbD6Mn+/K4Sxi3SdNOTFf+7mpps= github.com/pion/turn/v4 v4.0.0 h1:qxplo3Rxa9Yg1xXDxxH8xaqcyGUtbHYw4QSCvmFWvhM=
github.com/pion/turn/v4 v4.0.2/go.mod h1:pMMKP/ieNAG/fN5cZiN4SDuyKsXtNTr0ccN7IToA1zs= github.com/pion/turn/v4 v4.0.0/go.mod h1:MuPDkm15nYSklKpN8vWJ9W2M0PlyQZqYt1McGuxG7mA=
github.com/pion/webrtc/v4 v4.0.16 h1:5f8QMVIbNvJr2mPRGi2QamkPa/LVUB6NWolOCwphKHA= github.com/pion/webrtc/v4 v4.0.0 h1:x8ec7uJQPP3D1iI8ojPAiTOylPI7Fa7QgqZrhpLyqZ8=
github.com/pion/webrtc/v4 v4.0.16/go.mod h1:C3uTCPzVafUA0eUzru9f47OgNt3nEO7ZJ6zNY6VSJno= github.com/pion/webrtc/v4 v4.0.0/go.mod h1:SfNn8CcFxR6OUVjLXVslAQ3a3994JhyE3Hw1jAuqEto=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= github.com/prometheus/client_golang v1.21.0 h1:DIsaGmiaBkSangBgMtWdNfxbMNdku5IK6iNhrEqWvdA=
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= github.com/prometheus/client_golang v1.21.0/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/psanford/httpreadat v0.1.0 h1:VleW1HS2zO7/4c7c7zNl33fO6oYACSagjJIyMIwZLUE= github.com/psanford/httpreadat v0.1.0 h1:VleW1HS2zO7/4c7c7zNl33fO6oYACSagjJIyMIwZLUE=
github.com/psanford/httpreadat v0.1.0/go.mod h1:Zg7P+TlBm3bYbyHTKv/EdtSJZn3qwbPwpfZ/I9GKCRE= github.com/psanford/httpreadat v0.1.0/go.mod h1:Zg7P+TlBm3bYbyHTKv/EdtSJZn3qwbPwpfZ/I9GKCRE=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
@ -156,11 +157,14 @@ github.com/sourcegraph/tf-dag v0.2.2-0.20250131204052-3e8ff1477b4f/go.mod h1:pzr
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
@ -175,14 +179,16 @@ github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU=
github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA=
go.bug.st/serial v1.6.2 h1:kn9LRX3sdm+WxWKufMlIRndwGfPWsH1/9lCWXQCasq8= go.bug.st/serial v1.6.2 h1:kn9LRX3sdm+WxWKufMlIRndwGfPWsH1/9lCWXQCasq8=
go.bug.st/serial v1.6.2/go.mod h1:UABfsluHAiaNI+La2iESysd9Vetq7VRdpxvjx7CmmOE= go.bug.st/serial v1.6.2/go.mod h1:UABfsluHAiaNI+La2iESysd9Vetq7VRdpxvjx7CmmOE=
golang.org/x/arch v0.17.0 h1:4O3dfLzd+lQewptAHqjewQZQDyEdejz3VwgeYwkZneU= golang.org/x/arch v0.15.0 h1:QtOrQd0bTUnhNVNndMpLHNWrDmYzZ2KDqSrEymqInZw=
golang.org/x/arch v0.17.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk= golang.org/x/arch v0.15.0/go.mod h1:JmwW7aLIoRUKgaTzhkiEFxvcEiQGyOg9BMonBJUS7EE=
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE= golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE=
golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -190,8 +196,8 @@ golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View File

@ -14,44 +14,44 @@ var (
) )
metricTimeSyncCount = promauto.NewCounter( metricTimeSyncCount = promauto.NewCounter(
prometheus.CounterOpts{ prometheus.CounterOpts{
Name: "jetkvm_timesync_total", Name: "jetkvm_timesync_count",
Help: "The number of times the timesync has been run", Help: "The number of times the timesync has been run",
}, },
) )
metricTimeSyncSuccessCount = promauto.NewCounter( metricTimeSyncSuccessCount = promauto.NewCounter(
prometheus.CounterOpts{ prometheus.CounterOpts{
Name: "jetkvm_timesync_success_total", Name: "jetkvm_timesync_success_count",
Help: "The number of times the timesync has been successful", Help: "The number of times the timesync has been successful",
}, },
) )
metricRTCUpdateCount = promauto.NewCounter( //nolint:unused metricRTCUpdateCount = promauto.NewCounter( //nolint:unused
prometheus.CounterOpts{ prometheus.CounterOpts{
Name: "jetkvm_timesync_rtc_update_total", Name: "jetkvm_timesync_rtc_update_count",
Help: "The number of times the RTC has been updated", Help: "The number of times the RTC has been updated",
}, },
) )
metricNtpTotalSuccessCount = promauto.NewCounter( metricNtpTotalSuccessCount = promauto.NewCounter(
prometheus.CounterOpts{ prometheus.CounterOpts{
Name: "jetkvm_timesync_ntp_total_success_total", Name: "jetkvm_timesync_ntp_total_success_count",
Help: "The total number of successful NTP requests", Help: "The total number of successful NTP requests",
}, },
) )
metricNtpTotalRequestCount = promauto.NewCounter( metricNtpTotalRequestCount = promauto.NewCounter(
prometheus.CounterOpts{ prometheus.CounterOpts{
Name: "jetkvm_timesync_ntp_total_request_total", Name: "jetkvm_timesync_ntp_total_request_count",
Help: "The total number of NTP requests sent", Help: "The total number of NTP requests sent",
}, },
) )
metricNtpSuccessCount = promauto.NewCounterVec( metricNtpSuccessCount = promauto.NewCounterVec(
prometheus.CounterOpts{ prometheus.CounterOpts{
Name: "jetkvm_timesync_ntp_success_total", Name: "jetkvm_timesync_ntp_success_count",
Help: "The number of successful NTP requests", Help: "The number of successful NTP requests",
}, },
[]string{"url"}, []string{"url"},
) )
metricNtpRequestCount = promauto.NewCounterVec( metricNtpRequestCount = promauto.NewCounterVec(
prometheus.CounterOpts{ prometheus.CounterOpts{
Name: "jetkvm_timesync_ntp_request_total", Name: "jetkvm_timesync_ntp_request_count",
Help: "The number of NTP requests sent to the server", Help: "The number of NTP requests sent to the server",
}, },
[]string{"url"}, []string{"url"},
@ -83,39 +83,39 @@ var (
metricHttpTotalSuccessCount = promauto.NewCounter( metricHttpTotalSuccessCount = promauto.NewCounter(
prometheus.CounterOpts{ prometheus.CounterOpts{
Name: "jetkvm_timesync_http_total_success_total", Name: "jetkvm_timesync_http_total_success_count",
Help: "The total number of successful HTTP requests", Help: "The total number of successful HTTP requests",
}, },
) )
metricHttpTotalRequestCount = promauto.NewCounter( metricHttpTotalRequestCount = promauto.NewCounter(
prometheus.CounterOpts{ prometheus.CounterOpts{
Name: "jetkvm_timesync_http_total_request_total", Name: "jetkvm_timesync_http_total_request_count",
Help: "The total number of HTTP requests sent", Help: "The total number of HTTP requests sent",
}, },
) )
metricHttpTotalCancelCount = promauto.NewCounter( metricHttpTotalCancelCount = promauto.NewCounter(
prometheus.CounterOpts{ prometheus.CounterOpts{
Name: "jetkvm_timesync_http_total_cancel_total", Name: "jetkvm_timesync_http_total_cancel_count",
Help: "The total number of HTTP requests cancelled", Help: "The total number of HTTP requests cancelled",
}, },
) )
metricHttpSuccessCount = promauto.NewCounterVec( metricHttpSuccessCount = promauto.NewCounterVec(
prometheus.CounterOpts{ prometheus.CounterOpts{
Name: "jetkvm_timesync_http_success_total", Name: "jetkvm_timesync_http_success_count",
Help: "The number of successful HTTP requests", Help: "The number of successful HTTP requests",
}, },
[]string{"url"}, []string{"url"},
) )
metricHttpRequestCount = promauto.NewCounterVec( metricHttpRequestCount = promauto.NewCounterVec(
prometheus.CounterOpts{ prometheus.CounterOpts{
Name: "jetkvm_timesync_http_request_total", Name: "jetkvm_timesync_http_request_count",
Help: "The number of HTTP requests sent to the server", Help: "The number of HTTP requests sent to the server",
}, },
[]string{"url"}, []string{"url"},
) )
metricHttpCancelCount = promauto.NewCounterVec( metricHttpCancelCount = promauto.NewCounterVec(
prometheus.CounterOpts{ prometheus.CounterOpts{
Name: "jetkvm_timesync_http_cancel_total", Name: "jetkvm_timesync_http_cancel_count",
Help: "The number of HTTP requests cancelled", Help: "The number of HTTP requests cancelled",
}, },
[]string{"url"}, []string{"url"},

View File

@ -5,7 +5,6 @@ import (
"fmt" "fmt"
"os" "os"
"path/filepath" "path/filepath"
"reflect"
"time" "time"
"github.com/fsnotify/fsnotify" "github.com/fsnotify/fsnotify"
@ -150,12 +149,6 @@ func (c *DHCPClient) loadLeaseFile() error {
} }
isFirstLoad := c.lease == nil isFirstLoad := c.lease == nil
// Skip processing if lease hasn't changed to avoid unnecessary wake-ups.
if reflect.DeepEqual(c.lease, lease) {
return nil
}
c.lease = lease c.lease = lease
if lease.IPAddress == nil { if lease.IPAddress == nil {

View File

@ -1,7 +1,6 @@
package kvm package kvm
import ( import (
"bytes"
"encoding/json" "encoding/json"
"io" "io"
"os" "os"
@ -56,23 +55,18 @@ func handleTerminalChannel(d *webrtc.DataChannel) {
return return
} }
if msg.IsString { if msg.IsString {
maybeJson := bytes.TrimSpace(msg.Data) var size TerminalSize
// Cheap check to see if this resembles JSON err := json.Unmarshal([]byte(msg.Data), &size)
if len(maybeJson) > 1 && maybeJson[0] == '{' && maybeJson[len(maybeJson)-1] == '}' { if err == nil {
var size TerminalSize err = pty.Setsize(ptmx, &pty.Winsize{
err := json.Unmarshal(maybeJson, &size) Rows: uint16(size.Rows),
Cols: uint16(size.Cols),
})
if err == nil { if err == nil {
err = pty.Setsize(ptmx, &pty.Winsize{ return
Rows: uint16(size.Rows),
Cols: uint16(size.Cols),
})
if err == nil {
scopedLogger.Info().Int("rows", size.Rows).Int("cols", size.Cols).Msg("Set terminal size")
return
}
} }
scopedLogger.Warn().Err(err).Msg("Failed to parse terminal size")
} }
scopedLogger.Warn().Err(err).Msg("Failed to parse terminal size")
} }
_, err := ptmx.Write(msg.Data) _, err := ptmx.Write(msg.Data)
if err != nil { if err != nil {

View File

@ -10,7 +10,7 @@ import LoadingSpinner from "@components/LoadingSpinner";
import Card, { GridCard } from "@components/Card"; import Card, { GridCard } from "@components/Card";
interface OverlayContentProps { interface OverlayContentProps {
readonly children: React.ReactNode; children: React.ReactNode;
} }
function OverlayContent({ children }: OverlayContentProps) { function OverlayContent({ children }: OverlayContentProps) {
return ( return (
@ -23,7 +23,7 @@ function OverlayContent({ children }: OverlayContentProps) {
} }
interface LoadingOverlayProps { interface LoadingOverlayProps {
readonly show: boolean; show: boolean;
} }
export function LoadingVideoOverlay({ show }: LoadingOverlayProps) { export function LoadingVideoOverlay({ show }: LoadingOverlayProps) {
@ -57,8 +57,8 @@ export function LoadingVideoOverlay({ show }: LoadingOverlayProps) {
} }
interface LoadingConnectionOverlayProps { interface LoadingConnectionOverlayProps {
readonly show: boolean; show: boolean;
readonly text: string; text: string;
} }
export function LoadingConnectionOverlay({ show, text }: LoadingConnectionOverlayProps) { export function LoadingConnectionOverlay({ show, text }: LoadingConnectionOverlayProps) {
return ( return (
@ -91,8 +91,8 @@ export function LoadingConnectionOverlay({ show, text }: LoadingConnectionOverla
} }
interface ConnectionErrorOverlayProps { interface ConnectionErrorOverlayProps {
readonly show: boolean; show: boolean;
readonly setupPeerConnection: () => Promise<void>; setupPeerConnection: () => Promise<void>;
} }
export function ConnectionFailedOverlay({ export function ConnectionFailedOverlay({
@ -153,7 +153,7 @@ export function ConnectionFailedOverlay({
} }
interface PeerConnectionDisconnectedOverlay { interface PeerConnectionDisconnectedOverlay {
readonly show: boolean; show: boolean;
} }
export function PeerConnectionDisconnectedOverlay({ export function PeerConnectionDisconnectedOverlay({
@ -207,8 +207,8 @@ export function PeerConnectionDisconnectedOverlay({
} }
interface HDMIErrorOverlayProps { interface HDMIErrorOverlayProps {
readonly show: boolean; show: boolean;
readonly hdmiState: string; hdmiState: string;
} }
export function HDMIErrorOverlay({ show, hdmiState }: HDMIErrorOverlayProps) { export function HDMIErrorOverlay({ show, hdmiState }: HDMIErrorOverlayProps) {
@ -310,8 +310,8 @@ export function HDMIErrorOverlay({ show, hdmiState }: HDMIErrorOverlayProps) {
} }
interface NoAutoplayPermissionsOverlayProps { interface NoAutoplayPermissionsOverlayProps {
readonly show: boolean; show: boolean;
readonly onPlayClick: () => void; onPlayClick: () => void;
} }
export function NoAutoplayPermissionsOverlay({ export function NoAutoplayPermissionsOverlay({
@ -361,7 +361,7 @@ export function NoAutoplayPermissionsOverlay({
} }
interface PointerLockBarProps { interface PointerLockBarProps {
readonly show: boolean; show: boolean;
} }
export function PointerLockBar({ show }: PointerLockBarProps) { export function PointerLockBar({ show }: PointerLockBarProps) {
@ -369,10 +369,10 @@ export function PointerLockBar({ show }: PointerLockBarProps) {
<AnimatePresence mode="wait"> <AnimatePresence mode="wait">
{show ? ( {show ? (
<motion.div <motion.div
className="flex w-full items-center justify-between bg-transparent" className="absolute -top-[36px] left-0 right-0 z-20 bg-white"
initial={{ opacity: 0, zIndex: 0 }} initial={{ y: 20, opacity: 0, zIndex: 0 }}
animate={{ opacity: 1, zIndex: 20 }} animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, zIndex: 0 }} exit={{ y: 43, zIndex: 0 }}
transition={{ duration: 0.5, ease: "easeInOut", delay: 0.5 }} transition={{ duration: 0.5, ease: "easeInOut", delay: 0.5 }}
> >
<div> <div>

View File

@ -1,22 +1,23 @@
import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useResizeObserver } from "usehooks-ts"; import { useResizeObserver } from "usehooks-ts";
import VirtualKeyboard from "@components/VirtualKeyboard";
import Actionbar from "@components/ActionBar";
import MacroBar from "@/components/MacroBar";
import InfoBar from "@components/InfoBar";
import notifications from "@/notifications";
import useKeyboard from "@/hooks/useKeyboard";
import { useJsonRpc } from "@/hooks/useJsonRpc";
import { cx } from "@/cva.config";
import { keys, modifiers } from "@/keyboardMappings";
import { import {
useHidStore, useHidStore,
useMouseStore, useMouseStore,
useRTCStore, useRTCStore,
useSettingsStore, useSettingsStore,
useUiStore,
useVideoStore, useVideoStore,
} from "@/hooks/stores"; } from "@/hooks/stores";
import { keys, modifiers } from "@/keyboardMappings";
import { cx } from "@/cva.config";
import VirtualKeyboard from "@components/VirtualKeyboard";
import Actionbar from "@components/ActionBar";
import MacroBar from "@/components/MacroBar";
import InfoBar from "@components/InfoBar";
import useKeyboard from "@/hooks/useKeyboard";
import { useJsonRpc } from "@/hooks/useJsonRpc";
import notifications from "@/notifications";
import { import {
HDMIErrorOverlay, HDMIErrorOverlay,
@ -46,11 +47,6 @@ export default function WebRTCVideo() {
clientHeight: videoClientHeight, clientHeight: videoClientHeight,
} = useVideoStore(); } = useVideoStore();
// Video enhancement settings
const videoSaturation = useSettingsStore(state => state.videoSaturation);
const videoBrightness = useSettingsStore(state => state.videoBrightness);
const videoContrast = useSettingsStore(state => state.videoContrast);
// HID related states // HID related states
const keyboardLedStateSyncAvailable = useHidStore(state => state.keyboardLedStateSyncAvailable); const keyboardLedStateSyncAvailable = useHidStore(state => state.keyboardLedStateSyncAvailable);
const keyboardLedSync = useSettingsStore(state => state.keyboardLedSync); const keyboardLedSync = useSettingsStore(state => state.keyboardLedSync);
@ -71,8 +67,6 @@ export default function WebRTCVideo() {
const hdmiError = ["no_lock", "no_signal", "out_of_range"].includes(hdmiState); const hdmiError = ["no_lock", "no_signal", "out_of_range"].includes(hdmiState);
const isVideoLoading = !isPlaying; const isVideoLoading = !isPlaying;
const [blockWheelEvent, setBlockWheelEvent] = useState(false);
// Misc states and hooks // Misc states and hooks
const [send] = useJsonRpc(); const [send] = useJsonRpc();
@ -111,9 +105,8 @@ export default function WebRTCVideo() {
); );
// Pointer lock and keyboard lock related // Pointer lock and keyboard lock related
const isPointerLockPossible = window.location.protocol === "https:" || window.location.hostname === "localhost"; const isPointerLockPossible = window.location.protocol === "https:";
const isFullscreenEnabled = document.fullscreenEnabled;
const checkNavigatorPermissions = useCallback(async (permissionName: string) => { const checkNavigatorPermissions = useCallback(async (permissionName: string) => {
const name = permissionName as PermissionName; const name = permissionName as PermissionName;
const { state } = await navigator.permissions.query({ name }); const { state } = await navigator.permissions.query({ name });
@ -121,47 +114,23 @@ export default function WebRTCVideo() {
}, []); }, []);
const requestPointerLock = useCallback(async () => { const requestPointerLock = useCallback(async () => {
if (!isPointerLockPossible if (document.pointerLockElement) return;
|| videoElm.current === null
|| document.pointerLockElement) return;
const isPointerLockGranted = await checkNavigatorPermissions("pointer-lock"); const isPointerLockGranted = await checkNavigatorPermissions("pointer-lock");
if (isPointerLockGranted && settings.mouseMode === "relative") { if (isPointerLockGranted && settings.mouseMode === "relative") {
await videoElm.current.requestPointerLock(); videoElm.current?.requestPointerLock();
} }
}, [checkNavigatorPermissions, isPointerLockPossible, settings.mouseMode]); }, [checkNavigatorPermissions, settings.mouseMode]);
const requestKeyboardLock = useCallback(async () => {
if (videoElm.current === null) return;
const isKeyboardLockGranted = await checkNavigatorPermissions("keyboard-lock");
if (isKeyboardLockGranted) {
if ("keyboard" in navigator) {
// @ts-expect-error - keyboard lock is not supported in all browsers
await navigator.keyboard.lock();
}
}
}, [checkNavigatorPermissions]);
const releaseKeyboardLock = useCallback(async () => {
if (videoElm.current === null || document.fullscreenElement !== videoElm.current) return;
if ("keyboard" in navigator) {
// @ts-expect-error - keyboard unlock is not supported in all browsers
await navigator.keyboard.unlock();
}
}, []);
useEffect(() => { useEffect(() => {
if (!isPointerLockPossible || !videoElm.current) return; if (!isPointerLockPossible || !videoElm.current) return;
const handlePointerLockChange = () => { const handlePointerLockChange = () => {
if (document.pointerLockElement) { if (document.pointerLockElement) {
notifications.success("Pointer lock Enabled, press escape to unlock"); notifications.success("Pointer lock Enabled, hold escape to exit");
setIsPointerLockActive(true); setIsPointerLockActive(true);
} else { } else {
notifications.success("Pointer lock Disabled"); notifications.success("Pointer lock disabled");
setIsPointerLockActive(false); setIsPointerLockActive(false);
} }
}; };
@ -174,39 +143,27 @@ export default function WebRTCVideo() {
return () => { return () => {
abortController.abort(); abortController.abort();
}; };
}, [isPointerLockPossible]); }, [isPointerLockPossible, videoElm]);
const requestFullscreen = useCallback(async () => { const requestFullscreen = useCallback(async () => {
if (!isFullscreenEnabled || !videoElm.current) return; videoElm.current?.requestFullscreen({
// per https://wicg.github.io/keyboard-lock/#system-key-press-handler
// If keyboard lock is activated after fullscreen is already in effect, then the user my
// see multiple messages about how to exit fullscreen. For this reason, we recommend that
// developers call lock() before they enter fullscreen:
await requestKeyboardLock();
await requestPointerLock();
await videoElm.current.requestFullscreen({
navigationUI: "show", navigationUI: "show",
}); });
}, [isFullscreenEnabled, requestKeyboardLock, requestPointerLock]);
// setup to release the keyboard lock anytime the fullscreen ends // we do not care about pointer lock if it's for fullscreen
useEffect(() => { await requestPointerLock();
if (!videoElm.current) return;
const handleFullscreenChange = () => { const isKeyboardLockGranted = await checkNavigatorPermissions("keyboard-lock");
if (!document.fullscreenElement) { if (isKeyboardLockGranted) {
releaseKeyboardLock(); if ("keyboard" in navigator) {
// @ts-expect-error - keyboard lock is not supported in all browsers
await navigator.keyboard.lock();
} }
}; }
}, [requestPointerLock, checkNavigatorPermissions]);
document.addEventListener("fullscreenchange ", handleFullscreenChange);
}, [releaseKeyboardLock]);
// Mouse-related // Mouse-related
const calcDelta = (pos: number) => (Math.abs(pos) < 10 ? pos * 2 : pos); const calcDelta = (pos: number) => (Math.abs(pos) < 10 ? pos * 2 : pos);
const sendRelMouseMovement = useCallback( const sendRelMouseMovement = useCallback(
(x: number, y: number, buttons: number) => { (x: number, y: number, buttons: number) => {
if (settings.mouseMode !== "relative") return; if (settings.mouseMode !== "relative") return;
@ -221,13 +178,18 @@ export default function WebRTCVideo() {
const relMouseMoveHandler = useCallback( const relMouseMoveHandler = useCallback(
(e: MouseEvent) => { (e: MouseEvent) => {
if (settings.mouseMode !== "relative") return; if (settings.mouseMode !== "relative") return;
if (isPointerLockActive === false && isPointerLockPossible) return; if (isPointerLockActive === false && isPointerLockPossible === true) return;
// Send mouse movement // Send mouse movement
const { buttons } = e; const { buttons } = e;
sendRelMouseMovement(e.movementX, e.movementY, buttons); sendRelMouseMovement(e.movementX, e.movementY, buttons);
}, },
[isPointerLockActive, isPointerLockPossible, sendRelMouseMovement, settings.mouseMode], [
isPointerLockActive,
isPointerLockPossible,
sendRelMouseMovement,
settings.mouseMode,
],
); );
const sendAbsMouseMovement = useCallback( const sendAbsMouseMovement = useCallback(
@ -281,16 +243,18 @@ export default function WebRTCVideo() {
const { buttons } = e; const { buttons } = e;
sendAbsMouseMovement(x, y, buttons); sendAbsMouseMovement(x, y, buttons);
}, },
[settings.mouseMode, videoClientWidth, videoClientHeight, videoWidth, videoHeight, sendAbsMouseMovement], [
sendAbsMouseMovement,
videoClientHeight,
videoClientWidth,
videoWidth,
videoHeight,
settings.mouseMode,
],
); );
const mouseWheelHandler = useCallback( const mouseWheelHandler = useCallback(
(e: WheelEvent) => { (e: WheelEvent) => {
if (settings.scrollThrottling && blockWheelEvent) {
return;
}
// Determine if the wheel event is an accel scroll value // Determine if the wheel event is an accel scroll value
const isAccel = Math.abs(e.deltaY) >= 100; const isAccel = Math.abs(e.deltaY) >= 100;
@ -298,7 +262,7 @@ export default function WebRTCVideo() {
const accelScrollValue = e.deltaY / 100; const accelScrollValue = e.deltaY / 100;
// Calculate the no accel scroll value // Calculate the no accel scroll value
const noAccelScrollValue = Math.sign(e.deltaY); const noAccelScrollValue = e.deltaY > 0 ? 1 : e.deltaY < 0 ? -1 : 0;
// Get scroll value // Get scroll value
const scrollValue = isAccel ? accelScrollValue : noAccelScrollValue; const scrollValue = isAccel ? accelScrollValue : noAccelScrollValue;
@ -310,14 +274,8 @@ export default function WebRTCVideo() {
const invertedScrollValue = -clampedScrollValue; const invertedScrollValue = -clampedScrollValue;
send("wheelReport", { wheelY: invertedScrollValue }); send("wheelReport", { wheelY: invertedScrollValue });
// Apply blocking delay based of throttling settings
if (settings.scrollThrottling && !blockWheelEvent) {
setBlockWheelEvent(true);
setTimeout(() => setBlockWheelEvent(false), settings.scrollThrottling);
}
}, },
[send, blockWheelEvent, settings], [send],
); );
const resetMousePosition = useCallback(() => { const resetMousePosition = useCallback(() => {
@ -397,6 +355,13 @@ export default function WebRTCVideo() {
let code = e.code; let code = e.code;
const key = e.key; const key = e.key;
// if (document.activeElement?.id !== "videoFocusTrap") {
// console.log("KEYUP: Not focusing on the video", document.activeElement);
// return;
// }
// console.log(document.activeElement);
if (!isKeyboardLedManagedByHost) { if (!isKeyboardLedManagedByHost) {
setIsNumLockActive(e.getModifierState("NumLock")); setIsNumLockActive(e.getModifierState("NumLock"));
setIsCapsLockActive(e.getModifierState("CapsLock")); setIsCapsLockActive(e.getModifierState("CapsLock"));
@ -474,15 +439,13 @@ export default function WebRTCVideo() {
); );
const videoKeyUpHandler = useCallback((e: KeyboardEvent) => { const videoKeyUpHandler = useCallback((e: KeyboardEvent) => {
if (!videoElm.current) return;
// In fullscreen mode in chrome & safari, the space key is used to pause/play the video // In fullscreen mode in chrome & safari, the space key is used to pause/play the video
// there is no way to prevent this, so we need to simply force play the video when it's paused. // there is no way to prevent this, so we need to simply force play the video when it's paused.
// Fix only works in chrome based browsers. // Fix only works in chrome based browsers.
if (e.code === "Space") { if (e.code === "Space") {
if (videoElm.current.paused) { if (videoElm.current?.paused == true) {
console.log("Force playing video"); console.log("Force playing video");
videoElm.current.play(); videoElm.current?.play();
} }
} }
}, []); }, []);
@ -491,6 +454,7 @@ export default function WebRTCVideo() {
(mediaStream: MediaStream) => { (mediaStream: MediaStream) => {
if (!videoElm.current) return; if (!videoElm.current) return;
const videoElmRefValue = videoElm.current; const videoElmRefValue = videoElm.current;
// console.log("Adding stream to video element", videoElmRefValue);
videoElmRefValue.srcObject = mediaStream; videoElmRefValue.srcObject = mediaStream;
updateVideoSizeStore(videoElmRefValue); updateVideoSizeStore(videoElmRefValue);
}, },
@ -506,6 +470,7 @@ export default function WebRTCVideo() {
peerConnection.addEventListener( peerConnection.addEventListener(
"track", "track",
(e: RTCTrackEvent) => { (e: RTCTrackEvent) => {
// console.log("Adding stream to video element");
addStreamToVideoElm(e.streams[0]); addStreamToVideoElm(e.streams[0]);
}, },
{ signal }, { signal },
@ -521,6 +486,7 @@ export default function WebRTCVideo() {
useEffect( useEffect(
function updateVideoStream() { function updateVideoStream() {
if (!mediaStream) return; if (!mediaStream) return;
console.log("Updating video stream from mediaStream");
// We set the as early as possible // We set the as early as possible
addStreamToVideoElm(mediaStream); addStreamToVideoElm(mediaStream);
}, },
@ -542,6 +508,9 @@ export default function WebRTCVideo() {
document.addEventListener("keydown", keyDownHandler, { signal }); document.addEventListener("keydown", keyDownHandler, { signal });
document.addEventListener("keyup", keyUpHandler, { signal }); document.addEventListener("keyup", keyUpHandler, { signal });
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
window.clearKeys = () => sendKeyboardEvent([], []);
window.addEventListener("blur", resetKeyboardState, { signal }); window.addEventListener("blur", resetKeyboardState, { signal });
document.addEventListener("visibilitychange", resetKeyboardState, { signal }); document.addEventListener("visibilitychange", resetKeyboardState, { signal });
@ -549,7 +518,7 @@ export default function WebRTCVideo() {
abortController.abort(); abortController.abort();
}; };
}, },
[keyDownHandler, keyUpHandler, resetKeyboardState], [keyDownHandler, keyUpHandler, resetKeyboardState, sendKeyboardEvent],
); );
// Setup Video Event Listeners // Setup Video Event Listeners
@ -571,41 +540,78 @@ export default function WebRTCVideo() {
abortController.abort(); abortController.abort();
}; };
}, },
[onVideoPlaying, videoKeyUpHandler], [
absMouseMoveHandler,
resetMousePosition,
onVideoPlaying,
mouseWheelHandler,
videoKeyUpHandler,
],
); );
// Setup Mouse Events // Setup Absolute Mouse Events
useEffect( useEffect(
function setMouseModeEventListeners() { function setAbsoluteMouseModeEventListeners() {
const videoElmRefValue = videoElm.current; const videoElmRefValue = videoElm.current;
if (!videoElmRefValue) return; if (!videoElmRefValue) return;
const isRelativeMouseMode = (settings.mouseMode === "relative");
if (settings.mouseMode !== "absolute") return;
const abortController = new AbortController(); const abortController = new AbortController();
const signal = abortController.signal; const signal = abortController.signal;
videoElmRefValue.addEventListener("mousemove", isRelativeMouseMode ? relMouseMoveHandler : absMouseMoveHandler, { signal }); videoElmRefValue.addEventListener("mousemove", absMouseMoveHandler, { signal });
videoElmRefValue.addEventListener("pointerdown", isRelativeMouseMode ? relMouseMoveHandler : absMouseMoveHandler, { signal }); videoElmRefValue.addEventListener("pointerdown", absMouseMoveHandler, { signal });
videoElmRefValue.addEventListener("pointerup", isRelativeMouseMode ? relMouseMoveHandler :absMouseMoveHandler, { signal }); videoElmRefValue.addEventListener("pointerup", absMouseMoveHandler, { signal });
videoElmRefValue.addEventListener("wheel", mouseWheelHandler, { videoElmRefValue.addEventListener("wheel", mouseWheelHandler, {
signal, signal,
passive: true, passive: true,
}); });
if (isRelativeMouseMode) { // Reset the mouse position when the window is blurred or the document is hidden
videoElmRefValue.addEventListener("click", const local = resetMousePosition;
() => { window.addEventListener("blur", local, { signal });
if (isPointerLockPossible && !isPointerLockActive && !document.pointerLockElement) { document.addEventListener("visibilitychange", local, { signal });
requestPointerLock(); const preventContextMenu = (e: MouseEvent) => e.preventDefault();
} videoElmRefValue.addEventListener("contextmenu", preventContextMenu, { signal });
},
{ signal }, return () => {
); abortController.abort();
} else { };
// Reset the mouse position when the window is blurred or the document is hidden },
window.addEventListener("blur", resetMousePosition, { signal }); [absMouseMoveHandler, mouseWheelHandler, resetMousePosition, settings.mouseMode],
document.addEventListener("visibilitychange", resetMousePosition, { signal }); );
}
// Setup Relative Mouse Events
const containerRef = useRef<HTMLDivElement>(null);
useEffect(
function setupRelativeMouseEventListeners() {
if (settings.mouseMode !== "relative") return;
// Relative mouse mode should only be active if the pointer lock is active and Pointer Lock is possible
const videoElmRefValue = videoElm.current;
if (!videoElmRefValue) return;
const abortController = new AbortController();
const signal = abortController.signal;
videoElmRefValue.addEventListener("mousemove", relMouseMoveHandler, { signal });
videoElmRefValue.addEventListener("pointerdown", relMouseMoveHandler, { signal });
videoElmRefValue.addEventListener("pointerup", relMouseMoveHandler, { signal });
videoElmRefValue.addEventListener(
"click",
() => {
if (isPointerLockPossible && !document.pointerLockElement) {
requestPointerLock();
}
},
{ signal },
);
videoElmRefValue.addEventListener("wheel", mouseWheelHandler, {
signal,
passive: true,
});
const preventContextMenu = (e: MouseEvent) => e.preventDefault(); const preventContextMenu = (e: MouseEvent) => e.preventDefault();
videoElmRefValue.addEventListener("contextmenu", preventContextMenu, { signal }); videoElmRefValue.addEventListener("contextmenu", preventContextMenu, { signal });
@ -614,18 +620,24 @@ export default function WebRTCVideo() {
abortController.abort(); abortController.abort();
}; };
}, },
[absMouseMoveHandler, isPointerLockActive, isPointerLockPossible, mouseWheelHandler, relMouseMoveHandler, requestPointerLock, resetMousePosition, settings.mouseMode], [
settings.mouseMode,
relMouseMoveHandler,
mouseWheelHandler,
disableVideoFocusTrap,
requestPointerLock,
isPointerLockPossible,
isPointerLockActive,
],
); );
const containerRef = useRef<HTMLDivElement>(null);
const hasNoAutoPlayPermissions = useMemo(() => { const hasNoAutoPlayPermissions = useMemo(() => {
if (peerConnection?.connectionState !== "connected") return false; if (peerConnection?.connectionState !== "connected") return false;
if (isPlaying) return false; if (isPlaying) return false;
if (hdmiError) return false; if (hdmiError) return false;
if (videoHeight === 0 || videoWidth === 0) return false; if (videoHeight === 0 || videoWidth === 0) return false;
return true; return true;
}, [hdmiError, isPlaying, peerConnection?.connectionState, videoHeight, videoWidth]); }, [peerConnection?.connectionState, isPlaying, hdmiError, videoHeight, videoWidth]);
const showPointerLockBar = useMemo(() => { const showPointerLockBar = useMemo(() => {
if (settings.mouseMode !== "relative") return false; if (settings.mouseMode !== "relative") return false;
@ -635,7 +647,15 @@ export default function WebRTCVideo() {
if (!isPlaying) return false; if (!isPlaying) return false;
if (videoHeight === 0 || videoWidth === 0) return false; if (videoHeight === 0 || videoWidth === 0) return false;
return true; return true;
}, [isPlaying, isPointerLockActive, isPointerLockPossible, isVideoLoading, settings.mouseMode, videoHeight, videoWidth]); }, [
settings.mouseMode,
isPointerLockPossible,
isPointerLockActive,
isVideoLoading,
isPlaying,
videoHeight,
videoWidth,
]);
return ( return (
<div className="grid h-full w-full grid-rows-(--grid-layout)"> <div className="grid h-full w-full grid-rows-(--grid-layout)">
@ -665,10 +685,10 @@ export default function WebRTCVideo() {
<div className="relative grow overflow-hidden"> <div className="relative grow overflow-hidden">
<div className="flex h-full flex-col"> <div className="flex h-full flex-col">
<div className="grid grow grid-rows-(--grid-bodyFooter) overflow-hidden"> <div className="grid grow grid-rows-(--grid-bodyFooter) overflow-hidden">
{/* In relative mouse mode and under https, we enable the pointer lock, and to do so we need a bar to show the user to click on the video to enable mouse control */}
<PointerLockBar show={showPointerLockBar} />
<div className="relative mx-4 my-2 flex items-center justify-center overflow-hidden"> <div className="relative mx-4 my-2 flex items-center justify-center overflow-hidden">
<div className="relative flex h-full w-full items-center justify-center"> <div className="relative flex h-full w-full items-center justify-center">
{/* In relative mouse mode and under https, we enable the pointer lock, and to do so we need a bar to show the user to click on the video to enable mouse control */}
<PointerLockBar show={showPointerLockBar} />
<video <video
ref={videoElm} ref={videoElm}
autoPlay={true} autoPlay={true}
@ -679,9 +699,6 @@ export default function WebRTCVideo() {
playsInline playsInline
disablePictureInPicture disablePictureInPicture
controlsList="nofullscreen" controlsList="nofullscreen"
style={{
filter: `saturate(${videoSaturation}) brightness(${videoBrightness}) contrast(${videoContrast})`,
}}
className={cx( className={cx(
"max-h-full min-h-[384px] max-w-full min-w-[512px] bg-black/50 object-contain transition-all duration-1000", "max-h-full min-h-[384px] max-w-full min-w-[512px] bg-black/50 object-contain transition-all duration-1000",
{ {

View File

@ -314,19 +314,8 @@ interface SettingsState {
keyboardLedSync: KeyboardLedSync; keyboardLedSync: KeyboardLedSync;
setKeyboardLedSync: (sync: KeyboardLedSync) => void; setKeyboardLedSync: (sync: KeyboardLedSync) => void;
scrollThrottling: number;
setScrollThrottling: (value: number) => void;
showPressedKeys: boolean; showPressedKeys: boolean;
setShowPressedKeys: (show: boolean) => void; setShowPressedKeys: (show: boolean) => void;
// Video enhancement settings
videoSaturation: number;
setVideoSaturation: (value: number) => void;
videoBrightness: number;
setVideoBrightness: (value: number) => void;
videoContrast: number;
setVideoContrast: (value: number) => void;
} }
export const useSettingsStore = create( export const useSettingsStore = create(
@ -365,19 +354,8 @@ export const useSettingsStore = create(
keyboardLedSync: "auto", keyboardLedSync: "auto",
setKeyboardLedSync: sync => set({ keyboardLedSync: sync }), setKeyboardLedSync: sync => set({ keyboardLedSync: sync }),
scrollThrottling: 0,
setScrollThrottling: value => set({ scrollThrottling: value }),
showPressedKeys: true, showPressedKeys: true,
setShowPressedKeys: show => set({ showPressedKeys: show }), setShowPressedKeys: show => set({ showPressedKeys: show }),
// Video enhancement settings with default values (1.0 = normal)
videoSaturation: 1.0,
setVideoSaturation: value => set({ videoSaturation: value }),
videoBrightness: 1.0,
setVideoBrightness: value => set({ videoBrightness: value }),
videoContrast: 1.0,
setVideoContrast: value => set({ videoContrast: value }),
}), }),
{ {
name: "settings", name: "settings",

View File

@ -1,9 +1,9 @@
import { SettingsItem } from "./devices.$id.settings";
import { Checkbox } from "@/components/Checkbox"; import { Checkbox } from "@/components/Checkbox";
import { SettingsPageHeader } from "@/components/SettingsPageheader"; import { SettingsPageHeader } from "@/components/SettingsPageheader";
import { useSettingsStore } from "@/hooks/stores";
import { SettingsItem } from "./devices.$id.settings"; import { useSettingsStore } from "@/hooks/stores";
export default function SettingsCtrlAltDelRoute() { export default function SettingsCtrlAltDelRoute() {
const enableCtrlAltDel = useSettingsStore(state => state.actionBarCtrlAltDel); const enableCtrlAltDel = useSettingsStore(state => state.actionBarCtrlAltDel);

View File

@ -9,7 +9,6 @@ import { useSettingsStore } from "@/hooks/stores";
import { useJsonRpc } from "@/hooks/useJsonRpc"; import { useJsonRpc } from "@/hooks/useJsonRpc";
import notifications from "@/notifications"; import notifications from "@/notifications";
import { SettingsPageHeader } from "@components/SettingsPageheader"; import { SettingsPageHeader } from "@components/SettingsPageheader";
import { SelectMenuBasic } from "@components/SelectMenuBasic";
import { useFeatureFlag } from "../hooks/useFeatureFlag"; import { useFeatureFlag } from "../hooks/useFeatureFlag";
import { cx } from "../cva.config"; import { cx } from "../cva.config";
@ -27,19 +26,6 @@ export default function SettingsMouseRoute() {
const [jiggler, setJiggler] = useState(false); const [jiggler, setJiggler] = useState(false);
const scrollThrottling = useSettingsStore(state => state.scrollThrottling);
const setScrollThrottling = useSettingsStore(
state => state.setScrollThrottling,
);
const scrollThrottlingOptions = [
{ value: "0", label: "Off" },
{ value: "10", label: "Low" },
{ value: "25", label: "Medium" },
{ value: "50", label: "High" },
{ value: "100", label: "Very High" },
];
const [send] = useJsonRpc(); const [send] = useJsonRpc();
useEffect(() => { useEffect(() => {
@ -79,21 +65,6 @@ export default function SettingsMouseRoute() {
/> />
</SettingsItem> </SettingsItem>
<SettingsItem
title="Scroll Throttling"
description="Reduce the frequency of scroll events"
>
<SelectMenuBasic
size="SM"
label=""
className="max-w-[292px]"
value={scrollThrottling}
fullWidth
onChange={e => setScrollThrottling(parseInt(e.target.value))}
options={scrollThrottlingOptions}
/>
</SettingsItem>
<SettingsItem <SettingsItem
title="Jiggler" title="Jiggler"
description="Simulate movement of a computer mouse. Prevents sleep mode, standby mode or the screensaver from activating" description="Simulate movement of a computer mouse. Prevents sleep mode, standby mode or the screensaver from activating"

View File

@ -4,7 +4,6 @@ import { Button } from "@/components/Button";
import { TextAreaWithLabel } from "@/components/TextArea"; import { TextAreaWithLabel } from "@/components/TextArea";
import { useJsonRpc } from "@/hooks/useJsonRpc"; import { useJsonRpc } from "@/hooks/useJsonRpc";
import { SettingsPageHeader } from "@components/SettingsPageheader"; import { SettingsPageHeader } from "@components/SettingsPageheader";
import { useSettingsStore } from "@/hooks/stores";
import notifications from "../notifications"; import notifications from "../notifications";
import { SelectMenuBasic } from "../components/SelectMenuBasic"; import { SelectMenuBasic } from "../components/SelectMenuBasic";
@ -46,14 +45,6 @@ export default function SettingsVideoRoute() {
const [customEdidValue, setCustomEdidValue] = useState<string | null>(null); const [customEdidValue, setCustomEdidValue] = useState<string | null>(null);
const [edid, setEdid] = useState<string | null>(null); const [edid, setEdid] = useState<string | null>(null);
// Video enhancement settings from store
const videoSaturation = useSettingsStore(state => state.videoSaturation);
const setVideoSaturation = useSettingsStore(state => state.setVideoSaturation);
const videoBrightness = useSettingsStore(state => state.videoBrightness);
const setVideoBrightness = useSettingsStore(state => state.setVideoBrightness);
const videoContrast = useSettingsStore(state => state.videoContrast);
const setVideoContrast = useSettingsStore(state => state.setVideoContrast);
useEffect(() => { useEffect(() => {
send("getStreamQualityFactor", {}, resp => { send("getStreamQualityFactor", {}, resp => {
if ("error" in resp) return; if ("error" in resp) return;
@ -135,73 +126,6 @@ export default function SettingsVideoRoute() {
onChange={e => handleStreamQualityChange(e.target.value)} onChange={e => handleStreamQualityChange(e.target.value)}
/> />
</SettingsItem> </SettingsItem>
{/* Video Enhancement Settings */}
<SettingsItem
title="Video Enhancement"
description="Adjust color settings to make the video output more vibrant and colorful"
/>
<div className="space-y-4 pl-4">
<SettingsItem
title="Saturation"
description={`Color saturation (${videoSaturation.toFixed(1)}x)`}
>
<input
type="range"
min="0.5"
max="2.0"
step="0.1"
value={videoSaturation}
onChange={e => setVideoSaturation(parseFloat(e.target.value))}
className="w-32 h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
/>
</SettingsItem>
<SettingsItem
title="Brightness"
description={`Brightness level (${videoBrightness.toFixed(1)}x)`}
>
<input
type="range"
min="0.5"
max="1.5"
step="0.1"
value={videoBrightness}
onChange={e => setVideoBrightness(parseFloat(e.target.value))}
className="w-32 h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
/>
</SettingsItem>
<SettingsItem
title="Contrast"
description={`Contrast level (${videoContrast.toFixed(1)}x)`}
>
<input
type="range"
min="0.5"
max="2.0"
step="0.1"
value={videoContrast}
onChange={e => setVideoContrast(parseFloat(e.target.value))}
className="w-32 h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
/>
</SettingsItem>
<div className="flex gap-2">
<Button
size="SM"
theme="light"
text="Reset to Default"
onClick={() => {
setVideoSaturation(1.0);
setVideoBrightness(1.0);
setVideoContrast(1.0);
}}
/>
</div>
</div>
<SettingsItem <SettingsItem
title="EDID" title="EDID"
description="Adjust the EDID settings for the display" description="Adjust the EDID settings for the display"