mirror of
https://github.com/m1k1o/neko.git
synced 2025-05-18 19:47:07 +02:00
implement session stats, fixes last (admin|user) left at won't work for any provider that removed users after logout.
This commit is contained in:
parent
356a566bc6
commit
4f401ac2b6
7 changed files with 124 additions and 21 deletions
|
@ -47,6 +47,7 @@ func (api *ApiManagerCtx) Route(r types.Router) {
|
||||||
r.Post("/logout", api.Logout)
|
r.Post("/logout", api.Logout)
|
||||||
r.Get("/whoami", api.Whoami)
|
r.Get("/whoami", api.Whoami)
|
||||||
r.Post("/profile", api.UpdateProfile)
|
r.Post("/profile", api.UpdateProfile)
|
||||||
|
r.Get("/stats", api.Stats)
|
||||||
|
|
||||||
sessionsHandler := sessions.New(api.sessions)
|
sessionsHandler := sessions.New(api.sessions)
|
||||||
r.Route("/sessions", sessionsHandler.Route)
|
r.Route("/sessions", sessionsHandler.Route)
|
||||||
|
|
|
@ -103,3 +103,8 @@ func (api *ApiManagerCtx) UpdateProfile(w http.ResponseWriter, r *http.Request)
|
||||||
|
|
||||||
return utils.HttpSuccess(w, true)
|
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)
|
||||||
|
}
|
||||||
|
|
|
@ -7,10 +7,8 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"time"
|
|
||||||
|
|
||||||
"m1k1o/neko/internal/api"
|
"m1k1o/neko/internal/api"
|
||||||
"m1k1o/neko/internal/api/room"
|
|
||||||
oldEvent "m1k1o/neko/internal/http/legacy/event"
|
oldEvent "m1k1o/neko/internal/http/legacy/event"
|
||||||
oldMessage "m1k1o/neko/internal/http/legacy/message"
|
oldMessage "m1k1o/neko/internal/http/legacy/message"
|
||||||
oldTypes "m1k1o/neko/internal/http/legacy/types"
|
oldTypes "m1k1o/neko/internal/http/legacy/types"
|
||||||
|
@ -43,7 +41,6 @@ var (
|
||||||
type LegacyHandler struct {
|
type LegacyHandler struct {
|
||||||
logger zerolog.Logger
|
logger zerolog.Logger
|
||||||
serverAddr string
|
serverAddr string
|
||||||
startedAt time.Time
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func New() *LegacyHandler {
|
func New() *LegacyHandler {
|
||||||
|
@ -52,7 +49,6 @@ func New() *LegacyHandler {
|
||||||
return &LegacyHandler{
|
return &LegacyHandler{
|
||||||
logger: log.With().Str("module", "legacy").Logger(),
|
logger: log.With().Str("module", "legacy").Logger(),
|
||||||
serverAddr: "127.0.0.1:8080",
|
serverAddr: "127.0.0.1:8080",
|
||||||
startedAt: time.Now(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -208,9 +204,9 @@ func (h *LegacyHandler) Route(r types.Router) {
|
||||||
return utils.HttpInternalServerError().WithInternalErr(err)
|
return utils.HttpInternalServerError().WithInternalErr(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// get current control status
|
// get stats
|
||||||
control := room.ControlStatusPayload{}
|
newStats := types.Stats{}
|
||||||
err = s.apiReq(http.MethodGet, "/api/room/control", nil, &control)
|
err = s.apiReq(http.MethodGet, "/api/stats", nil, &newStats)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return utils.HttpInternalServerError().WithInternalErr(err)
|
return utils.HttpInternalServerError().WithInternalErr(err)
|
||||||
}
|
}
|
||||||
|
@ -236,18 +232,6 @@ func (h *LegacyHandler) Route(r types.Router) {
|
||||||
}
|
}
|
||||||
// append members
|
// append members
|
||||||
stats.Members = append(stats.Members, member)
|
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -256,10 +240,12 @@ func (h *LegacyHandler) Route(r types.Router) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
stats.Host = control.HostId
|
stats.Host = newStats.HostId
|
||||||
// TODO: stats.Banned, not implemented yet
|
// TODO: stats.Banned, not implemented yet
|
||||||
stats.Locked = locks
|
stats.Locked = locks
|
||||||
stats.ServerStartedAt = h.startedAt
|
stats.ServerStartedAt = newStats.ServerStartedAt
|
||||||
|
stats.LastAdminLeftAt = newStats.LastAdminLeftAt
|
||||||
|
stats.LastUserLeftAt = newStats.LastUserLeftAt
|
||||||
stats.ControlProtection = settings.ControlProtection
|
stats.ControlProtection = settings.ControlProtection
|
||||||
stats.ImplicitControl = settings.ImplicitHosting
|
stats.ImplicitControl = settings.ImplicitHosting
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/kataras/go-events"
|
"github.com/kataras/go-events"
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
|
@ -31,6 +32,8 @@ func New(config *config.Session) *SessionManagerCtx {
|
||||||
sessions: make(map[string]*SessionCtx),
|
sessions: make(map[string]*SessionCtx),
|
||||||
cursors: make(map[types.Session][]types.Cursor),
|
cursors: make(map[types.Session][]types.Cursor),
|
||||||
emmiter: events.New(),
|
emmiter: events.New(),
|
||||||
|
|
||||||
|
serverStartedAt: time.Now(),
|
||||||
}
|
}
|
||||||
|
|
||||||
// create API session
|
// create API session
|
||||||
|
@ -76,6 +79,12 @@ type SessionManagerCtx struct {
|
||||||
|
|
||||||
emmiter events.EventEmmiter
|
emmiter events.EventEmmiter
|
||||||
apiSession *SessionCtx
|
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) {
|
func (manager *SessionManagerCtx) Create(id string, profile types.MemberProfile) (types.Session, string, error) {
|
||||||
|
@ -468,3 +477,36 @@ func (manager *SessionManagerCtx) Settings() types.Settings {
|
||||||
func (manager *SessionManagerCtx) CookieEnabled() bool {
|
func (manager *SessionManagerCtx) CookieEnabled() bool {
|
||||||
return manager.config.CookieEnabled
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -121,6 +121,14 @@ func (session *SessionCtx) ConnectWebSocketPeer(websocketPeer types.WebSocketPee
|
||||||
session.state.ConnectedSince = &now
|
session.state.ConnectedSince = &now
|
||||||
session.state.NotConnectedSince = nil
|
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)
|
session.manager.emmiter.Emit("connected", session)
|
||||||
|
|
||||||
// if there is a previous peer, destroy it
|
// if there is a previous peer, destroy it
|
||||||
|
@ -180,6 +188,16 @@ func (session *SessionCtx) DisconnectWebSocketPeer(websocketPeer types.WebSocket
|
||||||
session.state.ConnectedSince = nil
|
session.state.ConnectedSince = nil
|
||||||
session.state.NotConnectedSince = &now
|
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.manager.emmiter.Emit("disconnected", session)
|
||||||
|
|
||||||
session.websocketMu.Lock()
|
session.websocketMu.Lock()
|
||||||
|
|
|
@ -125,6 +125,21 @@ paths:
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/components/schemas/MemberProfile'
|
$ref: '#/components/schemas/MemberProfile'
|
||||||
required: true
|
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
|
# sessions
|
||||||
|
@ -1145,6 +1160,30 @@ components:
|
||||||
is_watching:
|
is_watching:
|
||||||
type: boolean
|
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
|
# room
|
||||||
#
|
#
|
||||||
|
|
|
@ -52,6 +52,16 @@ type Settings struct {
|
||||||
Plugins PluginSettings `json:"plugins"`
|
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 {
|
type Session interface {
|
||||||
ID() string
|
ID() string
|
||||||
Profile() MemberProfile
|
Profile() MemberProfile
|
||||||
|
@ -110,6 +120,8 @@ type SessionManager interface {
|
||||||
Settings() Settings
|
Settings() Settings
|
||||||
CookieEnabled() bool
|
CookieEnabled() bool
|
||||||
|
|
||||||
|
Stats() Stats
|
||||||
|
|
||||||
CookieSetToken(w http.ResponseWriter, token string)
|
CookieSetToken(w http.ResponseWriter, token string)
|
||||||
CookieClearToken(w http.ResponseWriter, r *http.Request)
|
CookieClearToken(w http.ResponseWriter, r *http.Request)
|
||||||
Authenticate(r *http.Request) (Session, error)
|
Authenticate(r *http.Request) (Session, error)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue