Skip to main content
Jahia Store
EN

customGPT.ai - Jahia indexer

community
Download 1.0.7

Information

Module ID
customgpt-ai
Group ID
org.jahia.modules.community
Status
community
Category
Connectors
Author
fbourasse
Developer website
http://www.jahia.com
Requires Jahia
8.2.3.0
Released
2026-09-30
Source
scm:git:git@github.com:Jahia/customgpt-ai.git
Tags
  • ai
  • chat
  • chatbot

Jahia module that integrates with the CustomGPT.ai API to index Jahia site content (pages and files) into a CustomGPT project, keeping it in sync with JCR publish/unpublish events.

Dependencies

Depended on by

Nothing depends on this module.

Versions

Fixed - a stale JCR listener could abort a publication

A publication failed with NoClassDefFoundError on IndexOperations$CustomGptOperationType, thrown from a listener that had outlived the bundle classloader which created it. A wiring refresh did not clear it; only restarting the processing node did.

JCRObservationManager keeps listeners in a JVM-wide static list and removes them by instance identity, while TemplatePackageRegistry de-duplicates by Class identity - so a listener that stop() failed to detach is unreachable forever, and a redeploy registers a second listener beside the stale one rather than replacing it. Jahia contains Exception around listeners but not Error, which is why a NoClassDefFoundError killed the publication job while ordinary exceptions never did.

  • stop() now detaches the listener unconditionally and first, with each step isolated so one failure cannot skip another.
  • init() registers the listener last, after everything that can throw. A component whose activate() throws is never deactivated, so registering early stranded the listener on a path no shutdown hardening could reach.
  • onEvent() contains LinkageError, so a stale listener can no longer abort someone else's publication.
  • A deactivated component refuses to allocate new thread pools.

Upgrade note: installing this does not repair a node that is already in the bad state. The stranded listener belongs to the previous bundle generation and has no such handling. Restart affected processing nodes once after upgrading.

Fixed - silent failures that made the module look healthy while doing nothing

  • A listener with an empty node-type filter was registered and permanently deaf. Jahia rejects every event against an empty filter, so the module reported started and indexed nothing for the life of the component. Registration is now refused and retried when configuration arrives - and a change to the indexed node types now takes effect instead of being frozen at first registration.
  • A failed site indexation reported COMPLETED. Per-node failures were swallowed, so a run in which nothing indexed was indistinguishable from success. Failures are now recorded with node path and language, and a run that had any is reported FAILED.
  • Dry run skipped indexation silently. dryRun defaults to true in the shipped configuration, and that file is not overwritten on redeploy - so this was the likeliest cause of "the module is running but nothing is indexed", with no evidence in the logs. It now says so.

Fixed - re-indexing could leave the previous page behind

A corpus audit found 223 of 3055 pages (7.3%) redundant, concentrated in the security-advisory tree, with one URL holding 17 copies. Superseded copies of advisories stayed retrievable, so the chatbot could answer from a withdrawn one.

removeExistingPage() discarded the result of the page delete and added a replacement regardless, so every re-publish whose DELETE failed orphaned the previous page - with no log line at any level. The delete result is now honoured: a page confirmed gone proceeds, and any status that may have left it in place aborts rather than creating a duplicate.

Also coalesces repeat index operations. One publication performs several JCR saves, each delivering its own event batch, so the same node was dispatched up to 10 times per publication - each repeat costing a delete and a post against an API that degrades under sustained volume. Repeats are now dropped while equivalent work is queued.

Behaviour change: a second change to a node published while its first index is still pending is dropped rather than queued, so that content is indexed on the next publication.

Also since 1.0.5

  • customGptAdmin permission for fine-grained RBAC, with regression tests
  • Stored secrets no longer exposed; credential transport hardened; https enforced on the token-bearing base URL
  • Duplicate EventHandler registration removed; asynchronous errors surfaced; HTTP clients bounded
  • 86 SonarQube issues resolved; WCAG accessibility fixes across the admin UI
  • Vulnerable transitive test dependencies pinned

Verification

