189 lines
4.5 KiB
Go
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)
|
|
}
|