package controller import ( "fmt" "strings" "github.com/gin-gonic/gin" "github.com/phishingclub/phishingclub/data" "github.com/phishingclub/phishingclub/model" "github.com/phishingclub/phishingclub/remotebrowser" "github.com/phishingclub/phishingclub/repository" "github.com/phishingclub/phishingclub/service" ) // ReportTemplate is the report template controller type ReportTemplate struct { Common ReportTemplateService *service.ReportTemplate CampaignService *service.Campaign OptionService *service.Option ExecPath string } // Create creates a report template func (r *ReportTemplate) Create(g *gin.Context) { session, _, ok := r.handleSession(g) if !ok { return } var req model.ReportTemplate if ok := r.handleParseRequest(g, &req); !ok { return } id, err := r.ReportTemplateService.Create(g.Request.Context(), session, &req) if ok := r.handleErrors(g, err); !ok { return } r.Response.OK(g, gin.H{"id": id.String()}) } // GetAll gets report templates func (r *ReportTemplate) GetAll(g *gin.Context) { session, _, ok := r.handleSession(g) if !ok { return } queryArgs, ok := r.handleQueryArgs(g) if !ok { return } queryArgs.DefaultSortByUpdatedAt() companyID := companyIDFromRequestQuery(g) result, err := r.ReportTemplateService.GetAll( g.Request.Context(), session, companyID, &repository.ReportTemplateOption{QueryArgs: queryArgs}, ) if ok := r.handleErrors(g, err); !ok { return } r.Response.OK(g, result) } // GetByID gets a report template by id func (r *ReportTemplate) GetByID(g *gin.Context) { session, _, ok := r.handleSession(g) if !ok { return } id, ok := r.handleParseIDParam(g) if !ok { return } tmpl, err := r.ReportTemplateService.GetByID(g.Request.Context(), session, id) if ok := r.handleErrors(g, err); !ok { return } r.Response.OK(g, tmpl) } // UpdateByID updates a report template func (r *ReportTemplate) UpdateByID(g *gin.Context) { session, _, ok := r.handleSession(g) if !ok { return } id, ok := r.handleParseIDParam(g) if !ok { return } var req model.ReportTemplate if ok := r.handleParseRequest(g, &req); !ok { return } err := r.ReportTemplateService.UpdateByID(g.Request.Context(), session, id, &req) if ok := r.handleErrors(g, err); !ok { return } r.Response.OK(g, gin.H{}) } // DeleteByID deletes a report template func (r *ReportTemplate) DeleteByID(g *gin.Context) { session, _, ok := r.handleSession(g) if !ok { return } id, ok := r.handleParseIDParam(g) if !ok { return } err := r.ReportTemplateService.DeleteByID(g.Request.Context(), session, id) if ok := r.handleErrors(g, err); !ok { return } r.Response.OK(g, gin.H{}) } // GeneratePDFByCampaignID generates a PDF report for a campaign func (r *ReportTemplate) GeneratePDFByCampaignID(g *gin.Context) { session, _, ok := r.handleSession(g) if !ok { return } opt, _ := r.OptionService.GetOptionWithoutAuth(g.Request.Context(), data.OptionKeyReportPDFEnabled) if opt == nil || opt.Value.String() != "true" { r.Response.NotFound(g) return } id, ok := r.handleParseIDParam(g) if !ok { return } htmlContent, campaignName, err := r.CampaignService.BuildReportHTML( g.Request.Context(), session, id, ) if ok := r.handleErrors(g, err); !ok { return } pdfBytes, err := remotebrowser.RenderHTMLToPDF(g.Request.Context(), htmlContent, r.ExecPath) if ok := r.handleErrors(g, err); !ok { return } filename := fmt.Sprintf("report-%s.pdf", sanitizeFilename(campaignName)) r.responseWithPDF(g, pdfBytes, filename) } // WipeBrowserCache deletes the auto-downloaded Chromium binary used for PDF generation. func (r *ReportTemplate) WipeBrowserCache(g *gin.Context) { session, _, ok := r.handleSession(g) if !ok { return } if err := r.ReportTemplateService.WipeBrowserCache(session); err != nil { r.handleErrors(g, err) return } r.Response.OK(g, gin.H{}) } // sanitizeFilename removes characters that are unsafe in filenames func sanitizeFilename(name string) string { replacer := strings.NewReplacer( "/", "-", "\\", "-", ":", "-", "*", "-", "?", "-", "\"", "-", "<", "-", ">", "-", "|", "-", ) safe := strings.TrimSpace(replacer.Replace(name)) if safe == "" { return "campaign" } return safe }