Files
uPortal2/backend/internal/jitsi/repository.go
T
2026-10-21 19:41:26 +03:00

114 lines
3.7 KiB
Go

package jitsi
import (
"context"
"database/sql"
"log/slog"
"time"
)
type Queriable interface {
ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error)
PrepareContext(ctx context.Context, query string) (*sql.Stmt, error)
QueryContext(ctx context.Context, query string, args ...any) (*sql.Rows, error)
QueryRowContext(ctx context.Context, query string, args ...any) *sql.Row
}
type Repository struct {
Db *sql.DB
q Queriable
}
func NewRepository(db *sql.DB) (*Repository, error) {
return &Repository{Db: db, q: db}, nil
}
func (r *Repository) WithTx(tx *sql.Tx) *Repository {
return &Repository{Db: r.Db, q: tx}
}
func (r *Repository) RecordJoin(ctx context.Context, userId int64, room string, at time.Time) error {
query := "INSERT INTO jitsi_room_history(user_id, room_name, last_joined_at) VALUES(?, ?, ?) ON CONFLICT(user_id, room_name) DO UPDATE SET last_joined_at = excluded.last_joined_at"
_, err := r.q.ExecContext(ctx, query, userId, room, at.UTC().Format(time.RFC3339))
if err != nil {
slog.Error("record room join", slog.String("op", "repo.RecordJoin"), slog.Int64("user_id", userId), slog.String("room", room), slog.Any("error", err))
return ErrInternal
}
return nil
}
func (r *Repository) List(ctx context.Context, userId int64, limit, offset int64) ([]*RoomHistory, error) {
query := "SELECT user_id, room_name, last_joined_at FROM jitsi_room_history WHERE user_id = ? ORDER BY last_joined_at DESC"
args := []any{userId}
if limit > 0 {
query += " LIMIT ? OFFSET ?"
args = append(args, limit, offset)
}
rows, err := r.q.QueryContext(ctx, query, args...)
if err != nil {
slog.Error("list room history", slog.String("op", "repo.List"), slog.Int64("user_id", userId), slog.Any("error", err))
return nil, ErrInternal
}
defer rows.Close()
var history []*RoomHistory
for rows.Next() {
entry := &RoomHistory{}
var lastJoinedAt string
err := rows.Scan(&entry.UserId, &entry.RoomName, &lastJoinedAt)
if err != nil {
slog.Error("scan room history", slog.String("op", "repo.List"), slog.Int64("user_id", userId), slog.Any("error", err))
return nil, ErrInternal
}
parsed, err := time.Parse(time.RFC3339, lastJoinedAt)
if err != nil {
slog.Error("parse last_joined_at", slog.String("op", "repo.List"), slog.Int64("user_id", userId), slog.String("room", entry.RoomName), slog.Any("error", err))
return nil, ErrInternal
}
entry.LastJoinedAt = parsed
history = append(history, entry)
}
err = rows.Err()
if err != nil {
slog.Error("iterate room history", slog.String("op", "repo.List"), slog.Int64("user_id", userId), slog.Any("error", err))
return nil, ErrInternal
}
return history, nil
}
func (r *Repository) DeleteRoom(ctx context.Context, userId int64, room string) error {
query := "DELETE FROM jitsi_room_history WHERE user_id = ? AND room_name = ?"
res, err := r.q.ExecContext(ctx, query, userId, room)
if err != nil {
slog.Error("delete room", slog.String("op", "repo.DeleteRoom"), slog.Int64("user_id", userId), slog.String("room", room), slog.Any("error", err))
return ErrInternal
}
affected, err := res.RowsAffected()
if err != nil {
slog.Error("rows affected", slog.String("op", "repo.DeleteRoom"), slog.Int64("user_id", userId), slog.String("room", room), slog.Any("error", err))
return ErrInternal
}
if affected == 0 {
return ErrNotFound
}
return nil
}
func (r *Repository) DeleteAll(ctx context.Context, userId int64) error {
query := "DELETE FROM jitsi_room_history WHERE user_id = ?"
_, err := r.q.ExecContext(ctx, query, userId)
if err != nil {
slog.Error("delete all rooms", slog.String("op", "repo.DeleteAll"), slog.Int64("user_id", userId), slog.Any("error", err))
return ErrInternal
}
return nil
}