oups
This commit is contained in:
@@ -0,0 +1,188 @@
|
||||
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)
|
||||
}
|
||||
Reference in New Issue
Block a user