Drop meetings and meeting_attendees tables, remove all meeting-related
code across GraphQL, MCP, CLI, N8N, webhooks, frontend, and e2e tests.
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
Delete duplicate connectors per (organization_id, provider), keeping the
oldest one, then add a unique index to prevent future duplicates.
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
Mirror the SOA-to-document migration for the data list. Remove data
from the snapshot system and add a publish workflow that generates a
ProseMirror document for the full organization data inventory.
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
Expose document version approval quorums and decisions
through MCP, CLI, and n8n. This lets users inspect who
approved or rejected a document version, including the
rejection comment, without relying solely on the audit
log.
MCP tools: listDocumentVersionApprovalQuorums,
getDocumentVersionApprovalQuorum,
listDocumentVersionApprovalDecisions,
getDocumentVersionApprovalDecision.
CLI commands: document list-approval-quorums,
view-approval-quorum, list-approval-decisions,
view-approval-decision.
n8n operations: Get/Get Many Approval Quorums and
Approval Decisions on the Document resource.
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
Expose --probo-font-size and --probo-btn-radius so customers
can scale typography and button shape without overriding
internals. The type scale now uses calc() offsets from the
base variable. The settings button defaults now follow
--probo-accent so a single accent change updates both the
banner and the floating button.
Signed-off-by: Émile Ré <emile@getprobo.com>
Headless components are behavioral wrappers meant to be styled
by the consumer with regular CSS. Shadow DOM on each child
created unnecessary style boundaries with no visual content to
protect. Only ProboSettingsButton retains its own shadow root
since it renders encapsulated styled markup.
Also removes the auto-registerComponents() side effect from
the main entrypoint so consumers opt in explicitly.
Signed-off-by: Émile Ré <emile@getprobo.com>
Store baseUrl as URL and build endpoints with new URL(path, base).
Accept URL | string in fetchJSON to avoid unnecessary .href calls.
Signed-off-by: Émile Ré <emile@getprobo.com>
Validate that <probo-preference-panel> contains <probo-category-list>
and <probo-save-button>, and that the category template includes
<probo-category-toggle> and <probo-cookie-list>.
Signed-off-by: Émile Ré <emile@getprobo.com>
Introduce Shadow DOM-based custom elements that let customers build
their own cookie banner and preference panel while the SDK validates
structural compliance and auto-renders categories/cookies from config.
Components: probo-cookie-banner (root), probo-banner, probo-accept-button,
probo-reject-button, probo-customize-button, probo-preference-panel,
probo-category-list, probo-category, probo-category-toggle,
probo-cookie-list, probo-cookie, probo-save-button, probo-settings-button.
Signed-off-by: Émile Ré <emile@getprobo.com>
Implement a full OAuth2 2.0 and OpenID Connect 1.0 authorization
server with support for authorization code flow (with PKCE),
refresh token rotation, device authorization grant, dynamic
client registration, token introspection, and token revocation.
Includes database schema, coredata layer, service logic, HTTP
handlers, OIDC discovery endpoint, and JWKS publishing.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Expose webhook subscription CRUD and event listing through the MCP API
(list, get, create, update, delete subscriptions + list events) and add
a new webhook resource to the N8N node with matching operations.
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
The custom updater on the SCIM disconnect mutation ran on every
response and nulled organization.scimConfiguration in the Relay store
-- including on error responses, which flipped the UI to the
"disconnected" state even when the backend had actually rejected the
delete.
Replace the updater with a @deleteRecord directive on the returned
deletedScimConfigurationId, matching the pattern already used in
SCIMConfiguration.tsx. @deleteRecord is a no-op when the returned id
is null, so error responses no longer produce a false-positive UI
update.
Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
DeleteSCIMConfiguration unconditionally deleted the underlying OAuth2
connector together with the SCIM bridge and config. When the same
connector was also referenced from access_sources -- which happens
when Google Workspace is used for both SCIM and access reviews -- the
access_sources.connector_id foreign key (NO ACTION) rejected the
DELETE, aborting the whole transaction. Nothing was deleted and the
resolver returned an INTERNAL error.
Check the access_sources reference count before deleting the connector
and skip the connector delete when it is still in use. The bridge's
own connector_id FK is ON DELETE SET NULL, so dropping the bridge
alone is sufficient to unbind SCIM; leaving the connector untouched
keeps the access source working.
Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
Helper for callers that need to know whether a given connector row is
still referenced by any access_sources. Used by the SCIM disconnect
flow, which would otherwise fail with a foreign-key violation when the
connector is shared with an access source.
Mirrors the shape of CountByOrganizationID.
Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
UpdateSCIMBridge returned bare fmt.Errorf("SCIM bridge not found")
strings on the two not-found branches (resource missing, and
cross-tenant mismatch). Every other call site returning the same
condition uses the typed NewSCIMBridgeNotFoundError(bridgeID) (see
lines 1962 and 2153 in this file).
Switch both branches to the typed error so the error shape is
consistent across the service and callers can use errors.As to
detect the condition.
Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
Use typed ErrDocumentVersionNotPublished instead of plain fmt.Errorf in
signature request methods, and add missing ErrResourceNotFound handling
across document resolvers that were returning Internal for expected errors.
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
Authenticate to Docker Hub in jobs that pull images (build-snapshot-docker,
test-e2e, build-docker) to avoid unauthenticated rate limits.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Avoid unnecessary Docker registry checks on stack-up by setting
pull_policy: missing on every service. Also pin the tempo image to its
sha256 digest for reproducibility, consistent with all other services.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
The absolute-positioned collapse button overlapped the nav items
whenever the sidebar content exceeded the viewport. With the page now
scrolling at the document level, the overlap became permanent.
Restructure the aside as a flex column: nav list in a flex-1 region,
collapse button in a sticky bottom-0 container with a border-t
separator. The button pins to the viewport bottom while scrolling and
never overlaps the items above.
Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
The previous layout nested two scroll regions, sidebar and main, each
with its own scrollbar. Combined with the fixed 100vh flex container,
any child overflow (notably the sidebar nav) spilled past bg-level-0
and exposed the browser's default white background -- most visible in
dark mode as a white strip at the bottom of the viewport.
Switch to a single page-level scroll: pin the header with fixed, let
the wrapper grow with min-h-screen, and drop main's internal overflow.
The drawer becomes fixed too so it stays pinned while the page scrolls
and gets an opaque background to cover scrolled content behind it.
Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
The flush function pruned expired entries in memory but re-read from
localStorage before writing back, so the age filter was never persisted.
Apply the age cutoff to the final write filter and remove the early
return that skipped the write entirely on full send failure.
Signed-off-by: Émile Ré <emile@getprobo.com>
Re-read localStorage after network calls in flush() to avoid
discarding entries added by concurrent enqueue() calls. Also
call flush() in the cookie fast-path of load() so queued
consent retries are always drained.
Signed-off-by: Émile Ré <emile@getprobo.com>
- Rename MAX_RETRIES to MAX_ATTEMPTS (3 total) to fix
misleading naming
- Skip retry loop on caller-initiated aborts so explicit
cancellations terminate immediately
- Preserve original script type via data-type attribute
instead of always forcing text/javascript
- Recreate MutationObserver when consent changes so newly
added elements use fresh consent data
- Fix package.json exports: point main at ESM bundle and
add proper exports map with IIFE as separate entry
Signed-off-by: Émile Ré <emile@getprobo.com>
Reduce timeout/retry defaults (5s timeout, 2 retries, 500ms base delay)
to cap worst-case page-load blocking at ~12s instead of ~36s.
Add a localStorage-backed queue that persists failed consent POSTs and
replays them on next page load, closing the compliance gap where a
network failure could permanently lose the server-side audit record.
Signed-off-by: Émile Ré <emile@getprobo.com>
Unblock scripts, iframes, images, and other resource-loading
elements marked with data-cookie-consent when their category
is consented. Uses MutationObserver for dynamically added
elements. Restricted to known resource tags with data-href
support for link elements.
Signed-off-by: Émile Ré <emile@getprobo.com>