Skip to content

Commit

Permalink
implement session stats, fixes last (admin|user) left at won't work f…
Browse files Browse the repository at this point in the history
…or any provider that removed users after logout.
  • Loading branch information
m1k1o committed Sep 7, 2024
1 parent 356a566 commit 4f401ac
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 21 deletions.
1 change: 1 addition & 0 deletions server/internal/api/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ func (api *ApiManagerCtx) Route(r types.Router) {
r.Post("/logout", api.Logout)
r.Get("/whoami", api.Whoami)
r.Post("/profile", api.UpdateProfile)
r.Get("/stats", api.Stats)

sessionsHandler := sessions.New(api.sessions)
r.Route("/sessions", sessionsHandler.Route)
Expand Down
5 changes: 5 additions & 0 deletions server/internal/api/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,3 +103,8 @@ func (api *ApiManagerCtx) UpdateProfile(w http.ResponseWriter, r *http.Request)

return utils.HttpSuccess(w, true)
}

func (api *ApiManagerCtx) Stats(w http.ResponseWriter, r *http.Request) error {
stats := api.sessions.Stats()
return utils.HttpSuccess(w, stats)
}
28 changes: 7 additions & 21 deletions server/internal/http/legacy/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,8 @@ import (
"io"
"net/http"
"net/url"
"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"
Expand Down Expand Up @@ -43,7 +41,6 @@ var (
type LegacyHandler struct {
logger zerolog.Logger
serverAddr string
startedAt time.Time
}

func New() *LegacyHandler {
Expand All @@ -52,7 +49,6 @@ func New() *LegacyHandler {
return &LegacyHandler{
logger: log.With().Str("module", "legacy").Logger(),
serverAddr: "127.0.0.1:8080",
startedAt: time.Now(),
}
}

Expand Down Expand Up @@ -208,9 +204,9 @@ func (h *LegacyHandler) Route(r types.Router) {
return utils.HttpInternalServerError().WithInternalErr(err)
}

// get current control status
control := room.ControlStatusPayload{}
err = s.apiReq(http.MethodGet, "/api/room/control", nil, &control)
// get stats
newStats := types.Stats{}
err = s.apiReq(http.MethodGet, "/api/stats", nil, &newStats)
if err != nil {
return utils.HttpInternalServerError().WithInternalErr(err)
}
Expand All @@ -236,18 +232,6 @@ func (h *LegacyHandler) Route(r types.Router) {
}
// 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
}
}
}

Expand All @@ -256,10 +240,12 @@ func (h *LegacyHandler) Route(r types.Router) {
return err
}

stats.Host = control.HostId
stats.Host = newStats.HostId
// TODO: stats.Banned, not implemented yet
stats.Locked = locks
stats.ServerStartedAt = h.startedAt
stats.ServerStartedAt = newStats.ServerStartedAt
stats.LastAdminLeftAt = newStats.LastAdminLeftAt
stats.LastUserLeftAt = newStats.LastUserLeftAt
stats.ControlProtection = settings.ControlProtection
stats.ImplicitControl = settings.ImplicitHosting

Expand Down
42 changes: 42 additions & 0 deletions server/internal/session/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"errors"
"sync"
"sync/atomic"
"time"

"github.com/kataras/go-events"
"github.com/rs/zerolog"
Expand Down Expand Up @@ -31,6 +32,8 @@ func New(config *config.Session) *SessionManagerCtx {
sessions: make(map[string]*SessionCtx),
cursors: make(map[types.Session][]types.Cursor),
emmiter: events.New(),

serverStartedAt: time.Now(),
}

// create API session
Expand Down Expand Up @@ -76,6 +79,12 @@ type SessionManagerCtx struct {

emmiter events.EventEmmiter
apiSession *SessionCtx

serverStartedAt time.Time
totalAdmins atomic.Int32
lastAdminLeftAt atomic.Value
totalUsers atomic.Int32
lastUserLeftAt atomic.Value
}

