feat: add broadcast session tracking and chatlog service with database migrations

This commit is contained in:
fabio
2026-10-10 14:17:59 +02:00
parent 8e91572ec9
commit adc0b236a3
13 changed files with 473 additions and 28 deletions

132
broadcasters/chatlog.go Normal file
View File

@@ -0,0 +1,132 @@
package broadcasters
import (
"context"
"strings"
"time"
"encore.dev/beta/errs"
)
type ChatLogEntry struct {
ID int64 `json:"id"`
Timestamp time.Time `json:"timestamp"`
Room string `json:"room"`
Type string `json:"type"`
Data string `json:"data"`
}
type AddChatLogParams struct {
Type string `json:"type"`
Data string `json:"data"`
}
type ListChatLogParams struct {
Limit int `json:"limit,omitempty" query:"limit"`
Type string `json:"type,omitempty" query:"type"`
}
type ListChatLogResponse struct {
Messages []*ChatLogEntry `json:"messages"`
}
// AddChatLog inserisce un nuovo evento o messaggio nella tabella chatlog.
//
//encore:api public method=POST path=/broadcasters/id/:id/chatlog
func AddChatLog(ctx context.Context, id string, p *AddChatLogParams) (*ChatLogEntry, error) {
if strings.TrimSpace(id) == "" {
return nil, &errs.Error{Code: errs.InvalidArgument, Message: "id stanza obbligatorio"}
}
if p == nil || strings.TrimSpace(p.Data) == "" {
return nil, &errs.Error{Code: errs.InvalidArgument, Message: "data obbligatorio"}
}
msgType := strings.ToUpper(strings.TrimSpace(p.Type))
if msgType == "" {
msgType = "MESSAGE"
} else if msgType == "ALERT" {
msgType = "ALLERT"
}
switch msgType {
case "ACTION", "MESSAGE", "ALLERT", "CMD":
default:
msgType = "MESSAGE"
}
entry := &ChatLogEntry{
Room: id,
Type: msgType,
Data: p.Data,
}
err := db.QueryRow(ctx, `
INSERT INTO chatlog (room, type, data, timestamp)
VALUES ($1, $2, $3, NOW())
RETURNING id, timestamp
`, entry.Room, entry.Type, entry.Data).Scan(&entry.ID, &entry.Timestamp)
if err != nil {
return nil, errs.WrapCode(err, errs.Internal, "impossibile salvare il messaggio di chat")
}
return entry, nil
}
// ListChatLog preleva gli ultimi messaggi per la stanza (default 30) e li ordina
// in ordine cronologico crescente (dal più vecchio al più recente), pronti per l'append nella UI.
//
//encore:api public method=GET path=/broadcasters/id/:id/chatlog
func ListChatLog(ctx context.Context, id string, p *ListChatLogParams) (*ListChatLogResponse, error) {
if strings.TrimSpace(id) == "" {
return nil, &errs.Error{Code: errs.InvalidArgument, Message: "id stanza obbligatorio"}
}
limit := 30
filterType := ""
if p != nil {
if p.Limit > 0 {
limit = p.Limit
}
if t := strings.TrimSpace(p.Type); t != "" {
filterType = strings.ToUpper(t)
}
}
if limit > 100 {
limit = 100
}
query := `
SELECT id, timestamp, room, type, data
FROM (
SELECT id, timestamp, room, type, data
FROM chatlog
WHERE room = $1 AND ($3 = '' OR type = $3)
ORDER BY timestamp DESC, id DESC
LIMIT $2
) sub
ORDER BY timestamp ASC, id ASC
`
rows, err := db.Query(ctx, query, id, limit, filterType)
if err != nil {
return nil, errs.WrapCode(err, errs.Internal, "impossibile recuperare i messaggi di chat")
}
defer rows.Close()
var list []*ChatLogEntry
for rows.Next() {
var e ChatLogEntry
if err := rows.Scan(&e.ID, &e.Timestamp, &e.Room, &e.Type, &e.Data); err != nil {
return nil, errs.WrapCode(err, errs.Internal, "errore nella lettura dei messaggi")
}
list = append(list, &e)
}
if err := rows.Err(); err != nil {
return nil, errs.WrapCode(err, errs.Internal, "errore iterazione messaggi")
}
if list == nil {
list = []*ChatLogEntry{}
}
return &ListChatLogResponse{Messages: list}, nil
}

View File

@@ -0,0 +1,9 @@
CREATE TABLE chatlog (
id BIGSERIAL PRIMARY KEY,
timestamp TIMESTAMPTZ NOT NULL DEFAULT NOW(),
room UUID NOT NULL,
type TEXT NOT NULL CHECK (type IN ('ACTION','MESSAGE','ALLERT','CMD')),
data TEXT NOT NULL
);
CREATE INDEX idx_chatlog_room ON chatlog(room);

View File

@@ -6,6 +6,6 @@ ALTER TABLE broadcasters RENAME COLUMN new_id TO id;
ALTER TABLE broadcasters ALTER COLUMN id SET NOT NULL;
ALTER TABLE broadcasters ADD PRIMARY KEY (id);
ALTER TABLE broadcasters ADD COLUMN enabled BOOLEAN NOT NULL DEFAULT FALSE;
ALTER TABLE broadcasters ADD COLUMN used BOOLEAN NOT NULL DEFAULT FALSE;
UPDATE broadcasters SET used = TRUE, enabled = TRUE;
-- enabled column already added in previous migration
-- used column already added in previous migration
-- data update for enabled/used columns moved to previous migration

View File

@@ -0,0 +1,11 @@
-- 4_add_enabled_used.up.sql
-- Aggiunge le colonne "enabled" e "used" alla tabella broadcasters.
-- Queste colonne sono usate dal codice Go per indicare lo stato del profilo.
ALTER TABLE broadcasters
ADD COLUMN enabled BOOLEAN NOT NULL DEFAULT FALSE,
ADD COLUMN used BOOLEAN NOT NULL DEFAULT FALSE;
-- Indici facoltativi per velocizzare le query su queste colonne
CREATE INDEX idx_broadcasters_enabled ON broadcasters(enabled);
CREATE INDEX idx_broadcasters_used ON broadcasters(used);

View File

@@ -0,0 +1,4 @@
-- 5_chatlog_room_text.up.sql
-- Modifica la colonna "room" da UUID a TEXT per supportare qualsiasi identificatore di stanza (es. nomi LiveKit, slug, broadcast-uuid).
ALTER TABLE chatlog ALTER COLUMN room TYPE TEXT;