157 unit tests, and the end-to-end Cypress suite green at 33/33 against Jahia 8.2.3.2. The suite had been failing 20 of 33 before this release - for a harness reason rather than a module one - and that is fixed here too: it now resolves module dependencies from a releases-only repository and runs against a released Jahia.

Requires Jahia 8.2.3.0Released 2026-09-30

Highlights

  • Resolved all 86 pre-existing SonarQube issues across the codebase, significantly improving code quality.
  • Fixed WCAG 2.1 AA accessibility issues throughout the admin UI and CustomGptSettings component.
  • Hardened the rate-limit interceptor by moving Thread.sleep outside of the synchronized block to reduce contention.

Changes

Features

  • None in this range.

Fixes

  • ecd752e fix: resolve all 86 pre-existing SonarQube issues.
  • d711461 fix(rate-limit): move Thread.sleep outside synchronized block and reduce lock-hold time.
  • 1edf8a0 fix(ui): stick scrollbar to right edge by removing max-width from scroll container.
  • c45528e fix(a11y): resolve WCAG 2.1 AA issues in CustomGptSettings.
  • f34db7e fix: resolve WCAG 2.1 AA accessibility issues in admin UI.

Refactoring / chores

  • None in this range. (Touched files include broad refactors across Service.java, IndexerJCRListener.java, CustomGptIndexerNodeHandler.java, etc., bundled inside the SonarQube fix commit.)

Tests / CI

  • 86876a6 test(cypress): assert scheduleJobASAP resets to false after save.
  • d962126 test(cypress): increase defaultCommandTimeout to 10000ms.

Full Changelog: 1_0_4...1_0_5

Requires Jahia 8.2.3.0Released 2026-05-19

New features

  • Token-bucket rate limiting — the RateLimitInterceptor now enforces a configurable maximum request rate (default 10 req/s) using a token-bucket algorithm. Tokens are refilled lazily from elapsed wall-clock time; no background thread is needed. All API calls — indexing and purge alike — share the same bucket.
  • Configurable rate via Admin UI and OSGi config — the new rateLimit.requestsPerSecond property (org.jahia.community.modules.customgpt.rateLimit.requestsPerSecond) is exposed in the settings panel next to Batch Size. It can also be set directly in the .cfg file. Note: the OkHttp client reads the value at startup; a module restart is required for changes to take effect.

Improvements

  • Exponential back-off on HTTP 429 — replaced the previous single fixed-delay retry (1 s, one attempt) with up to 3 retries using full-jitter exponential back-off (base 1 s, cap 30 s). The Retry-After response header is honoured when present, overriding the computed delay.
  • Streaming-batch purge — purgeAllPages no longer loads every page ID into memory before starting deletions. It now fetches one API result page, deletes those IDs concurrently, then re-fetches until the first page comes back empty. Memory usage is bounded to one result page (typically 15–20 IDs) regardless of project size.
  • Capped purge thread pool — the deleteAllPages executor is sized to min(batchSize, requestsPerSecond) instead of batchSize, preventing the creation of hundreds of idle threads when the batch size is large.

Bug fixes

  • Fixed pagination drift during purge: the previous implementation followed next_page_url while deleting, which silently skipped items when offset-based pagination shifted after deletions. The new loop always re-queries the first page URL after each deletion round, guaranteeing no items are missed.

Upgrade notes

  • The new rateLimit.requestsPerSecond property defaults to 10. Existing deployments that relied on the previous fixed 500–1000 ms jitter between requests will now send up to 10 requests per second; adjust the property if the CustomGPT.ai plan has stricter limits.
  • No JCR schema changes. No migration script required.

Full Changelog: 1_0_3...1_0_4

Requires Jahia 8.2.3.0Released 2026-05-13