func (manager *SessionManagerCtx) Create(id string, profile types.MemberProfile) (types.Session, string, error) {
Expand Down Expand Up @@ -468,3 +477,36 @@ func (manager *SessionManagerCtx) Settings() types.Settings {
func (manager *SessionManagerCtx) CookieEnabled() bool {
return manager.config.CookieEnabled
}

// ---
// stats
// ---

func (manager *SessionManagerCtx) Stats() types.Stats {
hostId := ""

host, hasHost := manager.GetHost()
if hasHost {
hostId = host.ID()
}

var lastUserLeftAt *time.Time
if t, ok := manager.lastUserLeftAt.Load().(*time.Time); ok {
lastUserLeftAt = t
}

var lastAdminLeftAt *time.Time
if t, ok := manager.lastAdminLeftAt.Load().(*time.Time); ok {
lastAdminLeftAt = t
}

return types.Stats{
HasHost: hasHost,
HostId: hostId,
ServerStartedAt: manager.serverStartedAt,
TotalUsers: int(manager.totalUsers.Load()),
LastUserLeftAt: lastUserLeftAt,
TotalAdmins: int(manager.totalAdmins.Load()),
LastAdminLeftAt: lastAdminLeftAt,
}
}
18 changes: 18 additions & 0 deletions server/internal/session/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,14 @@ func (session *SessionCtx) ConnectWebSocketPeer(websocketPeer types.WebSocketPee
session.state.ConnectedSince = &now
session.state.NotConnectedSince = nil

if session.profile.IsAdmin {
session.manager.totalAdmins.Add(1)
session.manager.lastAdminLeftAt.Store((*time.Time)(nil))
} else {
session.manager.totalUsers.Add(1)
session.manager.lastUserLeftAt.Store((*time.Time)(nil))
}

session.manager.emmiter.Emit("connected", session)

// if there is a previous peer, destroy it
Expand Down Expand Up @@ -180,6 +188,16 @@ func (session *SessionCtx) DisconnectWebSocketPeer(websocketPeer types.WebSocket
session.state.ConnectedSince = nil
session.state.NotConnectedSince = &now

if session.profile.IsAdmin {
if session.manager.totalAdmins.Add(-1) == 0 {
session.manager.lastAdminLeftAt.Store(&now)
}
} else {
if session.manager.totalUsers.Add(-1) == 0 {
session.manager.lastUserLeftAt.Store(&now)
}
}

session.manager.emmiter.Emit("disconnected", session)

session.websocketMu.Lock()
Expand Down
39 changes: 39 additions & 0 deletions server/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,21 @@ paths:
schema:
$ref: '#/components/schemas/MemberProfile'
required: true
/api/stats:
get:
summary: stats
operationId: stats
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/Stats'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'

#
# sessions
Expand Down Expand Up @@ -1145,6 +1160,30 @@ components:
is_watching:
type: boolean

Stats:
type: object
properties:
has_host:
type: boolean
host_id:
type: string
optional: true
server_started_at:
type: string
format: date-time
total_users:
type: integer
last_user_left_at:
type: string
format: date-time
optional: true
total_admins:
type: integer
last_admin_left_at:
type: string
format: date-time
optional: true

#
# room
#
Expand Down
12 changes: 12 additions & 0 deletions server/pkg/types/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,16 @@ type Settings struct {
Plugins PluginSettings `json:"plugins"`
}

type Stats struct {
HasHost bool `json:"has_host"`
HostId string `json:"host_id,omitempty"`
ServerStartedAt time.Time `json:"server_started_at"`
TotalUsers int `json:"total_users"`
LastUserLeftAt *time.Time `json:"last_user_left_at,omitempty"`
TotalAdmins int `json:"total_admins"`
LastAdminLeftAt *time.Time `json:"last_admin_left_at,omitempty"`
}

type Session interface {
ID() string
Profile() MemberProfile
Expand Down Expand Up @@ -110,6 +120,8 @@ type SessionManager interface {
Settings() Settings
CookieEnabled() bool

Stats() Stats

CookieSetToken(w http.ResponseWriter, token string)
CookieClearToken(w http.ResponseWriter, r *http.Request)
Authenticate(r *http.Request) (Session, error)
Expand Down

0 comments on commit 4f401ac

Please sign in to comment.