mirror of
https://github.com/m1k1o/neko.git
synced 2024-07-24 14:40:50 +12:00
private mode implementation.
This commit is contained in:
parent
f549171ded
commit
d004ddd68f
@ -4,6 +4,8 @@ import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
|
||||
"gitlab.com/demodesk/neko/server/pkg/auth"
|
||||
"gitlab.com/demodesk/neko/server/pkg/types"
|
||||
"gitlab.com/demodesk/neko/server/pkg/utils"
|
||||
@ -13,6 +15,8 @@ type RoomHandler struct {
|
||||
sessions types.SessionManager
|
||||
desktop types.DesktopManager
|
||||
capture types.CaptureManager
|
||||
|
||||
privateModeImage []byte
|
||||
}
|
||||
|
||||
func New(
|
||||
@ -20,13 +24,32 @@ func New(
|
||||
desktop types.DesktopManager,
|
||||
capture types.CaptureManager,
|
||||
) *RoomHandler {
|
||||
// Init
|
||||
|
||||
return &RoomHandler{
|
||||
h := &RoomHandler{
|
||||
sessions: sessions,
|
||||
desktop: desktop,
|
||||
capture: capture,
|
||||
}
|
||||
|
||||
// generate fallback image for private mode when needed
|
||||
sessions.OnPrivateModeChanged(func(isPrivateMode bool) {
|
||||
if !isPrivateMode {
|
||||
log.Debug().Msg("clearing private mode fallback image")
|
||||
h.privateModeImage = nil
|
||||
return
|
||||
}
|
||||
|
||||
img := h.desktop.GetScreenshotImage()
|
||||
bytes, err := utils.CreateJPGImage(img, 90)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("could not generate private mode fallback image")
|
||||
return
|
||||
}
|
||||
|
||||
log.Debug().Msg("using private mode fallback image")
|
||||
h.privateModeImage = bytes
|
||||
})
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
func (h *RoomHandler) Route(r types.Router) {
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"gitlab.com/demodesk/neko/server/pkg/auth"
|
||||
"gitlab.com/demodesk/neko/server/pkg/types"
|
||||
"gitlab.com/demodesk/neko/server/pkg/types/event"
|
||||
"gitlab.com/demodesk/neko/server/pkg/types/message"
|
||||
@ -81,6 +82,19 @@ func (h *RoomHandler) screenShotGet(w http.ResponseWriter, r *http.Request) erro
|
||||
}
|
||||
|
||||
func (h *RoomHandler) screenCastGet(w http.ResponseWriter, r *http.Request) error {
|
||||
// display fallback image when private mode is enabled even if screencast is not
|
||||
if session, ok := auth.GetSession(r); ok && session.PrivateModeEnabled() {
|
||||
if h.privateModeImage != nil {
|
||||
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
|
||||
w.Header().Set("Content-Type", "image/jpeg")
|
||||
|
||||
_, err := w.Write(h.privateModeImage)
|
||||
return err
|
||||
}
|
||||
|
||||
return utils.HttpBadRequest("private mode is enabled but no fallback image available")
|
||||
}
|
||||
|
||||
screencast := h.capture.Screencast()
|
||||
if !screencast.Enabled() {
|
||||
return utils.HttpBadRequest("screencast pipeline is not enabled")
|
||||
|
@ -56,6 +56,9 @@ type SessionManagerCtx struct {
|
||||
host types.Session
|
||||
hostMu sync.Mutex
|
||||
|
||||
privateMode bool
|
||||
privateModeMu sync.Mutex
|
||||
|
||||
cursors map[types.Session][]types.Cursor
|
||||
cursorsMu sync.Mutex
|
||||
|
||||
@ -197,6 +200,39 @@ func (manager *SessionManagerCtx) ClearHost() {
|
||||
manager.SetHost(nil)
|
||||
}
|
||||
|
||||
// ---
|
||||
// private mode
|
||||
// ---
|
||||
|
||||
func (manager *SessionManagerCtx) SetPrivateMode(isPrivateMode bool) {
|
||||
manager.privateModeMu.Lock()
|
||||
|
||||
// only if value changed
|
||||
if manager.privateMode == isPrivateMode {
|
||||
manager.privateModeMu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
// update webrtc paused state for all sessions
|
||||
for _, session := range manager.List() {
|
||||
if webrtcPeer := session.GetWebRTCPeer(); webrtcPeer != nil {
|
||||
webrtcPeer.SetPaused(isPrivateMode && !session.Profile().IsAdmin)
|
||||
}
|
||||
}
|
||||
|
||||
manager.privateMode = isPrivateMode
|
||||
manager.privateModeMu.Unlock()
|
||||
|
||||
manager.emmiter.Emit("private_mode_changed", isPrivateMode)
|
||||
}
|
||||
|
||||
func (manager *SessionManagerCtx) PrivateMode() bool {
|
||||
manager.privateModeMu.Lock()
|
||||
defer manager.privateModeMu.Unlock()
|
||||
|
||||
return manager.privateMode
|
||||
}
|
||||
|
||||
// ---
|
||||
// cursors
|
||||
// ---
|
||||
@ -326,6 +362,12 @@ func (manager *SessionManagerCtx) OnHostChanged(listener func(session types.Sess
|
||||
})
|
||||
}
|
||||
|
||||
func (manager *SessionManagerCtx) OnPrivateModeChanged(listener func(isPrivateMode bool)) {
|
||||
manager.emmiter.On("private_mode_changed", func(payload ...interface{}) {
|
||||
listener(payload[0].(bool))
|
||||
})
|
||||
}
|
||||
|
||||
// ---
|
||||
// config
|
||||
// ---
|
||||
|
@ -44,6 +44,11 @@ func (session *SessionCtx) profileChanged() {
|
||||
if (!session.profile.CanConnect || !session.profile.CanLogin) && session.state.IsConnected {
|
||||
session.GetWebSocketPeer().Destroy("profile changed")
|
||||
}
|
||||
|
||||
// update webrtc paused state
|
||||
if webrtcPeer := session.GetWebRTCPeer(); webrtcPeer != nil {
|
||||
webrtcPeer.SetPaused(session.PrivateModeEnabled())
|
||||
}
|
||||
}
|
||||
|
||||
func (session *SessionCtx) State() types.SessionState {
|
||||
@ -54,6 +59,10 @@ func (session *SessionCtx) IsHost() bool {
|
||||
return session.manager.GetHost() == session
|
||||
}
|
||||
|
||||
func (session *SessionCtx) PrivateModeEnabled() bool {
|
||||
return session.manager.PrivateMode() && !session.profile.IsAdmin
|
||||
}
|
||||
|
||||
func (session *SessionCtx) SetCursor(cursor types.Cursor) {
|
||||
if session.manager.InactiveCursors() && session.profile.SendsInactiveCursor {
|
||||
session.manager.SetCursor(cursor, session)
|
||||
|
@ -200,6 +200,12 @@ func (manager *WebRTCManagerCtx) CreatePeer(session types.Session, videoID strin
|
||||
|
||||
return videoTrack.SetStream(videoStream)
|
||||
},
|
||||
setPaused: func(isPaused bool) {
|
||||
videoTrack.SetPaused(isPaused)
|
||||
audioTrack.SetPaused(isPaused)
|
||||
|
||||
// TODO: Send fresh cursor position & image when unpausing.
|
||||
},
|
||||
iceTrickle: manager.config.ICETrickle,
|
||||
}
|
||||
|
||||
@ -314,12 +320,22 @@ func (manager *WebRTCManagerCtx) CreatePeer(session types.Session, videoID strin
|
||||
})
|
||||
|
||||
cursorImage := func(entry *cursor.ImageEntry) {
|
||||
// TODO: Refactor.
|
||||
if videoTrack.paused {
|
||||
return
|
||||
}
|
||||
|
||||
if err := peer.SendCursorImage(entry.Cursor, entry.Image); err != nil {
|
||||
logger.Err(err).Msg("could not send cursor image")
|
||||
}
|
||||
}
|
||||
|
||||
cursorPosition := func(x, y int) {
|
||||
// TODO: Refactor.
|
||||
if videoTrack.paused {
|
||||
return
|
||||
}
|
||||
|
||||
if session.IsHost() {
|
||||
return
|
||||
}
|
||||
@ -352,6 +368,11 @@ func (manager *WebRTCManagerCtx) CreatePeer(session types.Session, videoID strin
|
||||
})
|
||||
|
||||
dataChannel.OnMessage(func(message webrtc.DataChannelMessage) {
|
||||
// TODO: Refactor.
|
||||
if videoTrack.paused {
|
||||
return
|
||||
}
|
||||
|
||||
if err := manager.handle(message.Data, session); err != nil {
|
||||
logger.Err(err).Msg("data handle failed")
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ type WebRTCPeerCtx struct {
|
||||
connection *webrtc.PeerConnection
|
||||
dataChannel *webrtc.DataChannel
|
||||
changeVideo func(videoID string) error
|
||||
setPaused func(isPaused bool)
|
||||
iceTrickle bool
|
||||
}
|
||||
|
||||
@ -122,6 +123,19 @@ func (peer *WebRTCPeerCtx) SetVideoID(videoID string) error {
|
||||
return peer.changeVideo(videoID)
|
||||
}
|
||||
|
||||
func (peer *WebRTCPeerCtx) SetPaused(isPaused bool) error {
|
||||
peer.mu.Lock()
|
||||
defer peer.mu.Unlock()
|
||||
|
||||
if peer.connection == nil {
|
||||
return types.ErrWebRTCConnectionNotFound
|
||||
}
|
||||
|
||||
peer.logger.Info().Bool("is_paused", isPaused).Msg("set paused")
|
||||
peer.setPaused(isPaused)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (peer *WebRTCPeerCtx) Destroy() {
|
||||
peer.mu.Lock()
|
||||
defer peer.mu.Unlock()
|
||||
|
@ -26,12 +26,17 @@ func (manager *WebRTCManagerCtx) newPeerStreamTrack(stream types.StreamSinkManag
|
||||
peer := &PeerStreamTrack{
|
||||
logger: logger,
|
||||
track: track,
|
||||
listener: func(sample types.Sample) {
|
||||
err := track.WriteSample(media.Sample(sample))
|
||||
if err != nil && errors.Is(err, io.ErrClosedPipe) {
|
||||
logger.Warn().Err(err).Msg("pipeline failed to write")
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
peer.listener = func(sample types.Sample) {
|
||||
if peer.paused {
|
||||
return
|
||||
}
|
||||
|
||||
err := track.WriteSample(media.Sample(sample))
|
||||
if err != nil && errors.Is(err, io.ErrClosedPipe) {
|
||||
logger.Warn().Err(err).Msg("pipeline failed to write")
|
||||
}
|
||||
}
|
||||
|
||||
err = peer.SetStream(stream)
|
||||
@ -41,6 +46,7 @@ func (manager *WebRTCManagerCtx) newPeerStreamTrack(stream types.StreamSinkManag
|
||||
type PeerStreamTrack struct {
|
||||
logger zerolog.Logger
|
||||
track *webrtc.TrackLocalStaticSample
|
||||
paused bool
|
||||
listener func(sample types.Sample)
|
||||
|
||||
stream types.StreamSinkManager
|
||||
@ -92,3 +98,7 @@ func (peer *PeerStreamTrack) AddToConnection(connection *webrtc.PeerConnection)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (peer *PeerStreamTrack) SetPaused(paused bool) {
|
||||
peer.paused = paused
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ var (
|
||||
)
|
||||
|
||||
func (h *MessageHandlerCtx) controlRelease(session types.Session) error {
|
||||
if !session.Profile().CanHost {
|
||||
if !session.Profile().CanHost || session.PrivateModeEnabled() {
|
||||
return ErrIsNotAllowedToHost
|
||||
}
|
||||
|
||||
@ -32,7 +32,7 @@ func (h *MessageHandlerCtx) controlRelease(session types.Session) error {
|
||||
}
|
||||
|
||||
func (h *MessageHandlerCtx) controlRequest(session types.Session) error {
|
||||
if !session.Profile().CanHost {
|
||||
if !session.Profile().CanHost || session.PrivateModeEnabled() {
|
||||
return ErrIsNotAllowedToHost
|
||||
}
|
||||
|
||||
|
@ -24,6 +24,11 @@ func (h *MessageHandlerCtx) signalRequest(session types.Session, payload *messag
|
||||
return err
|
||||
}
|
||||
|
||||
// set webrtc as paused if session has private mode enabled
|
||||
if webrtcPeer := session.GetWebRTCPeer(); webrtcPeer != nil && session.PrivateModeEnabled() {
|
||||
webrtcPeer.SetPaused(true)
|
||||
}
|
||||
|
||||
session.Send(
|
||||
event.SIGNAL_PROVIDE,
|
||||
message.SignalProvide{
|
||||
|
@ -54,6 +54,10 @@ func CanHostOnly(w http.ResponseWriter, r *http.Request) (context.Context, error
|
||||
return nil, utils.HttpForbidden("session cannot host")
|
||||
}
|
||||
|
||||
if session.PrivateModeEnabled() {
|
||||
return nil, utils.HttpUnprocessableEntity("private mode is enabled")
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
|
@ -27,6 +27,7 @@ type Session interface {
|
||||
Profile() MemberProfile
|
||||
State() SessionState
|
||||
IsHost() bool
|
||||
PrivateModeEnabled() bool
|
||||
|
||||
// cursor
|
||||
SetCursor(cursor Cursor)
|
||||
@ -55,6 +56,9 @@ type SessionManager interface {
|
||||
GetHost() Session
|
||||
ClearHost()
|
||||
|
||||
SetPrivateMode(isPrivateMode bool)
|
||||
PrivateMode() bool
|
||||
|
||||
SetCursor(cursor Cursor, session Session)
|
||||
PopCursors() map[Session][]Cursor
|
||||
|
||||
@ -69,6 +73,7 @@ type SessionManager interface {
|
||||
OnProfileChanged(listener func(session Session))
|
||||
OnStateChanged(listener func(session Session))
|
||||
OnHostChanged(listener func(session Session))
|
||||
OnPrivateModeChanged(listener func(isPrivateMode bool))
|
||||
|
||||
ImplicitHosting() bool
|
||||
InactiveCursors() bool
|
||||
|
@ -26,6 +26,8 @@ type WebRTCPeer interface {
|
||||
SetCandidate(candidate webrtc.ICECandidateInit) error
|
||||
|
||||
SetVideoID(videoID string) error
|
||||
SetPaused(isPaused bool) error
|
||||
|
||||
SendCursorPosition(x, y int) error
|
||||
SendCursorImage(cur *CursorImage, img []byte) error
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user