New features

  • Purge All Pages — new danger-zone button in the admin UI that deletes every page registered in the CustomGPT project. The operation runs concurrently in configurable batches (operations.batch.size) and returns the exact count of pages deleted. A confirmation dialog prevents accidental use.
  • Project name display — the admin UI now shows the CustomGPT project name (resolved live from the API) next to the Project ID field, making it easy to verify the correct project is configured.
  • Configurable API base URL — the CustomGPT API base URL is now a first-class configuration property (org.jahia.community.modules.customgpt.apiBaseUrl, default https://app.customgpt.ai/api/v1/). This replaces the previously hardcoded value and allows connecting to custom or on-premise CustomGPT deployments.
  • React admin UI — a full settings panel is now available under Jahia Administration → CustomGPT.ai (/jahia/administration/customgptAiSettings). All configuration properties can be viewed and saved directly from the UI without editing .cfg files.
  • End-to-end test suite — Cypress tests covering the full indexing lifecycle (add site, index, update, delete page) with Docker Compose setup and CI scripts.

Improvements

  • Concurrent batch deletion — purgeAllPages now fetches all page IDs first (following next_page_url pagination) and then deletes them in parallel batches using a dedicated ExecutorService, significantly reducing purge time on large projects.
  • Detailed purge logging — each batch deletion logs the page ID, HTTP status, and running count so progress is visible in Jahia logs.
  • scheduleJobASAP auto-reset — after indexing jobs are scheduled with scheduleJobASAP=true, the flag is automatically written back to false via ConfigurationAdmin. This prevents repeated re-indexing on every module restart.
  • EVENT_TYPE_CONFIG_UPDATED_REQUIRE_REINDEX wired — changing indexable node types in the config now correctly triggers a full re-index of affected sites.
  • i18n — purge/danger-zone UI labels added for English, French, and German.
  • Vertical scrolling — the settings panel now correctly scrolls when the content overflows the available height.
  • Build automation — frontend-maven-plugin compiles the React UI automatically during mvn install (no separate yarn build step required).

Refactoring

  • JCR mapping node redesign — customGptPageId is now stored in a customgptIndex child node of type jnt:customGptIndexEntry directly under each indexed node. This replaces the previous approach of storing mapping nodes in a separate repository subtree (/sites/…/customgpt-index/…), which caused conflicts with Jahia's publication workflow.
  • Credential scoping — Basic Auth credentials for Jahia page rendering are now added per-request instead of via a global OkHttp3 interceptor, preventing accidental credential leakage on redirects or shared client instances.
  • Removed static executors — Service.java no longer uses static ExecutorService fields; executors are instance-managed and properly shut down on OSGi component deactivation.
  • GWT Guava removed — com.google.gwt.thirdparty.guava imports replaced with standard java.util.Objects and Java string utilities throughout.

Bug fixes

  • Fixed GraphQL admin query self-reference (admin.customGpt resolving to itself in introspection).
  • Fixed jmix:customGptIndexable mixin not being present on site nodes, causing customgptIndex child node creation to fail.
  • Fixed customgpt-index mapping node name collision with the jnt:customGptIndexContainer node type; renamed to customgptIndex.
  • Fixed unhandled NullPointerException on malformed CustomGPT API responses by adding safe JSON validation in extractPageId().
  • Fixed InterruptedException catches that swallowed the interrupt signal without calling Thread.currentThread().interrupt(), causing thread pool misbehaviour under shutdown.

Code quality

  • SonarQube quality gate passing on new code: 0 open issues, A ratings on maintainability, reliability, and security.
  • All public Java classes and methods documented with Javadoc.
  • GqlSettings DTO refactored to Builder pattern (eliminates 16-parameter constructor).
  • purgeAllPages decomposed into focused private helpers to meet cognitive-complexity limits.

Migration

If upgrading from a version that used the jmix:customGptIndexed or jmix:customGptFileIndexed mixins, run the provided Groovy cleanup script from the Jahia Groovy console:

scripts/cleanup-legacy-customgpt-mixins.groovy

This removes the legacy mixins and the customGptPageId property from all nodes in both EDIT and LIVE workspaces. A full re-index is required after migration.


Full Changelog: 1_0_2...1_0_3

Requires Jahia 8.2.3.0Released 2026-05-13
Requires Jahia 8.2.1.0Released 2025-06-12