Files
2026-05-10 18:07:51 +02:00

189 lines
4.5 KiB
Go

package handlers
import (
"fmt"
"net/http"
"os"
"strconv"
"time"
"github.com/go-chi/chi/v5"
"github.com/leolionad58/ticketbot/internal/db"
)
// TranscriptsHandler serves /transcripts routes.
type TranscriptsHandler struct {
TicketRepo *db.TicketRepo
Renderer *Renderer
DiscordInfo DiscordInfo
}
type transcriptsData struct {
baseData
Tickets []*db.Ticket
Total int
Page int
Pages int
Types []string
StaffList []string
Guilds []GuildInfo
GuildNames map[string]string
// Filter values echoed back to the form
FStatus string
FType string
FStaff string
FGuild string
FFrom string
FTo string
FSearch string
}
// HandleList serves GET /transcripts with search/filter/pagination.
func (h *TranscriptsHandler) HandleList(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
base := h.Renderer.base(r, "transcripts")
q := r.URL.Query()
fStatus := q.Get("status")
fType := q.Get("type")
fStaff := q.Get("staff")
fGuild := q.Get("guild")
fFrom := q.Get("from")
fTo := q.Get("to")
fSearch := q.Get("search")
page, _ := strconv.Atoi(q.Get("page"))
if page < 1 {
page = 1
}
filter := db.TicketFilter{
Status: fStatus,
Type: fType,
StaffID: fStaff,
GuildID: fGuild,
Search: fSearch,
Page: page,
PageSize: 20,
}
if fFrom != "" {
if t, err := time.Parse("2006-01-02", fFrom); err == nil {
filter.From = t
}
}
if fTo != "" {
if t, err := time.Parse("2006-01-02", fTo); err == nil {
filter.To = t.Add(24*time.Hour - time.Second)
}
}
tickets, total, err := h.TicketRepo.ListFiltered(ctx, filter)
if err != nil {
http.Error(w, "db error: "+err.Error(), http.StatusInternalServerError)
return
}
pages := total / 20
if total%20 != 0 {
pages++
}
if pages < 1 {
pages = 1
}
types, _ := h.TicketRepo.ListDistinctTypes(ctx)
staff, _ := h.TicketRepo.ListDistinctStaff(ctx)
var guilds []GuildInfo
guildNames := map[string]string{}
if h.DiscordInfo != nil {
guilds = h.DiscordInfo.GetGuildList()
for _, g := range guilds {
guildNames[g.ID] = g.Name
}
}
h.Renderer.Page(w, "transcripts", transcriptsData{
baseData: base,
Tickets: tickets,
Total: total,
Page: page,
Pages: pages,
Types: types,
StaffList: staff,
Guilds: guilds,
GuildNames: guildNames,
FStatus: fStatus,
FType: fType,
FStaff: fStaff,
FGuild: fGuild,
FFrom: fFrom,
FTo: fTo,
FSearch: fSearch,
})
}
// HandleView serves GET /transcripts/view/{id} — serves the HTML transcript file directly.
// This handler must NOT be wrapped with securityHeaders to avoid CSP restrictions.
func (h *TranscriptsHandler) HandleView(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
id, err := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64)
if err != nil {
http.Error(w, "invalid id", http.StatusBadRequest)
return
}
ticket, err := h.TicketRepo.GetByID(ctx, id)
if err != nil || ticket == nil {
http.NotFound(w, r)
return
}
if !ticket.TranscriptPath.Valid || ticket.TranscriptPath.String == "" {
http.Error(w, "no transcript for this ticket", http.StatusNotFound)
return
}
path := ticket.TranscriptPath.String
data, err := os.ReadFile(path)
if err != nil {
http.Error(w, fmt.Sprintf("transcript file not found: %s", path), http.StatusNotFound)
return
}
w.Header().Set("Content-Type", "text/html; charset=utf-8")
w.WriteHeader(http.StatusOK)
_, _ = w.Write(data)
}
// HandleDownload serves GET /transcripts/download/{id} — serves the HTML transcript as attachment.
func (h *TranscriptsHandler) HandleDownload(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
id, err := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64)
if err != nil {
http.Error(w, "invalid id", http.StatusBadRequest)
return
}
ticket, err := h.TicketRepo.GetByID(ctx, id)
if err != nil || ticket == nil {
http.NotFound(w, r)
return
}
if !ticket.TranscriptPath.Valid || ticket.TranscriptPath.String == "" {
http.Error(w, "no transcript for this ticket", http.StatusNotFound)
return
}
path := ticket.TranscriptPath.String
data, err := os.ReadFile(path)
if err != nil {
http.Error(w, fmt.Sprintf("transcript file not found: %s", path), http.StatusNotFound)
return
}
filename := fmt.Sprintf("transcript_ticket_%d.html", ticket.TicketNumber)
w.Header().Set("Content-Type", "text/html; charset=utf-8")
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%q", filename))
w.WriteHeader(http.StatusOK)
_, _ = w.Write(data)
}