legacy: add stats.

This commit is contained in:
Miroslav Šedivý 2024-09-06 20:48:40 +02:00
parent 6a8f8052cd
commit a1f2e379cd
4 changed files with 158 additions and 55 deletions

View file

@ -1,12 +1,17 @@
package legacy
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"time"
"m1k1o/neko/internal/api"
"m1k1o/neko/internal/api/room"
oldEvent "m1k1o/neko/internal/http/legacy/event"
oldMessage "m1k1o/neko/internal/http/legacy/message"
oldTypes "m1k1o/neko/internal/http/legacy/types"
"m1k1o/neko/pkg/types"
"m1k1o/neko/pkg/types/event"
@ -36,6 +41,7 @@ var (
type LegacyHandler struct {
logger zerolog.Logger
serverAddr string
startedAt time.Time
}
func New() *LegacyHandler {
@ -44,6 +50,7 @@ func New() *LegacyHandler {
return &LegacyHandler{
logger: log.With().Str("module", "legacy").Logger(),
serverAddr: "127.0.0.1:8080",
startedAt: time.Now(),
}
}
@ -64,7 +71,7 @@ func (h *LegacyHandler) Route(r types.Router) {
// create a new session
username := r.URL.Query().Get("username")
password := r.URL.Query().Get("password")
token, err := s.create(username, password)
err = s.create(username, password)
if err != nil {
h.logger.Error().Err(err).Msg("couldn't create a new session")
@ -80,7 +87,7 @@ func (h *LegacyHandler) Route(r types.Router) {
defer s.destroy()
// dial to the remote backend
connBackend, _, err := DefaultDialer.Dial("ws://"+h.serverAddr+"/api/ws?token="+token, nil)
connBackend, _, err := DefaultDialer.Dial("ws://"+h.serverAddr+"/api/ws?token="+s.token, nil)
if err != nil {
h.logger.Error().Err(err).Msg("couldn't dial to the remote backend")
@ -174,24 +181,90 @@ func (h *LegacyHandler) Route(r types.Router) {
return nil
})
/*
r.Get("/stats", func(w http.ResponseWriter, r *http.Request) error {
password := r.URL.Query().Get("pwd")
isAdmin, err := webSocketHandler.IsAdmin(password)
if err != nil {
return utils.HttpForbidden(err)
}
s := newSession(h.logger, h.serverAddr)
if !isAdmin {
// create a new session
username := r.URL.Query().Get("usr")
password := r.URL.Query().Get("pwd")
err := s.create(username, password)
if err != nil {
return utils.HttpForbidden(err.Error())
}
defer s.destroy()
if !s.isAdmin {
return utils.HttpUnauthorized().Msg("bad authorization")
}
w.Header().Set("Content-Type", "application/json")
stats := webSocketHandler.Stats()
// get all sessions
sessions := []api.SessionDataPayload{}
err = s.apiReq(http.MethodGet, "/api/sessions", nil, &sessions)
if err != nil {
return utils.HttpInternalServerError().WithInternalErr(err)
}
// get current control status
control := room.ControlStatusPayload{}
err = s.apiReq(http.MethodGet, "/api/room/control", nil, &control)
if err != nil {
return utils.HttpInternalServerError().WithInternalErr(err)
}
// get settings
settings := types.Settings{}
err = s.apiReq(http.MethodGet, "/api/room/settings", nil, &settings)
if err != nil {
return utils.HttpInternalServerError().WithInternalErr(err)
}
var stats oldTypes.Stats
// create empty array so that it's not null in json
stats.Members = []*oldTypes.Member{}
for _, session := range sessions {
if session.State.IsConnected {
stats.Connections++
member, err := profileToMember(session.ID, session.Profile)
if err != nil {
return utils.HttpInternalServerError().WithInternalErr(err)
}
// append members
stats.Members = append(stats.Members, member)
} else if session.State.NotConnectedSince != nil {
//
// TODO: This wont work if the user is removed after the session is closed
//
// populate last admin left time
if session.Profile.IsAdmin && (stats.LastAdminLeftAt == nil || (*session.State.NotConnectedSince).After(*stats.LastAdminLeftAt)) {
stats.LastAdminLeftAt = session.State.NotConnectedSince
}
// populate last user left time
if !session.Profile.IsAdmin && (stats.LastUserLeftAt == nil || (*session.State.NotConnectedSince).After(*stats.LastUserLeftAt)) {
stats.LastUserLeftAt = session.State.NotConnectedSince
}
}
}
locks, err := s.settingsToLocks(settings)
if err != nil {
return err
}
stats.Host = control.HostId
// TODO: stats.Banned, not implemented yet
stats.Locked = locks
stats.ServerStartedAt = h.startedAt
stats.ControlProtection = settings.ControlProtection
stats.ImplicitControl = settings.ImplicitHosting
return json.NewEncoder(w).Encode(stats)
})
/*
r.Get("/screenshot.jpg", func(w http.ResponseWriter, r *http.Request) error {
password := r.URL.Query().Get("pwd")
isAdmin, err := webSocketHandler.IsAdmin(password)

View file

@ -35,6 +35,7 @@ type session struct {
id string
token string
name string
isAdmin bool
client *http.Client
lastHostID string
@ -142,7 +143,7 @@ func (s *session) toBackend(event string, payload any) error {
return nil
}
func (s *session) create(username, password string) (string, error) {
func (s *session) create(username, password string) error {
data := api.SessionDataPayload{}
err := s.apiReq(http.MethodPost, "/api/login", api.SessionLoginPayload{
@ -150,19 +151,20 @@ func (s *session) create(username, password string) (string, error) {
Password: password,
}, &data)
if err != nil {
return "", err
return err
}
s.id = data.ID
s.token = data.Token
s.name = data.Profile.Name
s.isAdmin = data.Profile.IsAdmin
// if Cookie auth, the token will be empty
if s.token == "" {
return "", fmt.Errorf("token not found - make sure you are not using Cookie auth on the server")
return fmt.Errorf("token not found - make sure you are not using Cookie auth on the server")
}
return data.Token, nil
return nil
}
func (s *session) destroy() {

View file

@ -1,5 +1,23 @@
package types
import "time"
type Stats struct {
Connections uint32 `json:"connections"`
Host string `json:"host"`
Members []*Member `json:"members"`
Banned map[string]string `json:"banned"` // IP -> session ID (that banned it)
Locked map[string]string `json:"locked"` // resource name -> session ID (that locked it)
ServerStartedAt time.Time `json:"server_started_at"`
LastAdminLeftAt *time.Time `json:"last_admin_left_at"`
LastUserLeftAt *time.Time `json:"last_user_left_at"`
ControlProtection bool `json:"control_protection"`
ImplicitControl bool `json:"implicit_control"`
}
type Member struct {
ID string `json:"id"`
Name string `json:"displayname"`

View file

@ -67,6 +67,41 @@ func screenConfigurations(screenSizes []types.ScreenSize) map[int]oldTypes.Scree
return screenSizesList
}
func (s *session) settingsToLocks(settings types.Settings) (map[string]string, error) {
//
// FileTransfer
//
filetransferSettings := filetransfer.Settings{
Enabled: true, // defaults to true
}
err := settings.Plugins.Unmarshal(filetransfer.PluginName, &filetransferSettings)
if err != nil && !errors.Is(err, types.ErrPluginSettingsNotFound) {
return nil, fmt.Errorf("unable to unmarshal %s plugin settings from global settings: %w", filetransfer.PluginName, err)
}
//
// Locks
//
locks := map[string]string{}
if settings.LockedLogins {
locks["login"] = "" // TODO: We don't know who locked the login.
s.lockedLogins = true
}
if settings.LockedControls {
locks["control"] = "" // TODO: We don't know who locked the control.
s.lockedControls = true
}
if !filetransferSettings.Enabled {
locks["file_transfer"] = "" // TODO: We don't know who locked the file transfer.
s.lockedFileTransfer = true
}
return locks, nil
}
func (s *session) sendControlHost(request message.ControlHost) error {
lastHostID := s.lastHostID
@ -198,34 +233,9 @@ func (s *session) wsToClient(msg []byte) error {
return err
}
//
// FileTransfer
//
filetransferSettings := filetransfer.Settings{
Enabled: true, // defaults to true
}
err = request.Settings.Plugins.Unmarshal(filetransfer.PluginName, &filetransferSettings)
if err != nil && !errors.Is(err, types.ErrPluginSettingsNotFound) {
return fmt.Errorf("unable to unmarshal %s plugin settings from global settings: %w", filetransfer.PluginName, err)
}
//
// Locks
//
locks := map[string]string{}
if request.Settings.LockedLogins {
locks["login"] = "" // TODO: We don't know who locked the login.
s.lockedLogins = true
}
if request.Settings.LockedControls {
locks["control"] = "" // TODO: We don't know who locked the control.
s.lockedControls = true
}
if !filetransferSettings.Enabled {
locks["file_transfer"] = "" // TODO: We don't know who locked the file transfer.
s.lockedFileTransfer = true
locks, err := s.settingsToLocks(request.Settings)
if err != nil {
return err
}
return s.toClient(&oldMessage.SystemInit{