Files
phishingclub/backend/controller/companyReportConfig.go
T
Ronni Skansing d1f100968e added report delivery
Signed-off-by: Ronni Skansing <rskansing@gmail.com>
2026-06-13 12:46:49 +02:00

241 lines
6.6 KiB
Go

package controller
import (
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"github.com/oapi-codegen/nullable"
"github.com/phishingclub/phishingclub/errs"
"github.com/phishingclub/phishingclub/model"
"github.com/phishingclub/phishingclub/repository"
"github.com/phishingclub/phishingclub/service"
"github.com/phishingclub/phishingclub/vo"
)
// CompanyReportConfig is the report delivery configuration controller. handlers
// come in a per company variant (companyID from the url) and a global variant
// (nil companyID) that edits the fallback config.
type CompanyReportConfig struct {
Common
CompanyReportConfigService *service.CompanyReportConfig
CampaignService *service.Campaign
}
// upsertReportConfigRequest is the request body for the upsert handlers
type upsertReportConfigRequest struct {
Enabled bool `json:"enabled"`
SendOnFinish bool `json:"sendOnFinish"`
RecipientGroupID *string `json:"recipientGroupID"`
SMTPConfigurationID *string `json:"smtpConfigurationID"`
SenderEmail *string `json:"senderEmail"`
EmailSubject *string `json:"emailSubject"`
EmailBody *string `json:"emailBody"`
}
// get returns the config for the given scope (nil companyID is the global default)
func (c *CompanyReportConfig) get(g *gin.Context, companyID *uuid.UUID) {
session, _, ok := c.handleSession(g)
if !ok {
return
}
config, err := c.CompanyReportConfigService.GetByCompanyID(
g.Request.Context(),
session,
companyID,
)
if ok := c.handleErrors(g, err); !ok {
return
}
c.Response.OK(g, config)
}
// upsert creates or updates the config for the given scope
func (c *CompanyReportConfig) upsert(g *gin.Context, companyID *uuid.UUID) {
session, _, ok := c.handleSession(g)
if !ok {
return
}
var req upsertReportConfigRequest
if ok := c.handleParseRequest(g, &req); !ok {
return
}
incoming := &model.CompanyReportConfig{
Enabled: req.Enabled,
SendOnFinish: req.SendOnFinish,
RecipientGroupID: nullable.NewNullNullable[uuid.UUID](),
SMTPConfigurationID: nullable.NewNullNullable[uuid.UUID](),
SenderEmail: nullable.NewNullNullable[vo.Email](),
EmailSubject: nullable.NewNullableWithValue(""),
EmailBody: nullable.NewNullableWithValue(""),
}
if req.EmailSubject != nil {
incoming.EmailSubject.Set(*req.EmailSubject)
}
if req.EmailBody != nil {
incoming.EmailBody.Set(*req.EmailBody)
}
if req.RecipientGroupID != nil && *req.RecipientGroupID != "" {
groupID, err := uuid.Parse(*req.RecipientGroupID)
if err != nil {
c.Response.BadRequestMessage(g, errs.MsgFailedToParseUUID)
return
}
incoming.RecipientGroupID.Set(groupID)
}
if req.SMTPConfigurationID != nil && *req.SMTPConfigurationID != "" {
smtpID, err := uuid.Parse(*req.SMTPConfigurationID)
if err != nil {
c.Response.BadRequestMessage(g, errs.MsgFailedToParseUUID)
return
}
incoming.SMTPConfigurationID.Set(smtpID)
}
if req.SenderEmail != nil && *req.SenderEmail != "" {
email, err := vo.NewEmail(*req.SenderEmail)
if err != nil {
c.Response.BadRequestMessage(g, "invalid sender email")
return
}
incoming.SenderEmail.Set(*email)
}
result, err := c.CompanyReportConfigService.Upsert(
g.Request.Context(),
session,
companyID,
incoming,
)
if ok := c.handleErrors(g, err); !ok {
return
}
c.Response.OK(g, result)
}
// remove deletes the config for the given scope
func (c *CompanyReportConfig) remove(g *gin.Context, companyID *uuid.UUID) {
session, _, ok := c.handleSession(g)
if !ok {
return
}
err := c.CompanyReportConfigService.DeleteByCompanyID(
g.Request.Context(),
session,
companyID,
)
if ok := c.handleErrors(g, err); !ok {
return
}
c.Response.OK(g, gin.H{})
}
// parseCompanyIDParam parses the :companyID url param
func (c *CompanyReportConfig) parseCompanyIDParam(g *gin.Context) (*uuid.UUID, bool) {
companyID, err := uuid.Parse(g.Param("companyID"))
if err != nil {
c.Logger.Debugw("failed to parse companyID param", "error", err)
c.Response.BadRequestMessage(g, errs.MsgFailedToParseUUID)
return nil, false
}
return &companyID, true
}
// GetByCompanyID returns the report delivery configuration for the given company
func (c *CompanyReportConfig) GetByCompanyID(g *gin.Context) {
companyID, ok := c.parseCompanyIDParam(g)
if !ok {
return
}
c.get(g, companyID)
}
// Upsert creates or updates the report delivery configuration for the given company
func (c *CompanyReportConfig) Upsert(g *gin.Context) {
companyID, ok := c.parseCompanyIDParam(g)
if !ok {
return
}
c.upsert(g, companyID)
}
// Delete removes the report delivery configuration for the given company
func (c *CompanyReportConfig) Delete(g *gin.Context) {
companyID, ok := c.parseCompanyIDParam(g)
if !ok {
return
}
c.remove(g, companyID)
}
// GetGlobal returns the global default report delivery configuration
func (c *CompanyReportConfig) GetGlobal(g *gin.Context) {
c.get(g, nil)
}
// UpsertGlobal creates or updates the global default report delivery configuration
func (c *CompanyReportConfig) UpsertGlobal(g *gin.Context) {
c.upsert(g, nil)
}
// DeleteGlobal removes the global default report delivery configuration
func (c *CompanyReportConfig) DeleteGlobal(g *gin.Context) {
c.remove(g, nil)
}
// GetLogByCompanyID returns the report delivery log for the given company
func (c *CompanyReportConfig) GetLogByCompanyID(g *gin.Context) {
session, _, ok := c.handleSession(g)
if !ok {
return
}
companyID, ok := c.parseCompanyIDParam(g)
if !ok {
return
}
queryArgs, ok := c.handleQueryArgs(g)
if !ok {
return
}
// map the table column labels to the underlying db columns
queryArgs.RemapOrderBy(map[string]string{
"date": "created_at",
"created_at": "created_at",
"campaign": "campaign_name",
"group": "group_name",
"trigger": "trigger",
"status": "status",
"recipients": "recipient_count",
})
queryArgs.DefaultSortByCreatedAt()
result, err := c.CompanyReportConfigService.ListLogByCompanyID(
g.Request.Context(),
session,
companyID,
&repository.ReportSendLogOption{QueryArgs: queryArgs},
)
if ok := c.handleErrors(g, err); !ok {
return
}
c.Response.OK(g, result)
}
// SendNow generates the report for the campaign and emails it to the configured
// recipient group right away.
func (c *CompanyReportConfig) SendNow(g *gin.Context) {
session, _, ok := c.handleSession(g)
if !ok {
return
}
campaignID, ok := c.handleParseIDParam(g)
if !ok {
return
}
err := c.CampaignService.SendCampaignReport(
g.Request.Context(),
session,
campaignID,
true,
)
if ok := c.handleErrors(g, err); !ok {
return
}
c.Response.OK(g, gin.H{})
}