This repository has been archived on 2026-04-03. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
core/server/store/sqlite/sessions.go
2026-03-10 13:38:01 +00:00

120 lines
3.6 KiB
Go

package sqlite
import (
"context"
"database/sql"
"time"
"git.gobha.me/xcaliber/chat-switchboard/models"
"git.gobha.me/xcaliber/chat-switchboard/store"
)
// ── SessionStore ───────────────────────────
type SessionStore struct{}
func NewSessionStore() *SessionStore { return &SessionStore{} }
func (s *SessionStore) Create(ctx context.Context, sp *models.SessionParticipant) error {
if sp.ID == "" {
sp.ID = store.NewID()
}
_, err := DB.ExecContext(ctx, `
INSERT INTO session_participants (id, session_token, channel_id, display_name, fingerprint)
VALUES (?, ?, ?, ?, ?)`,
sp.ID, sp.SessionToken, sp.ChannelID, sp.DisplayName, nullText(sp.Fingerprint),
)
if err != nil {
return err
}
return DB.QueryRowContext(ctx, `SELECT created_at FROM session_participants WHERE id = ?`, sp.ID).
Scan(&sp.CreatedAt)
}
func (s *SessionStore) GetByToken(ctx context.Context, token string) (*models.SessionParticipant, error) {
sp := &models.SessionParticipant{}
err := DB.QueryRowContext(ctx, `
SELECT id, session_token, channel_id, display_name, COALESCE(fingerprint, ''), created_at
FROM session_participants WHERE session_token = ?`, token,
).Scan(&sp.ID, &sp.SessionToken, &sp.ChannelID, &sp.DisplayName, &sp.Fingerprint, &sp.CreatedAt)
if err != nil {
return nil, err
}
return sp, nil
}
func (s *SessionStore) GetByID(ctx context.Context, id string) (*models.SessionParticipant, error) {
sp := &models.SessionParticipant{}
err := DB.QueryRowContext(ctx, `
SELECT id, session_token, channel_id, display_name, COALESCE(fingerprint, ''), created_at
FROM session_participants WHERE id = ?`, id,
).Scan(&sp.ID, &sp.SessionToken, &sp.ChannelID, &sp.DisplayName, &sp.Fingerprint, &sp.CreatedAt)
if err != nil {
return nil, err
}
return sp, nil
}
func (s *SessionStore) ListForChannel(ctx context.Context, channelID string) ([]models.SessionParticipant, error) {
rows, err := DB.QueryContext(ctx, `
SELECT id, session_token, channel_id, display_name, COALESCE(fingerprint, ''), created_at
FROM session_participants WHERE channel_id = ?
ORDER BY created_at ASC`, channelID)
if err != nil {
return nil, err
}
defer rows.Close()
var result []models.SessionParticipant
for rows.Next() {
var sp models.SessionParticipant
if err := rows.Scan(&sp.ID, &sp.SessionToken, &sp.ChannelID, &sp.DisplayName, &sp.Fingerprint, &sp.CreatedAt); err != nil {
return nil, err
}
result = append(result, sp)
}
return result, rows.Err()
}
func (s *SessionStore) CountForChannel(ctx context.Context, channelID string) (int, error) {
var count int
err := DB.QueryRowContext(ctx, `
SELECT COUNT(*) FROM session_participants WHERE channel_id = ?`, channelID,
).Scan(&count)
return count, err
}
func (s *SessionStore) Delete(ctx context.Context, id string) error {
res, err := DB.ExecContext(ctx, `DELETE FROM session_participants WHERE id = ?`, id)
if err != nil {
return err
}
n, _ := res.RowsAffected()
if n == 0 {
return sql.ErrNoRows
}
return nil
}
// DeleteExpired removes sessions created before olderThan whose channel
// has no messages. Returns the count of deleted rows.
func (s *SessionStore) DeleteExpired(ctx context.Context, olderThan time.Time) (int64, error) {
res, err := DB.ExecContext(ctx, `
DELETE FROM session_participants
WHERE created_at < ?
AND NOT EXISTS (
SELECT 1 FROM messages WHERE messages.channel_id = session_participants.channel_id
)`, olderThan)
if err != nil {
return 0, err
}
return res.RowsAffected()
}
func nullText(s string) interface{} {
if s == "" {
return nil
}
return s
}