Add new console compliance page brand tab

Signed-off-by: Émile Ré <emile@getprobo.com>
This commit is contained in:
Émile Ré
2026-01-29 16:22:23 +04:00
parent 65deef5d29
commit 8b5b9e732f
11 changed files with 610 additions and 98 deletions

View File

@@ -19,7 +19,6 @@ import (
"fmt"
"io"
"mime"
"net/url"
"path/filepath"
"time"
@@ -338,23 +337,102 @@ func (s TrustCenterService) GenerateNDAFileURL(
return nil, nil
}
presignClient := s3.NewPresignClient(s.svc.s3)
encodedFilename := url.QueryEscape(file.FileName)
contentDisposition := fmt.Sprintf("attachment; filename=\"%s\"; filename*=UTF-8''%s",
encodedFilename, encodedFilename)
presignedReq, err := presignClient.PresignGetObject(ctx, &s3.GetObjectInput{
Bucket: aws.String(s.svc.bucket),
Key: aws.String(file.FileKey),
ResponseCacheControl: aws.String("max-age=3600, public"),
ResponseContentDisposition: aws.String(contentDisposition),
}, func(opts *s3.PresignOptions) {
opts.Expires = expiresIn
})
presignedURL, err := s.svc.fileManager.GenerateFileUrl(ctx, file, expiresIn)
if err != nil {
return nil, fmt.Errorf("cannot presign GetObject request: %w", err)
return nil, fmt.Errorf("cannot generate file URL: %w", err)
}
return &presignedReq.URL, nil
return &presignedURL, nil
}
func (s TrustCenterService) GenerateLogoURL(
ctx context.Context,
compliancePageID gid.GID,
expiresIn time.Duration,
) (*string, error) {
file := &coredata.File{}
compliancePage := &coredata.TrustCenter{}
err := s.svc.pg.WithConn(
ctx,
func(conn pg.Conn) error {
if err := compliancePage.LoadByID(ctx, conn, s.svc.scope, compliancePageID); err != nil {
return fmt.Errorf("cannot load compliance page: %w", err)
}
if compliancePage.LogoFileID == nil {
return nil
}
if err := file.LoadByID(ctx, conn, s.svc.scope, *compliancePage.LogoFileID); err != nil {
return fmt.Errorf("cannot load file: %w", err)
}
return nil
},
)
if err != nil {
return nil, err
}
if compliancePage.LogoFileID == nil {
return nil, nil
}
if file.FileKey == "" {
return nil, nil
}
presignedURL, err := s.svc.fileManager.GenerateFileUrl(ctx, file, expiresIn)
if err != nil {
return nil, fmt.Errorf("cannot generate file URL: %w", err)
}
return &presignedURL, nil
}
func (s TrustCenterService) GenerateDarkLogoURL(
ctx context.Context,
compliancePageID gid.GID,
expiresIn time.Duration,
) (*string, error) {
file := &coredata.File{}
compliancePage := &coredata.TrustCenter{}
err := s.svc.pg.WithConn(
ctx,
func(conn pg.Conn) error {
if err := compliancePage.LoadByID(ctx, conn, s.svc.scope, compliancePageID); err != nil {
return fmt.Errorf("cannot load compliance page: %w", err)
}
if compliancePage.DarkLogoFileID == nil {
return nil
}
if err := file.LoadByID(ctx, conn, s.svc.scope, *compliancePage.DarkLogoFileID); err != nil {
return fmt.Errorf("cannot load file: %w", err)
}
return nil
},
)
if err != nil {
return nil, err
}
if compliancePage.LogoFileID == nil {
return nil, nil
}
if file.FileKey == "" {
return nil, nil
}
presignedURL, err := s.svc.fileManager.GenerateFileUrl(ctx, file, expiresIn)
if err != nil {
return nil, fmt.Errorf("cannot generate file URL: %w", err)
}
return &presignedURL, nil
}

View File

