mirror of
https://github.com/m1k1o/neko.git
synced 2025-05-19 12:07:06 +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.Get("/whoami", api.Whoami)
|
||||
r.Post("/profile", api.UpdateProfile)
|
||||
r.Get("/stats", api.Stats)
|
||||
|
||||
sessionsHandler := sessions.New(api.sessions)
|
||||
r.Route("/sessions", sessionsHandler.Route)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
@ -43,7 +41,6 @@ var (
|
|||
type LegacyHandler struct {
|
||||
logger zerolog.Logger
|
||||
serverAddr string
|
||||
startedAt time.Time
|
||||
}
|
||||
|
||||
func New() *LegacyHandler {
|
||||
|
@ -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(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"errors"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/kataras/go-events"
|
||||
"github.com/rs/zerolog"
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
@ -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
|
||||
#
|
||||
|
|
|
@ -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
|
||||
|
@ -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)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue