Add slack integration

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
Sacha Al Himdani
2025-10-15 23:41:59 +02:00
parent 8302b11614
commit de004ce8d7
38 changed files with 2621 additions and 1578 deletions

View File

@@ -34,6 +34,7 @@ type (
proboSvc *probo.Service
encryptionKey cipher.EncryptionKey
tokenSecret string
hostname string
auth *auth.Service
html2pdfConverter *html2pdf.Converter
fileManager *filemanager.Service
@@ -47,6 +48,7 @@ type (
proboSvc *probo.Service
encryptionKey cipher.EncryptionKey
tokenSecret string
hostname string
auth *auth.Service
html2pdfConverter *html2pdf.Converter
fileManager *filemanager.Service
@@ -66,6 +68,7 @@ func NewService(
pgClient *pg.Client,
s3Client *s3.Client,
bucket string,
hostname string,
encryptionKey cipher.EncryptionKey,
tokenSecret string,
auth *auth.Service,
@@ -78,6 +81,7 @@ func NewService(
bucket: bucket,
encryptionKey: encryptionKey,
tokenSecret: tokenSecret,
hostname: hostname,
auth: auth,
html2pdfConverter: html2pdfConverter,
fileManager: fileManagerService,
@@ -93,6 +97,7 @@ func (s *Service) WithTenant(tenantID gid.TenantID) *TenantService {
proboSvc: s.proboSvc,
encryptionKey: s.encryptionKey,
tokenSecret: s.tokenSecret,
hostname: s.hostname,
auth: s.auth,
html2pdfConverter: s.html2pdfConverter,
fileManager: s.fileManager,

24
pkg/trust/templates.go Normal file
View File

@@ -0,0 +1,24 @@
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
package trust
import (
"embed"
)
var (
//go:embed templates/*.tmpl
Templates embed.FS
)

View File

@@ -0,0 +1,7 @@
*New Trust Center Access Request*
*Organization:* {{.OrganizationName}}
*Requested by:* {{.RequesterName}}
*Email:* {{.RequesterEmail}}
<{{.ConsoleUrl}}|View Access Requests>

View File

@@ -15,22 +15,28 @@
package trust
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"net/mail"
"text/template"
"time"
"github.com/getprobo/probo/pkg/auth"
"github.com/getprobo/probo/pkg/coredata"
"github.com/getprobo/probo/pkg/gid"
"github.com/getprobo/probo/pkg/auth"
"go.gearno.de/kit/pg"
)
var (
accessRequestTemplate = template.Must(template.ParseFS(Templates, "templates/access-request.txt.tmpl"))
)
type (
TrustCenterAccessService struct {
svc *TenantService
svc *TenantService
auth *auth.Service
}
@@ -45,6 +51,7 @@ type (
const (
TokenTypeTrustCenterAccess = "trust_center_access"
TrustCenterAccessURLFormat = "https://%s/organizations/%s/trust-center/access"
)
func (s TrustCenterAccessService) ValidateToken(
@@ -74,9 +81,9 @@ func (s TrustCenterAccessService) Request(
now := time.Now()
var access *coredata.TrustCenterAccess
var trustCenter *coredata.TrustCenter
err := s.svc.pg.WithTx(ctx, func(tx pg.Conn) error {
var trustCenter *coredata.TrustCenter
var organizationID gid.GID
trustCenter = &coredata.TrustCenter{}
if err := trustCenter.LoadByID(ctx, tx, s.svc.scope, req.TrustCenterID); err != nil {
@@ -168,6 +175,10 @@ func (s TrustCenterAccessService) Request(
return fmt.Errorf("cannot bulk insert trust center report accesses: %w", err)
}
if err := s.queueSlackNotification(ctx, tx, organizationID, access.Name, access.Email); err != nil {
return fmt.Errorf("cannot queue slack notification: %w", err)
}
return nil
})
@@ -330,3 +341,42 @@ func filterExistingIDs(allIDs []gid.GID, existingIDs []gid.GID) []gid.GID {
return newIDs
}
func (s TrustCenterAccessService) queueSlackNotification(
ctx context.Context,
tx pg.Conn,
organizationID gid.GID,
requesterName string,
requesterEmail string,
) error {
var organization coredata.Organization
if err := organization.LoadByID(ctx, tx, s.svc.scope, organizationID); err != nil {
return fmt.Errorf("cannot load organization: %w", err)
}
consoleURL := fmt.Sprintf(TrustCenterAccessURLFormat, s.svc.hostname, organizationID)
data := struct {
OrganizationName string
RequesterName string
RequesterEmail string
ConsoleUrl string
}{
OrganizationName: organization.Name,
RequesterName: requesterName,
RequesterEmail: requesterEmail,
ConsoleUrl: consoleURL,
}
var buf bytes.Buffer
if err := accessRequestTemplate.Execute(&buf, data); err != nil {
return fmt.Errorf("failed to execute template: %w", err)
}
slackMessage := coredata.NewSlackMessage(s.svc.scope, organizationID, buf.String())
if err := slackMessage.Insert(ctx, tx, s.svc.scope); err != nil {
return fmt.Errorf("cannot insert slack message: %w", err)
}
return nil
}