@@ -1562,6 +1562,8 @@ input VendorFilter {
type TrustCenter implements Node {
id: ID!
active: Boolean!
logoFileUrl: String @goField(forceResolver: true)
darkLogoFileUrl: String @goField(forceResolver: true)
ndaFileName: String
ndaFileUrl: String @goField(forceResolver: true)
createdAt: Datetime!

View File

@@ -1495,16 +1495,18 @@ type ComplexityRoot struct {
}
TrustCenter struct {
Accesses func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.OrderBy[coredata.TrustCenterAccessOrderField]) int
Active func(childComplexity int) int
CreatedAt func(childComplexity int) int
ID func(childComplexity int) int
NdaFileName func(childComplexity int) int
NdaFileURL func(childComplexity int) int
Organization func(childComplexity int) int
Permission func(childComplexity int, action string) int
References func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.OrderBy[coredata.TrustCenterReferenceOrderField]) int
UpdatedAt func(childComplexity int) int
Accesses func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.OrderBy[coredata.TrustCenterAccessOrderField]) int
Active func(childComplexity int) int
CreatedAt func(childComplexity int) int
DarkLogoFileURL func(childComplexity int) int
ID func(childComplexity int) int
LogoFileURL func(childComplexity int) int
NdaFileName func(childComplexity int) int
NdaFileURL func(childComplexity int) int
Organization func(childComplexity int) int
Permission func(childComplexity int, action string) int
References func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.OrderBy[coredata.TrustCenterReferenceOrderField]) int
UpdatedAt func(childComplexity int) int
}
TrustCenterAccess struct {
@@ -2374,6 +2376,9 @@ type TransferImpactAssessmentConnectionResolver interface {
TotalCount(ctx context.Context, obj *types.TransferImpactAssessmentConnection) (int, error)
}
type TrustCenterResolver interface {
LogoFileURL(ctx context.Context, obj *types.TrustCenter) (*string, error)
DarkLogoFileURL(ctx context.Context, obj *types.TrustCenter) (*string, error)
NdaFileURL(ctx context.Context, obj *types.TrustCenter) (*string, error)
Organization(ctx context.Context, obj *types.TrustCenter) (*types.Organization, error)
@@ -8575,12 +8580,24 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
}
return e.complexity.TrustCenter.CreatedAt(childComplexity), true
case "TrustCenter.darkLogoFileUrl":
if e.complexity.TrustCenter.DarkLogoFileURL == nil {
break
}
return e.complexity.TrustCenter.DarkLogoFileURL(childComplexity), true
case "TrustCenter.id":
if e.complexity.TrustCenter.ID == nil {
break
}
return e.complexity.TrustCenter.ID(childComplexity), true
case "TrustCenter.logoFileUrl":
if e.complexity.TrustCenter.LogoFileURL == nil {
break
}
return e.complexity.TrustCenter.LogoFileURL(childComplexity), true
case "TrustCenter.ndaFileName":
if e.complexity.TrustCenter.NdaFileName == nil {
break
@@ -11873,6 +11890,8 @@ input VendorFilter {
type TrustCenter implements Node {
id: ID!
active: Boolean!
logoFileUrl: String @goField(forceResolver: true)
darkLogoFileUrl: String @goField(forceResolver: true)
ndaFileName: String
ndaFileUrl: String @goField(forceResolver: true)
createdAt: Datetime!
@@ -28154,6 +28173,10 @@ func (ec *executionContext) fieldContext_DeleteTrustCenterNDAPayload_trustCenter
return ec.fieldContext_TrustCenter_id(ctx, field)
case "active":
return ec.fieldContext_TrustCenter_active(ctx, field)
case "logoFileUrl":
return ec.fieldContext_TrustCenter_logoFileUrl(ctx, field)
case "darkLogoFileUrl":
return ec.fieldContext_TrustCenter_darkLogoFileUrl(ctx, field)
case "ndaFileName":
return ec.fieldContext_TrustCenter_ndaFileName(ctx, field)
case "ndaFileUrl":
@@ -42676,6 +42699,10 @@ func (ec *executionContext) fieldContext_Organization_trustCenter(_ context.Cont
return ec.fieldContext_TrustCenter_id(ctx, field)
case "active":
return ec.fieldContext_TrustCenter_active(ctx, field)
case "logoFileUrl":
return ec.fieldContext_TrustCenter_logoFileUrl(ctx, field)
case "darkLogoFileUrl":
return ec.fieldContext_TrustCenter_darkLogoFileUrl(ctx, field)
case "ndaFileName":
return ec.fieldContext_TrustCenter_ndaFileName(ctx, field)
case "ndaFileUrl":
@@ -50470,6 +50497,64 @@ func (ec *executionContext) fieldContext_TrustCenter_active(_ context.Context, f
return fc, nil
}
func (ec *executionContext) _TrustCenter_logoFileUrl(ctx context.Context, field graphql.CollectedField, obj *types.TrustCenter) (ret graphql.Marshaler) {
return graphql.ResolveField(
ctx,
ec.OperationContext,
field,
ec.fieldContext_TrustCenter_logoFileUrl,
func(ctx context.Context) (any, error) {
return ec.resolvers.TrustCenter().LogoFileURL(ctx, obj)
},
nil,
ec.marshalOString2ᚖstring,
true,
false,
)
}
func (ec *executionContext) fieldContext_TrustCenter_logoFileUrl(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "TrustCenter",
Field: field,
IsMethod: true,
IsResolver: true,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
return nil, errors.New("field of type String does not have child fields")
},
}
return fc, nil
}
func (ec *executionContext) _TrustCenter_darkLogoFileUrl(ctx context.Context, field graphql.CollectedField, obj *types.TrustCenter) (ret graphql.Marshaler) {
return graphql.ResolveField(
ctx,
ec.OperationContext,
field,
ec.fieldContext_TrustCenter_darkLogoFileUrl,
func(ctx context.Context) (any, error) {
return ec.resolvers.TrustCenter().DarkLogoFileURL(ctx, obj)
},
nil,
ec.marshalOString2ᚖstring,
true,
false,
)
}
func (ec *executionContext) fieldContext_TrustCenter_darkLogoFileUrl(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "TrustCenter",
Field: field,
IsMethod: true,
IsResolver: true,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
return nil, errors.New("field of type String does not have child fields")
},
}
return fc, nil
}
func (ec *executionContext) _TrustCenter_ndaFileName(ctx context.Context, field graphql.CollectedField, obj *types.TrustCenter) (ret graphql.Marshaler) {
return graphql.ResolveField(
ctx,
@@ -51887,6 +51972,10 @@ func (ec *executionContext) fieldContext_TrustCenterEdge_node(_ context.Context,
return ec.fieldContext_TrustCenter_id(ctx, field)
case "active":
return ec.fieldContext_TrustCenter_active(ctx, field)
case "logoFileUrl":
return ec.fieldContext_TrustCenter_logoFileUrl(ctx, field)
case "darkLogoFileUrl":
return ec.fieldContext_TrustCenter_darkLogoFileUrl(ctx, field)
case "ndaFileName":
return ec.fieldContext_TrustCenter_ndaFileName(ctx, field)
case "ndaFileUrl":
@@ -54292,6 +54381,10 @@ func (ec *executionContext) fieldContext_UpdateTrustCenterPayload_trustCenter(_
return ec.fieldContext_TrustCenter_id(ctx, field)
case "active":
return ec.fieldContext_TrustCenter_active(ctx, field)
case "logoFileUrl":
return ec.fieldContext_TrustCenter_logoFileUrl(ctx, field)
case "darkLogoFileUrl":
return ec.fieldContext_TrustCenter_darkLogoFileUrl(ctx, field)
case "ndaFileName":
return ec.fieldContext_TrustCenter_ndaFileName(ctx, field)
case "ndaFileUrl":
@@ -54777,6 +54870,10 @@ func (ec *executionContext) fieldContext_UploadTrustCenterNDAPayload_trustCenter
return ec.fieldContext_TrustCenter_id(ctx, field)
case "active":
return ec.fieldContext_TrustCenter_active(ctx, field)
case "logoFileUrl":
return ec.fieldContext_TrustCenter_logoFileUrl(ctx, field)
case "darkLogoFileUrl":
return ec.fieldContext_TrustCenter_darkLogoFileUrl(ctx, field)
case "ndaFileName":
return ec.fieldContext_TrustCenter_ndaFileName(ctx, field)
case "ndaFileUrl":
@@ -84633,6 +84730,72 @@ func (ec *executionContext) _TrustCenter(ctx context.Context, sel ast.SelectionS
if out.Values[i] == graphql.Null {
atomic.AddUint32(&out.Invalids, 1)
}
case "logoFileUrl":
field := field
innerFunc := func(ctx context.Context, _ *graphql.FieldSet) (res graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
}
}()
res = ec._TrustCenter_logoFileUrl(ctx, field, obj)
return res
}
if field.Deferrable != nil {
dfs, ok := deferred[field.Deferrable.Label]
di := 0
if ok {
dfs.AddField(field)
di = len(dfs.Values) - 1
} else {
dfs = graphql.NewFieldSet([]graphql.CollectedField{field})
deferred[field.Deferrable.Label] = dfs
}
dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler {
return innerFunc(ctx, dfs)
})
// don't run the out.Concurrently() call below
out.Values[i] = graphql.Null
continue
}
out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) })
case "darkLogoFileUrl":
field := field
innerFunc := func(ctx context.Context, _ *graphql.FieldSet) (res graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
}
}()
res = ec._TrustCenter_darkLogoFileUrl(ctx, field, obj)
return res
}
if field.Deferrable != nil {
dfs, ok := deferred[field.Deferrable.Label]
di := 0
if ok {
dfs.AddField(field)
di = len(dfs.Values) - 1
} else {
dfs = graphql.NewFieldSet([]graphql.CollectedField{field})
deferred[field.Deferrable.Label] = dfs
}
dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler {
return innerFunc(ctx, dfs)
})
// don't run the out.Concurrently() call below
out.Values[i] = graphql.Null
continue
}
out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) })
case "ndaFileName":
out.Values[i] = ec._TrustCenter_ndaFileName(ctx, field, obj)
case "ndaFileUrl":

