Add trust center front

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
Sacha Al Himdani
2025-07-28 17:24:16 +02:00
parent 2ac8f31492
commit cb3d79502d
72 changed files with 18712 additions and 82 deletions

View File

@@ -142,6 +142,7 @@ func (a *Audits) LoadByOrganizationID(
scope Scoper,
organizationID gid.GID,
cursor *page.Cursor[AuditOrderField],
filter *AuditFilter,
) error {
q := `
SELECT
@@ -161,12 +162,14 @@ WHERE
%s
AND organization_id = @organization_id
AND %s
AND %s
`
q = fmt.Sprintf(q, scope.SQLFragment(), cursor.SQLFragment())
q = fmt.Sprintf(q, scope.SQLFragment(), filter.SQLFragment(), cursor.SQLFragment())
args := pgx.StrictNamedArgs{"organization_id": organizationID}
maps.Copy(args, scope.SQLArguments())
maps.Copy(args, filter.SQLArguments())
maps.Copy(args, cursor.SQLArguments())
rows, err := conn.Query(ctx, q, args)

View File

@@ -0,0 +1,54 @@
// 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 coredata
import (
"github.com/jackc/pgx/v5"
)
type (
AuditFilter struct {
showOnTrustCenter *bool
}
)
func NewAuditFilter() *AuditFilter {
return &AuditFilter{}
}
func NewAuditTrustCenterFilter() *AuditFilter {
showOnTrustCenter := true
return &AuditFilter{
showOnTrustCenter: &showOnTrustCenter,
}
}
func (f *AuditFilter) SQLArguments() pgx.NamedArgs {
args := pgx.NamedArgs{}
if f.showOnTrustCenter != nil {
args["show_on_trust_center"] = *f.showOnTrustCenter
}
return args
}
func (f *AuditFilter) SQLFragment() string {
if f.showOnTrustCenter != nil {
return "show_on_trust_center = @show_on_trust_center"
}
return "TRUE"
}

View File

@@ -20,7 +20,8 @@ import (
type (
DocumentFilter struct {
query *string
query *string
showOnTrustCenter *bool
}
)
@@ -30,21 +31,52 @@ func NewDocumentFilter(query *string) *DocumentFilter {
}
}
func (f *DocumentFilter) SQLArguments() pgx.NamedArgs {
return pgx.NamedArgs{
"query": f.query,
func NewDocumentTrustCenterFilter() *DocumentFilter {
showOnTrustCenter := true
return &DocumentFilter{
showOnTrustCenter: &showOnTrustCenter,
}
}
func (f *DocumentFilter) SQLArguments() pgx.NamedArgs {
args := pgx.NamedArgs{}
if f.query != nil {
args["query"] = *f.query
}
if f.showOnTrustCenter != nil {
args["show_on_trust_center"] = *f.showOnTrustCenter
}
return args
}
func (f *DocumentFilter) SQLFragment() string {
if f.query == nil || *f.query == "" {
return "TRUE"
}
conditions := []string{}
return `
if f.query != nil && *f.query != "" {
conditions = append(conditions, `
search_vector @@ (
SELECT to_tsquery('simple', string_agg(lexeme || ':*', ' & '))
FROM unnest(regexp_split_to_array(trim(@query), '\s+')) AS lexeme
)
`
)`)
}
if f.showOnTrustCenter != nil {
conditions = append(conditions, "show_on_trust_center = @show_on_trust_center")
}
if len(conditions) == 0 {
return "TRUE"
}
result := ""
for i, condition := range conditions {
if i > 0 {
result += " AND "
}
result += condition
}
return result
}

View File

@@ -38,4 +38,5 @@ const (
AuditEntityType
ReportEntityType
TrustCenterEntityType
TrustCenterAccessEntityType
)

View File

@@ -0,0 +1,11 @@
CREATE TABLE trust_center_accesses (
id TEXT PRIMARY KEY,
tenant_id TEXT NOT NULL,
trust_center_id TEXT NOT NULL REFERENCES trust_centers(id) ON DELETE CASCADE,
email CITEXT NOT NULL,
name TEXT NOT NULL,
active BOOLEAN NOT NULL DEFAULT true,
created_at TIMESTAMP WITH TIME ZONE NOT NULL,
updated_at TIMESTAMP WITH TIME ZONE NOT NULL,
UNIQUE(trust_center_id, email)
);

View File

@@ -135,6 +135,44 @@ LIMIT 1;
return nil
}
func (tc *TrustCenter) LoadBySlug(
ctx context.Context,
conn pg.Conn,
slug string,
) error {
q := `
SELECT
id,
organization_id,
tenant_id,
active,
slug,
created_at,
updated_at
FROM
trust_centers
WHERE
slug = @slug
LIMIT 1;
`
args := pgx.StrictNamedArgs{"slug": slug}
rows, err := conn.Query(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot query trust center: %w", err)
}
trustCenter, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[TrustCenter])
if err != nil {
return fmt.Errorf("cannot collect trust center: %w", err)
}
*tc = trustCenter
return nil
}
func (tc *TrustCenter) Insert(
ctx context.Context,
conn pg.Conn,

View File

@@ -0,0 +1,318 @@
// 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 coredata
import (
"context"
"fmt"
"maps"
"time"
"github.com/getprobo/probo/pkg/gid"
"github.com/getprobo/probo/pkg/page"
"github.com/jackc/pgx/v5"
"go.gearno.de/kit/pg"
)
type (
TrustCenterAccess struct {
ID gid.GID `db:"id"`
TenantID gid.TenantID `db:"tenant_id"`
TrustCenterID gid.GID `db:"trust_center_id"`
Email string `db:"email"`
Name string `db:"name"`
Active bool `db:"active"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
}
TrustCenterAccesses []*TrustCenterAccess
)
func (tca *TrustCenterAccess) CursorKey(orderBy TrustCenterAccessOrderField) page.CursorKey {
switch orderBy {
case TrustCenterAccessOrderFieldCreatedAt:
return page.NewCursorKey(tca.ID, tca.CreatedAt)
}
panic(fmt.Sprintf("unsupported order by: %s", orderBy))
}
func (tca *TrustCenterAccess) LoadByID(
ctx context.Context,
conn pg.Conn,
scope Scoper,
accessID gid.GID,
) error {
q := `
SELECT
id,
tenant_id,
trust_center_id,
email,
name,
active,
created_at,
updated_at
FROM
trust_center_accesses
WHERE
%s
AND id = @access_id
LIMIT 1;
`
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{"access_id": accessID}
maps.Copy(args, scope.SQLArguments())
rows, err := conn.Query(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot query trust center access: %w", err)
}
access, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[TrustCenterAccess])
if err != nil {
return fmt.Errorf("cannot collect trust center access: %w", err)
}
*tca = access
return nil
}
func (tca *TrustCenterAccess) LoadByTrustCenterIDAndEmail(
ctx context.Context,
conn pg.Conn,
scope Scoper,
trustCenterID gid.GID,
email string,
) error {
q := `
SELECT
id,
tenant_id,
trust_center_id,
email,
name,
active,
created_at,
updated_at
FROM
trust_center_accesses
WHERE
%s
AND trust_center_id = @trust_center_id
AND email = @email
LIMIT 1;
`
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{
"trust_center_id": trustCenterID,
"email": email,
}
maps.Copy(args, scope.SQLArguments())
rows, err := conn.Query(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot query trust center access: %w", err)
}
access, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[TrustCenterAccess])
if err != nil {
return fmt.Errorf("cannot collect trust center access: %w", err)
}
*tca = access
return nil
}
func (tca *TrustCenterAccess) Insert(
ctx context.Context,
conn pg.Conn,
scope Scoper,
) error {
q := `
INSERT INTO trust_center_accesses (
id,
tenant_id,
trust_center_id,
email,
name,
active,
created_at,
updated_at
) VALUES (
@id,
@tenant_id,
@trust_center_id,
@email,
@name,
@active,
@created_at,
@updated_at
)
`
args := pgx.StrictNamedArgs{
"id": tca.ID,
"tenant_id": tca.TenantID,
"trust_center_id": tca.TrustCenterID,
"email": tca.Email,
"name": tca.Name,
"active": tca.Active,
"created_at": tca.CreatedAt,
"updated_at": tca.UpdatedAt,
}
_, err := conn.Exec(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot insert trust center access: %w", err)
}
return nil
}
func (tca *TrustCenterAccess) Update(
ctx context.Context,
conn pg.Conn,
scope Scoper,
) error {
q := `
UPDATE trust_center_accesses
SET
active = @active,
updated_at = @updated_at
WHERE
%s
AND id = @id
`
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{
"id": tca.ID,
"active": tca.Active,
"updated_at": tca.UpdatedAt,
}
maps.Copy(args, scope.SQLArguments())
_, err := conn.Exec(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot update trust center access: %w", err)
}
return nil
}
func (tca *TrustCenterAccess) Delete(
ctx context.Context,
conn pg.Conn,
scope Scoper,
) error {
q := `
DELETE FROM trust_center_accesses
WHERE
%s
AND id = @id
`
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{
"id": tca.ID,
}
maps.Copy(args, scope.SQLArguments())
_, err := conn.Exec(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot delete trust center access: %w", err)
}
return nil
}
func (tcas *TrustCenterAccesses) LoadByTrustCenterID(
ctx context.Context,
conn pg.Conn,
scope Scoper,
trustCenterID gid.GID,
cursor *page.Cursor[TrustCenterAccessOrderField],
) error {
q := `
SELECT
id,
tenant_id,
trust_center_id,
email,
name,
active,
created_at,
updated_at
FROM
trust_center_accesses
WHERE
%s
AND trust_center_id = @trust_center_id
AND %s
`
q = fmt.Sprintf(q, scope.SQLFragment(), cursor.SQLFragment())
args := pgx.StrictNamedArgs{
"trust_center_id": trustCenterID,
}
maps.Copy(args, scope.SQLArguments())
maps.Copy(args, cursor.SQLArguments())
rows, err := conn.Query(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot query trust center accesses: %w", err)
}
accesses, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[TrustCenterAccess])
if err != nil {
return fmt.Errorf("cannot collect trust center accesses: %w", err)
}
*tcas = accesses
return nil
}
type (
TrustCenterAccessOrderField string
)
const (
TrustCenterAccessOrderFieldCreatedAt TrustCenterAccessOrderField = "CREATED_AT"
)
func (tcaof TrustCenterAccessOrderField) String() string {
return string(tcaof)
}
func (tcaof TrustCenterAccessOrderField) Column() string {
switch tcaof {
case TrustCenterAccessOrderFieldCreatedAt:
return "created_at"
}
panic(fmt.Sprintf("unsupported order by: %s", tcaof))
}

View File

@@ -278,45 +278,48 @@ func (v *Vendors) LoadByOrganizationID(
scope Scoper,
organizationID gid.GID,
cursor *page.Cursor[VendorOrderField],
filter *VendorFilter,
) error {
q := `
SELECT
id,
tenant_id,
organization_id,
name,
description,
category,
headquarter_address,
legal_name,
website_url,
privacy_policy_url,
service_level_agreement_url,
data_processing_agreement_url,
business_associate_agreement_url,
subprocessors_list_url,
certifications,
business_owner_id,
security_owner_id,
status_page_url,
terms_of_service_url,
security_page_url,
trust_page_url,
show_on_trust_center,
created_at,
updated_at
id,
tenant_id,
organization_id,
name,
description,
category,
headquarter_address,
legal_name,
website_url,
privacy_policy_url,
service_level_agreement_url,
data_processing_agreement_url,
business_associate_agreement_url,
subprocessors_list_url,
certifications,
business_owner_id,
security_owner_id,
status_page_url,
terms_of_service_url,
security_page_url,
trust_page_url,
show_on_trust_center,
created_at,
updated_at
FROM
vendors
vendors
WHERE
%s
AND organization_id = @organization_id
AND %s
%s
AND organization_id = @organization_id
AND %s
AND %s
`
q = fmt.Sprintf(q, scope.SQLFragment(), cursor.SQLFragment())
q = fmt.Sprintf(q, scope.SQLFragment(), filter.SQLFragment(), cursor.SQLFragment())
args := pgx.StrictNamedArgs{"organization_id": organizationID}
maps.Copy(args, cursor.SQLArguments())
maps.Copy(args, scope.SQLArguments())
maps.Copy(args, filter.SQLArguments())
maps.Copy(args, cursor.SQLArguments())
rows, err := conn.Query(ctx, q, args)
if err != nil {

View File

@@ -0,0 +1,54 @@
// 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 coredata
import (
"github.com/jackc/pgx/v5"
)
type (
VendorFilter struct {
showOnTrustCenter *bool
}
)
func NewVendorFilter() *VendorFilter {
return &VendorFilter{}
}
func NewVendorTrustCenterFilter() *VendorFilter {
showOnTrustCenter := true
return &VendorFilter{
showOnTrustCenter: &showOnTrustCenter,
}
}
func (f *VendorFilter) SQLArguments() pgx.NamedArgs {
args := pgx.NamedArgs{}
if f.showOnTrustCenter != nil {
args["show_on_trust_center"] = *f.showOnTrustCenter
}
return args
}
func (f *VendorFilter) SQLFragment() string {
if f.showOnTrustCenter != nil {
return "show_on_trust_center = @show_on_trust_center"
}
return "TRUE"
}