Replace the draft-only unique index with one covering both DRAFT and
PENDING_APPROVAL statuses, ensuring a document can have at most one
active version at a time. Deduplicate any existing data before creating
the index, prioritizing PENDING_APPROVAL over DRAFT.
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
Merge type-only schema files (country_code.graphql, pagination.graphql,
identity.graphql) back into base.graphql for both trust/v1 and console/v1.
These standalone files had no corresponding _resolvers.go files, causing
gqlgen v0.17.87's Rewriter.getSource() to panic with 'slice bounds out
of range' when running go generate. By consolidating them into
base.graphql (which already has base_resolvers.go), gqlgen can process
the schema without needing separate resolver files for pure-type
definitions.
Add server-side validation in BulkRequestSignatures, RequestSignature,
and RequestApproval to load the referenced profiles and verify none
have an ended contract before proceeding. Returns ErrProfileContractEnded
if a profile's contract_end_date is in the past, surfaced as a CONFLICT
GraphQL error in all three resolvers.
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
Move the paginated people query from PeopleGraph.ts into
SignatureDocumentsDialog.tsx following colocated query conventions
and pass excludeContractEnded: true to hide people whose contract has ended.
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
Align MCP and n8n document listing endpoints with the GraphQL console
API so every document filter is available across all three interfaces.
MCP:
- listDocuments: add status filter, default to ACTIVE
- listDocumentVersions: add statuses filter
- DocumentOrderField: add UPDATED_AT
n8n:
- document getAll: add query, documentTypes, classifications, status filters (default ACTIVE)
- document getAllVersions: add statuses filter
- document getAllSignatures: add states and activeContract filters
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
- Remove the right-side drawer and display document properties in a
3-column Card below the page header
- Move status badge to the PageHeader (right-aligned, matching
compliance page style)
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
Strip forwarded headers (Forwarded, X-Forwarded-For, X-Real-Ip)
from requests originating from untrusted proxies at the HTTP
server level, reusing the existing proxy-protocol trusted-proxies
config. The clientip package is now a pure extraction helper;
context plumbing and middleware wrappers are removed.
Signed-off-by: Émile Ré <emile@getprobo.com>
Add coredata guide section on using Go enum constants as named
SQL parameters instead of hardcoded string literals. Fix mixed
inline/multiline RenderJSON call in cookie banner handler.
Signed-off-by: Émile Ré <emile@getprobo.com>
Move clientIP extraction into a reusable pkg/server/api/clientip
package with RFC 7239 Forwarded header support. Add
pkg/server/jsonutil with helpers for common HTTP error responses
(RenderForbidden, RenderInternalServerError, RenderNotFound,
RenderBadRequest) and use them in the cookie banner handlers.
Signed-off-by: Émile Ré <emile@getprobo.com>
Parse only the first IP from X-Forwarded-For to prevent
the full chain from bypassing anonymization. Add secondary
sort key for deterministic consent selection. Reject origins
with empty port suffix in the validator.
Signed-off-by: Émile Ré <emile@getprobo.com>
Implement config, consent retrieval, and consent recording
endpoints for the JS SDK. IP addresses are anonymized (last
octet zeroed for IPv4, /48 mask for IPv6) before storage.
Signed-off-by: Émile Ré <emile@getprobo.com>
Introduce /cookie-banner/v1/{bannerID}/config endpoint for the JS SDK.
The custom CORS middleware validates each request origin against the
specific banner being requested, preventing cross-customer leakage.
Signed-off-by: Émile Ré <emile@getprobo.com>
Strip www. prefix and trailing slash from origin when creating or
updating a cookie banner so CORS lookups match regardless of whether
the customer's site redirects www to the apex domain.
Signed-off-by: Émile Ré <emile@getprobo.com>
Move OIDCProviderInfo type into base.graphql alongside its query field
in both connect and trust APIs, removing orphan oidc.graphql files.
Split connect profile.graphql into three domain files: profile (with
user mutations), membership, and invitation.
Signed-off-by: Émile Ré <emile@getprobo.com>
The make relay target merges split graphql files into a single
schema.graphql before running the Relay compiler. All CI workflows,
the generate target, and documentation now use make relay.
Signed-off-by: Émile Ré <emile@getprobo.com>
Relay v20 treats extend type in server schema as client extensions,
causing all mutation fields to have "text": null in generated artifacts.
Replace the cat-based schema concatenation with a merge script that
collects all extend type Mutation blocks and produces a single
type Mutation definition. The split graphql/ files remain the source
of truth for gqlgen; the merged schema.graphql is generated for Relay.
Signed-off-by: Émile Ré <emile@getprobo.com>
Update GNUmakefile targets to use wildcard graphql/*.graphql
prerequisites instead of the deleted schema.graphql files, and
remove v1_resolver.go from build prerequisites.
Also update .gitattributes, package.json graphql.schema path,
and AGENTS.md.
Signed-off-by: Émile Ré <emile@getprobo.com>
Change gqlgen filename_template from {name}.resolvers.go to
{name}_resolvers.go across all three APIs for consistent Go naming.
Signed-off-by: Émile Ré <emile@getprobo.com>
Move Identity, Organization, Viewer, PageInfo, OrderDirection,
CountryCode, OIDCProviderInfo, File, and ReauthenticationReason out
of base.graphql into their own dedicated files across all three APIs.
base.graphql now only contains directives, scalars, Node interface,
Query type, and an empty Mutation type (required by Relay
schemaExtensions). Entity files use extend type Mutation for their
mutations.
Signed-off-by: Émile Ré <emile@getprobo.com>
Move Organization, Identity, TrustCenter, and Viewer definitions to
include all their connection fields directly, removing all extend type
blocks for these hub types from entity files.
This eliminates the Relay schemaExtensions constraint where extend type
could only target types defined in the main schema file. Entity files
now only define their own standalone types and extend type Mutation.
Signed-off-by: Émile Ré <emile@getprobo.com>
Add inherentRiskScore and residualRiskScore to the mutation fragment so
the Relay store gets updated scores. On the backend, use RETURNING to
get the recomputed values from the database generated columns.
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
Use the shared getSeverity helper from @probo/helpers in SeverityBadge
so the list view displays the same labels (Low/High/Critical) and
thresholds (0/5/15) as the detail view. Also fix the RisksChart legend
and getLevel thresholds to match.
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
The dev config uses http://localhost:8080 (plain HTTP), but both session
cookies had secure: true, which tells browsers to only send cookies over
HTTPS. This caused a sign-in loop where authentication succeeded but the
browser dropped the cookie, redirecting back to login. This change sets
secure: false for both cookie configurations in the dev config only.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Emily Moir <emi@emimoir.com>
The controls tab used useRefetchableFragment which only showed the
first 20 controls with no "Show more" button, while the tab badge
displayed the actual total count. Switch to usePaginationFragment
to enable proper pagination via the SortableTable component.
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
Replace the three separate draft mutations (createDraftDocumentVersion,
updateDocumentVersion, deleteDraftDocumentVersion) with automatic draft
lifecycle management inside updateDocument. The backend now auto-creates
a draft when a published document is edited, updates the existing draft
on subsequent edits, and auto-deletes the draft when content reverts to
match the published version.
A new deleteDocumentDraft mutation provides explicit draft deletion.
Backend:
- Merge version-level fields (content, title, classification,
documentType) into UpdateDocumentRequest
- Convert CreateDraft, UpdateVersion, DeleteDraft into private
transaction helpers called from Update
- Update returns (*Document, *DocumentVersion, error) with the version
present only when a draft exists
Frontend:
- Remove all create/update/delete draft mutations from components
- Auto-save via updateDocument with layout refetch on draft status
transitions while preserving editor cursor (data-generation key)
- Title, type, and classification editable on published versions
(backend auto-creates draft)
- Forms use react-hook-form values option to stay synced with Relay
fragment data across draft/publish transitions
API surface (GraphQL, MCP, CLI, n8n) updated consistently:
- Removed: createDraftDocumentVersion, updateDocumentVersion,
deleteDraftDocumentVersion
- Added: deleteDocumentDraft (document-level)
- Updated: updateDocument accepts content, classification, documentType
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
Consolidate the two per-app relay configs (console and trust)
into a single multi-project relay.config.json at the repo root
with three projects: core, iam, and trust. Bump all relay
packages from v19 to v20.1.1 and move relay-compiler to the
root devDependencies. Replace per-workspace relay scripts with
a single root-level npm run relay command and update the
GNUmakefile, CI workflows, and docs accordingly.
Signed-off-by: Émile Ré <emile@getprobo.com>
Every other LoadAll* method in coredata takes a Scoper parameter
for tenant isolation. LoadAllByCookieBannerID was the only one
that omitted it, making the isolation invariant depend entirely
on callers first loading the banner with a scoped query.
Add scope.SQLFragment to the WHERE clause to match the pattern
used by the paginated sibling LoadByCookieBannerID.
Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
The connector initiate and complete HTTP handlers used panic for
operational errors (network, DB, provider failures). No recovery
middleware exists on the console chi router, so these panics
produced incomplete responses instead of proper HTTP 500 errors.
Use the same log-and-render pattern already established in
loadExistingConnector error handling.
Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
- Return 400 instead of panicking on invalid organization_id
- Use generic error message for internal failures
- Drop duplicate validation from initiate handler (kept in tx)
- Make preserveConnectionFields mutate in place
- Remove as type assertions in GoogleWorkspaceConnector
- Use sort.Slice instead of sort.SliceStable
- Consistent error prefixes in Slack sender
Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
ApplyProviderDefaults now sets redirect-uri, auth-url,
token-url, extra-auth-params, token-endpoint-auth, and
scopes at startup. Only client-id, client-secret, and
provider-specific settings are needed in the YAML.
Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
Check the ok and error fields from Slack's token
response and reject flows with a missing access token.
Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
Switch to useMutation + useToast with onCompleted and
onError callbacks, matching the pattern used across
the rest of the console app.
Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
The 500 response was wrapping the underlying error with
%w, exposing internal details to the client. Log the
full error, return a generic message.
Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>