View File

@@ -1841,16 +1841,18 @@ type TransferImpactAssessmentFilter struct {
}
type TrustCenter struct {
ID gid.GID `json:"id"`
Active bool `json:"active"`
NdaFileName *string `json:"ndaFileName,omitempty"`
NdaFileURL *string `json:"ndaFileUrl,omitempty"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
Organization *Organization `json:"organization"`
Accesses *TrustCenterAccessConnection `json:"accesses"`
References *TrustCenterReferenceConnection `json:"references"`
Permission bool `json:"permission"`
ID gid.GID `json:"id"`
Active bool `json:"active"`
LogoFileURL *string `json:"logoFileUrl,omitempty"`
DarkLogoFileURL *string `json:"darkLogoFileUrl,omitempty"`
NdaFileName *string `json:"ndaFileName,omitempty"`
NdaFileURL *string `json:"ndaFileUrl,omitempty"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
Organization *Organization `json:"organization"`
Accesses *TrustCenterAccessConnection `json:"accesses"`
References *TrustCenterReferenceConnection `json:"references"`
Permission bool `json:"permission"`
}
func (TrustCenter) IsNode() {}

View File

@@ -7577,6 +7577,40 @@ func (r *transferImpactAssessmentConnectionResolver) TotalCount(ctx context.Cont
panic(fmt.Errorf("unsupported resolver: %T", obj.Resolver))
}
// LogoFileURL is the resolver for the logoFileUrl field.
func (r *trustCenterResolver) LogoFileURL(ctx context.Context, obj *types.TrustCenter) (*string, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionTrustCenterGet); err != nil {
return nil, err
}
prb := r.ProboService(ctx, obj.ID.TenantID())
logoURL, err := prb.TrustCenters.GenerateLogoURL(ctx, obj.ID, 1*time.Hour)
if err != nil {
// TODO no panic use gqlutils.InternalError
panic(fmt.Errorf("cannot generate logo url: %w", err))
}
return logoURL, nil
}
// DarkLogoFileURL is the resolver for the darkLogoFileUrl field.
func (r *trustCenterResolver) DarkLogoFileURL(ctx context.Context, obj *types.TrustCenter) (*string, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionTrustCenterGet); err != nil {
return nil, err
}
prb := r.ProboService(ctx, obj.ID.TenantID())
logoURL, err := prb.TrustCenters.GenerateDarkLogoURL(ctx, obj.ID, 1*time.Hour)
if err != nil {
// TODO no panic use gqlutils.InternalError
panic(fmt.Errorf("cannot generate logo url: %w", err))
}
return logoURL, nil
}
// NdaFileURL is the resolver for the ndaFileUrl field.
func (r *trustCenterResolver) NdaFileURL(ctx context.Context, obj *types.TrustCenter) (*string, error) {
hasPermission, err := r.Resolver.Permission(ctx, obj, probo.ActionTrustCenterGetNda)

View File

@@ -17,11 +17,8 @@ package trust
import (
"context"
"fmt"
"net/url"
"time"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/s3"
"go.gearno.de/kit/pg"
"go.probo.inc/probo/pkg/coredata"
"go.probo.inc/probo/pkg/gid"
@@ -120,25 +117,12 @@ func (s TrustCenterService) GenerateNDAFileURL(
return "", err
}
presignClient := s3.NewPresignClient(s.svc.s3)
encodedFilename := url.QueryEscape(file.FileName)
contentDisposition := fmt.Sprintf("attachment; filename=\"%s\"; filename*=UTF-8''%s",
encodedFilename, encodedFilename)
presignedReq, err := presignClient.PresignGetObject(ctx, &s3.GetObjectInput{
Bucket: aws.String(s.svc.bucket),
Key: aws.String(file.FileKey),
ResponseCacheControl: aws.String("max-age=3600, public"),
ResponseContentDisposition: aws.String(contentDisposition),
}, func(opts *s3.PresignOptions) {
opts.Expires = expiresIn
})
presignedURL, err := s.svc.fileManager.GenerateFileUrl(ctx, file, expiresIn)
if err != nil {
return "", fmt.Errorf("cannot presign GetObject request: %w", err)
return "", fmt.Errorf("cannot generate file URL: %w", err)
}
return presignedReq.URL, nil
return presignedURL, nil
}
func (s TrustCenterService) GenerateLogoURL(
@@ -146,21 +130,45 @@ func (s TrustCenterService) GenerateLogoURL(
compliancePageID gid.GID,
expiresIn time.Duration,
) (*string, error) {
compliancePage, _, err := s.Get(ctx, compliancePageID)
file := &coredata.File{}
compliancePage := &coredata.TrustCenter{}
err := s.svc.pg.WithConn(
ctx,
func(conn pg.Conn) error {
if err := compliancePage.LoadByID(ctx, conn, s.svc.scope, compliancePageID); err != nil {
return fmt.Errorf("cannot load compliance page: %w", err)
}
if compliancePage.LogoFileID == nil {
return nil
}
if err := file.LoadByID(ctx, conn, s.svc.scope, *compliancePage.LogoFileID); err != nil {
return fmt.Errorf("cannot load file: %w", err)
}
return nil
},
)
if err != nil {
return nil, fmt.Errorf("cannot get compliance page: %w", err)
return nil, err
}
if compliancePage.LogoFileID == nil {
return nil, nil
}
url, err := s.generateFileURL(ctx, *compliancePage.LogoFileID, expiresIn)
if file.FileKey == "" {
return nil, nil
}
presignedURL, err := s.svc.fileManager.GenerateFileUrl(ctx, file, expiresIn)
if err != nil {
return nil, fmt.Errorf("cannot generate file URL: %w", err)
}
return url, nil
return &presignedURL, nil
}
func (s TrustCenterService) GenerateDarkLogoURL(
@@ -168,51 +176,43 @@ func (s TrustCenterService) GenerateDarkLogoURL(
compliancePageID gid.GID,
expiresIn time.Duration,
) (*string, error) {
compliancePage, _, err := s.Get(ctx, compliancePageID)
file := &coredata.File{}
compliancePage := &coredata.TrustCenter{}
err := s.svc.pg.WithConn(
ctx,
func(conn pg.Conn) error {
if err := compliancePage.LoadByID(ctx, conn, s.svc.scope, compliancePageID); err != nil {
return fmt.Errorf("cannot load compliance page: %w", err)
}
if compliancePage.DarkLogoFileID == nil {
return nil
}
if err := file.LoadByID(ctx, conn, s.svc.scope, *compliancePage.DarkLogoFileID); err != nil {
return fmt.Errorf("cannot load file: %w", err)
}
return nil
},
)
if err != nil {
return nil, fmt.Errorf("cannot get compliance page: %w", err)
return nil, err
}
if compliancePage.DarkLogoFileID == nil {
if compliancePage.LogoFileID == nil {
return nil, nil
}
url, err := s.generateFileURL(ctx, *compliancePage.DarkLogoFileID, expiresIn)
if file.FileKey == "" {
return nil, nil
}
presignedURL, err := s.svc.fileManager.GenerateFileUrl(ctx, file, expiresIn)
if err != nil {
return nil, fmt.Errorf("cannot generate file URL: %w", err)
}
return url, nil
}
func (s TrustCenterService) generateFileURL(ctx context.Context, fileID gid.GID, expiresIn time.Duration) (*string, error) {
file := &coredata.File{}
if err := s.svc.pg.WithConn(
ctx,
func(conn pg.Conn) error {
return file.LoadByID(ctx, conn, s.svc.scope, fileID)
},
); err != nil {
return nil, fmt.Errorf("cannot load file: %w", err)
}
presignClient := s3.NewPresignClient(s.svc.s3)
encodedFilename := url.QueryEscape(file.FileName)
contentDisposition := fmt.Sprintf("attachment; filename=\"%s\"; filename*=UTF-8''%s",
encodedFilename, encodedFilename)
presignedReq, err := presignClient.PresignGetObject(ctx, &s3.GetObjectInput{
Bucket: aws.String(s.svc.bucket),
Key: aws.String(file.FileKey),
ResponseCacheControl: aws.String("max-age=3600, public"),
ResponseContentDisposition: aws.String(contentDisposition),
}, func(opts *s3.PresignOptions) {
opts.Expires = expiresIn
})
if err != nil {
return nil, fmt.Errorf("cannot presign GetObject request: %w", err)
}
return &presignedReq.URL, nil
return &presignedURL, nil
}